• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.aead;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static java.nio.charset.StandardCharsets.UTF_8;
21 import static org.junit.Assert.assertThrows;
22 
23 import com.google.crypto.tink.Aead;
24 import com.google.crypto.tink.InsecureSecretKeyAccess;
25 import com.google.crypto.tink.KeysetHandle;
26 import com.google.crypto.tink.RegistryConfiguration;
27 import com.google.crypto.tink.TinkProtoKeysetFormat;
28 import com.google.crypto.tink.aead.AesCtrHmacAeadParameters.HashType;
29 import com.google.crypto.tink.aead.AesCtrHmacAeadParameters.Variant;
30 import com.google.crypto.tink.internal.MonitoringAnnotations;
31 import com.google.crypto.tink.internal.MutableMonitoringRegistry;
32 import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
33 import com.google.crypto.tink.internal.PrimitiveConstructor;
34 import com.google.crypto.tink.internal.PrimitiveRegistry;
35 import com.google.crypto.tink.internal.testing.FakeMonitoringClient;
36 import com.google.crypto.tink.proto.AesCtrHmacAeadKey;
37 import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
38 import com.google.crypto.tink.proto.KeyStatusType;
39 import com.google.crypto.tink.proto.Keyset;
40 import com.google.crypto.tink.proto.Keyset.Key;
41 import com.google.crypto.tink.proto.OutputPrefixType;
42 import com.google.crypto.tink.subtle.Bytes;
43 import com.google.crypto.tink.subtle.Hex;
44 import com.google.crypto.tink.subtle.Random;
45 import com.google.crypto.tink.testing.TestUtil;
46 import com.google.crypto.tink.util.SecretBytes;
47 import java.security.GeneralSecurityException;
48 import java.util.Arrays;
49 import java.util.List;
50 import org.junit.BeforeClass;
51 import org.junit.Test;
52 import org.junit.experimental.theories.DataPoints;
53 import org.junit.experimental.theories.FromDataPoints;
54 import org.junit.experimental.theories.Theories;
55 import org.junit.experimental.theories.Theory;
56 import org.junit.runner.RunWith;
57 
58 /** Unit tests for {@link AeadWrapper}. */
59 @RunWith(Theories.class)
60 public class AeadWrapperTest {
61 
62   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkKey;
63   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkFixedKey;
64   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkFixedKeyDifferentId;
65   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey crunchyFixedKey;
66   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawKey0;
67   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawKey1;
68   private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawFixedKey;
69 
70   @DataPoints("keys")
71   public static com.google.crypto.tink.Key[] keys;
72 
73   @BeforeClass
setUpClass()74   public static void setUpClass() throws Exception {
75     AesCtrHmacAeadKeyManager.register(true);
76 
77     AesCtrHmacAeadParameters tinkParameters =
78         AesCtrHmacAeadParameters.builder()
79             .setAesKeySizeBytes(32)
80             .setHmacKeySizeBytes(32)
81             .setHashType(HashType.SHA512)
82             .setIvSizeBytes(16)
83             .setTagSizeBytes(16)
84             .setVariant(Variant.TINK)
85             .build();
86     tinkKey =
87         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
88             .setParameters(tinkParameters)
89             .setAesKeyBytes(SecretBytes.randomBytes(32))
90             .setHmacKeyBytes(SecretBytes.randomBytes(32))
91             .setIdRequirement(42)
92             .build();
93     tinkFixedKey =
94         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
95             .setParameters(tinkParameters)
96             .setAesKeyBytes(
97                 SecretBytes.copyFrom(
98                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
99                     InsecureSecretKeyAccess.get()))
100             .setHmacKeyBytes(
101                 SecretBytes.copyFrom(
102                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
103                     InsecureSecretKeyAccess.get()))
104             .setIdRequirement(42)
105             .build();
106     tinkFixedKeyDifferentId =
107         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
108             .setParameters(tinkParameters)
109             .setAesKeyBytes(
110                 SecretBytes.copyFrom(
111                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
112                     InsecureSecretKeyAccess.get()))
113             .setHmacKeyBytes(
114                 SecretBytes.copyFrom(
115                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
116                     InsecureSecretKeyAccess.get()))
117             .setIdRequirement(43)
118             .build();
119     AesCtrHmacAeadParameters crunchyParameters =
120         AesCtrHmacAeadParameters.builder()
121             .setAesKeySizeBytes(32)
122             .setHmacKeySizeBytes(32)
123             .setHashType(HashType.SHA512)
124             .setIvSizeBytes(16)
125             .setTagSizeBytes(16)
126             .setVariant(Variant.CRUNCHY)
127             .build();
128     crunchyFixedKey =
129         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
130             .setParameters(crunchyParameters)
131             .setAesKeyBytes(
132                 SecretBytes.copyFrom(
133                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
134                     InsecureSecretKeyAccess.get()))
135             .setHmacKeyBytes(
136                 SecretBytes.copyFrom(
137                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
138                     InsecureSecretKeyAccess.get()))
139             .setIdRequirement(42)
140             .build();
141     AesCtrHmacAeadParameters rawParameters =
142         AesCtrHmacAeadParameters.builder()
143             .setAesKeySizeBytes(32)
144             .setHmacKeySizeBytes(32)
145             .setHashType(HashType.SHA512)
146             .setIvSizeBytes(16)
147             .setTagSizeBytes(16)
148             .setVariant(Variant.NO_PREFIX)
149             .build();
150     rawKey0 =
151         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
152             .setParameters(rawParameters)
153             .setAesKeyBytes(SecretBytes.randomBytes(32))
154             .setHmacKeyBytes(SecretBytes.randomBytes(32))
155             .build();
156     rawKey1 =
157         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
158             .setParameters(rawParameters)
159             .setAesKeyBytes(SecretBytes.randomBytes(32))
160             .setHmacKeyBytes(SecretBytes.randomBytes(32))
161             .build();
162     rawFixedKey =
163         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
164             .setParameters(rawParameters)
165             .setAesKeyBytes(
166                 SecretBytes.copyFrom(
167                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
168                     InsecureSecretKeyAccess.get()))
169             .setHmacKeyBytes(
170                 SecretBytes.copyFrom(
171                     Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"),
172                     InsecureSecretKeyAccess.get()))
173             .build();
174 
175     keys = new com.google.crypto.tink.Key[] {tinkKey, crunchyFixedKey, rawKey0};
176   }
177 
178   @Test
wrappedNonRawEncrypt_addsPrefixToRawCiphertext()179   public void wrappedNonRawEncrypt_addsPrefixToRawCiphertext() throws Exception {
180     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
181     AeadConfig.register();
182 
183     byte[] plaintext = "plaintext".getBytes(UTF_8);
184     byte[] associatedData = "associatedData".getBytes(UTF_8);
185     byte[] outputPrefix = Hex.decode("000000002a");
186 
187     KeysetHandle rawKeysetHandle =
188         KeysetHandle.newBuilder()
189             .addEntry(KeysetHandle.importKey(rawFixedKey).withFixedId(0x0000002a).makePrimary())
190             .build();
191     Aead rawAead = rawKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
192     KeysetHandle tinkKeysetHandle =
193         KeysetHandle.newBuilder()
194             .addEntry(KeysetHandle.importKey(crunchyFixedKey).makePrimary())
195             .build();
196     Aead crunchyAead = tinkKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
197 
198     byte[] ciphertext = crunchyAead.encrypt(plaintext, associatedData);
199     byte[] ciphertextPrefix = Arrays.copyOf(ciphertext, 5);
200     byte[] ciphertextWithoutPrefix = Arrays.copyOfRange(ciphertext, 5, ciphertext.length);
201 
202     assertThat(ciphertextPrefix).isEqualTo(outputPrefix);
203     assertThat(rawAead.decrypt(ciphertextWithoutPrefix, associatedData)).isEqualTo(plaintext);
204   }
205 
206   @Test
wrappedNonRawDecrypt_decryptsRawCiphertextWithPrefix()207   public void wrappedNonRawDecrypt_decryptsRawCiphertextWithPrefix() throws Exception {
208     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
209     AeadConfig.register();
210 
211     byte[] plaintext = "plaintext".getBytes(UTF_8);
212     byte[] associatedData = "associatedData".getBytes(UTF_8);
213     byte[] outputPrefix = Hex.decode("010000002a");
214     byte[] invalid = "invalid".getBytes(UTF_8);
215 
216     KeysetHandle rawKeysetHandle =
217         KeysetHandle.newBuilder()
218             .addEntry(KeysetHandle.importKey(rawFixedKey).withFixedId(0x0000002a).makePrimary())
219             .build();
220     Aead rawAead = rawKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
221     KeysetHandle tinkKeysetHandle =
222         KeysetHandle.newBuilder()
223             .addEntry(KeysetHandle.importKey(tinkFixedKey).makePrimary())
224             .build();
225     Aead tinkAead = tinkKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
226 
227     byte[] rawCiphertext = rawAead.encrypt(plaintext, associatedData);
228     byte[] rawCiphertextWithTinkPrefix = Bytes.concat(outputPrefix, rawCiphertext);
229 
230     assertThat(tinkAead.decrypt(rawCiphertextWithTinkPrefix, associatedData)).isEqualTo(plaintext);
231     assertThrows(
232         GeneralSecurityException.class, () -> tinkAead.decrypt(rawCiphertext, associatedData));
233     assertThrows(
234         GeneralSecurityException.class,
235         () -> tinkAead.decrypt(rawCiphertextWithTinkPrefix, invalid));
236     assertThrows(GeneralSecurityException.class, () -> tinkAead.decrypt(invalid, associatedData));
237     assertThrows(
238         GeneralSecurityException.class, () -> tinkAead.decrypt("".getBytes(UTF_8), associatedData));
239   }
240 
241   @Theory
encryptAndDecrypt_success(@romDataPoints"keys") com.google.crypto.tink.Key key)242   public void encryptAndDecrypt_success(@FromDataPoints("keys") com.google.crypto.tink.Key key)
243       throws Exception {
244     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
245     AeadConfig.register();
246 
247     byte[] plaintext = "plaintext".getBytes(UTF_8);
248     byte[] associatedData = "associatedData".getBytes(UTF_8);
249 
250     KeysetHandle keysetHandle =
251         KeysetHandle.newBuilder()
252             .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary())
253             .build();
254     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
255     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
256 
257     assertThat(aead.decrypt(ciphertext, associatedData)).isEqualTo(plaintext);
258   }
259 
260   @Theory
encryptAndDecrypt_incorrectInputsFail( @romDataPoints"keys") com.google.crypto.tink.Key key)261   public void encryptAndDecrypt_incorrectInputsFail(
262       @FromDataPoints("keys") com.google.crypto.tink.Key key) throws Exception {
263     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
264     AeadConfig.register();
265 
266     byte[] plaintext = "plaintext".getBytes(UTF_8);
267     byte[] associatedData = "associatedData".getBytes(UTF_8);
268     byte[] invalid = "invalid".getBytes(UTF_8);
269 
270     KeysetHandle keysetHandle =
271         KeysetHandle.newBuilder()
272             .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary())
273             .build();
274     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
275     KeysetHandle incorrectKeyKeysetHandle =
276         KeysetHandle.newBuilder()
277             .addEntry(KeysetHandle.importKey(rawKey1).withFixedId(42).makePrimary())
278             .build();
279     Aead incorrectKeyAead =
280         incorrectKeyKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
281     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
282 
283     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext, invalid));
284     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(invalid, associatedData));
285     assertThrows(
286         GeneralSecurityException.class, () -> aead.decrypt("".getBytes(UTF_8), associatedData));
287     assertThrows(
288         GeneralSecurityException.class, () -> incorrectKeyAead.decrypt(ciphertext, associatedData));
289   }
290 
291   @Test
decryptWorksIfCiphertextIsValidForAnyPrimitiveInThePrimitiveSet()292   public void decryptWorksIfCiphertextIsValidForAnyPrimitiveInThePrimitiveSet() throws Exception {
293     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
294     AeadConfig.register();
295 
296     byte[] plaintext = "plaintext".getBytes(UTF_8);
297     byte[] associatedData = "associatedData".getBytes(UTF_8);
298 
299     KeysetHandle keysetHandle0 =
300         KeysetHandle.newBuilder()
301             .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary())
302             .build();
303     Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class);
304     KeysetHandle keysetHandle1 =
305         KeysetHandle.newBuilder()
306             .addEntry(KeysetHandle.importKey(rawKey1).withRandomId().makePrimary())
307             .build();
308     Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
309     KeysetHandle keysetHandle01 =
310         KeysetHandle.newBuilder()
311             .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary())
312             .addEntry(KeysetHandle.importKey(rawKey1).withRandomId())
313             .build();
314     Aead aead01 = keysetHandle01.getPrimitive(RegistryConfiguration.get(), Aead.class);
315     byte[] ciphertext0 = aead0.encrypt(plaintext, associatedData);
316     byte[] ciphertext1 = aead1.encrypt(plaintext, associatedData);
317 
318     assertThat(aead01.decrypt(ciphertext0, associatedData)).isEqualTo(plaintext);
319     assertThat(aead01.decrypt(ciphertext1, associatedData)).isEqualTo(plaintext);
320   }
321 
322   @Test
encryptUsesPrimaryPrimitive()323   public void encryptUsesPrimaryPrimitive() throws Exception {
324     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
325     AeadConfig.register();
326 
327     byte[] plaintext = "plaintext".getBytes(UTF_8);
328     byte[] associatedData = "associatedData".getBytes(UTF_8);
329 
330     KeysetHandle keysetHandle0 =
331         KeysetHandle.newBuilder()
332             .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary())
333             .build();
334     Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class);
335     KeysetHandle keysetHandle1 =
336         KeysetHandle.newBuilder()
337             .addEntry(KeysetHandle.importKey(rawKey1).withRandomId().makePrimary())
338             .build();
339     Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
340     KeysetHandle keysetHandle01 =
341         KeysetHandle.newBuilder()
342             .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary())
343             .addEntry(KeysetHandle.importKey(rawKey1).withRandomId())
344             .build();
345     Aead aead01 = keysetHandle01.getPrimitive(RegistryConfiguration.get(), Aead.class);
346     byte[] ciphertext = aead01.encrypt(plaintext, associatedData);
347 
348     // rawKey0 is the primary key of aead01. Therefore, aead0 should be able to decrypt, and aead1
349     // not.
350     assertThat(aead0.decrypt(ciphertext, associatedData)).isEqualTo(plaintext);
351     assertThrows(GeneralSecurityException.class, () -> aead1.decrypt(ciphertext, associatedData));
352   }
353 
354   @Theory
decryptFailsIfEncryptedWithOtherKeyEvenIfKeyIdsAreEqual( @romDataPoints"keys") com.google.crypto.tink.Key key)355   public void decryptFailsIfEncryptedWithOtherKeyEvenIfKeyIdsAreEqual(
356       @FromDataPoints("keys") com.google.crypto.tink.Key key) throws Exception {
357     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
358     AeadConfig.register();
359 
360     byte[] plaintext = "plaintext".getBytes(UTF_8);
361     byte[] associatedData = "associatedData".getBytes(UTF_8);
362 
363     KeysetHandle keysetHandle0 =
364         KeysetHandle.newBuilder()
365             .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary())
366             .build();
367     Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class);
368     KeysetHandle keysetHandle1 =
369         KeysetHandle.newBuilder()
370             .addEntry(KeysetHandle.importKey(rawKey1).withFixedId(42).makePrimary())
371             .build();
372     Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
373     byte[] ciphertext = aead0.encrypt(plaintext, associatedData);
374 
375     assertThrows(GeneralSecurityException.class, () -> aead1.decrypt(ciphertext, associatedData));
376   }
377 
378   @Test
nonRawKeysWithSameKeyMaterialButDifferentKeyIds_decryptFails()379   public void nonRawKeysWithSameKeyMaterialButDifferentKeyIds_decryptFails() throws Exception {
380     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
381     AeadConfig.register();
382 
383     byte[] plaintext = "plaintext".getBytes(UTF_8);
384     byte[] associatedData = "associatedData".getBytes(UTF_8);
385 
386     KeysetHandle tinkKeysetHandle0 =
387         KeysetHandle.newBuilder()
388             .addEntry(KeysetHandle.importKey(tinkFixedKey).makePrimary())
389             .build();
390     Aead tinkAead0 = tinkKeysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class);
391     KeysetHandle tinkKeysetHandle1 =
392         KeysetHandle.newBuilder()
393             .addEntry(KeysetHandle.importKey(tinkFixedKeyDifferentId).makePrimary())
394             .build();
395     Aead tinkAead1 = tinkKeysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
396 
397     byte[] ciphertext = tinkAead0.encrypt(plaintext, associatedData);
398 
399     assertThrows(
400         GeneralSecurityException.class, () -> tinkAead1.decrypt(ciphertext, associatedData));
401   }
402 
403   @Test
rawKeysWithSameKeyMaterialButDifferentKeyIds_decryptWorks()404   public void rawKeysWithSameKeyMaterialButDifferentKeyIds_decryptWorks() throws Exception {
405     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
406     AeadConfig.register();
407 
408     byte[] plaintext = "plaintext".getBytes(UTF_8);
409     byte[] associatedData = "associatedData".getBytes(UTF_8);
410 
411     KeysetHandle keysetHandle0 =
412         KeysetHandle.newBuilder()
413             .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(123).makePrimary())
414             .build();
415     Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class);
416     KeysetHandle keysetHandle1 =
417         KeysetHandle.newBuilder()
418             .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(42).makePrimary())
419             .build();
420     Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
421     byte[] ciphertext = aead0.encrypt(plaintext, associatedData);
422 
423     assertThat(aead1.decrypt(ciphertext, associatedData)).isEqualTo(plaintext);
424   }
425 
426   @Test
getPrimitiveFromKeysetHandleWithoutPrimary_throws()427   public void getPrimitiveFromKeysetHandleWithoutPrimary_throws() throws Exception {
428     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
429     AeadConfig.register();
430 
431     AesCtrHmacAeadKey aesCtrHmacAeadProtoKey =
432         AesCtrHmacAeadKey.newBuilder()
433             .setAesCtrKey(TestUtil.createAesCtrKey(Random.randBytes(16), 12))
434             .setHmacKey(TestUtil.createHmacKey(Random.randBytes(20), 16))
435             .build();
436     Key key =
437         TestUtil.createKey(
438             TestUtil.createKeyData(
439                 aesCtrHmacAeadProtoKey,
440                 "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey",
441                 KeyMaterialType.SYMMETRIC),
442             123,
443             KeyStatusType.ENABLED,
444             OutputPrefixType.TINK);
445     Keyset keysetWithoutPrimary = Keyset.newBuilder().addKey(key).build();
446     assertThrows(
447         GeneralSecurityException.class,
448         () ->
449             TinkProtoKeysetFormat.parseKeyset(
450                     keysetWithoutPrimary.toByteArray(), InsecureSecretKeyAccess.get())
451                 .getPrimitive(RegistryConfiguration.get(), Aead.class));
452   }
453 
454   @Test
testAeadWithoutAnnotations_hasNoMonitoring()455   public void testAeadWithoutAnnotations_hasNoMonitoring() throws Exception {
456     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
457     AeadConfig.register();
458     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
459     MutableMonitoringRegistry.globalInstance().clear();
460     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
461 
462     KeysetHandle keysetHandle =
463         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(tinkKey).makePrimary()).build();
464     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
465 
466     byte[] plaintext = "plaintext".getBytes(UTF_8);
467     byte[] associatedData = "associatedData".getBytes(UTF_8);
468     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
469     assertThat(aead.decrypt(ciphertext, associatedData)).isEqualTo(plaintext);
470     assertThrows(
471         GeneralSecurityException.class, () -> aead.decrypt(ciphertext, "invalid".getBytes(UTF_8)));
472 
473     // Without annotations, nothing gets logged.
474     assertThat(fakeMonitoringClient.getLogEntries()).isEmpty();
475     assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty();
476   }
477 
478   @Test
testAeadWithAnnotations_hasMonitoring()479   public void testAeadWithAnnotations_hasMonitoring() throws Exception {
480     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
481     AeadConfig.register();
482     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
483     MutableMonitoringRegistry.globalInstance().clear();
484     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
485 
486     byte[] plaintext = Random.randBytes(20);
487     byte[] plaintext2 = Random.randBytes(30);
488     byte[] associatedData = Random.randBytes(40);
489 
490     // generate ciphertext2 using key2
491     KeysetHandle singleKeyKeysetHandle =
492         KeysetHandle.newBuilder()
493             .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(43).makePrimary())
494             .build();
495     Aead aead2 = singleKeyKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
496     byte[] ciphertext2 = aead2.encrypt(plaintext2, associatedData);
497 
498     MonitoringAnnotations annotations =
499         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
500     KeysetHandle twoKeysKeysetHandle =
501         KeysetHandle.newBuilder()
502             .addEntry(KeysetHandle.importKey(tinkKey).makePrimary())
503             .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(43))
504             .setMonitoringAnnotations(annotations)
505             .build();
506     Aead aead = twoKeysKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
507 
508     byte[] ciphertext = aead.encrypt(plaintext, associatedData); // uses key1 to encrypt
509     byte[] decrypted = aead.decrypt(ciphertext, associatedData);
510     assertThat(decrypted).isEqualTo(plaintext);
511     byte[] decrypted2 = aead.decrypt(ciphertext2, associatedData);
512     assertThat(decrypted2).isEqualTo(plaintext2);
513 
514     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext, new byte[0]));
515 
516     List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries();
517     assertThat(logEntries).hasSize(3);
518     FakeMonitoringClient.LogEntry encEntry = logEntries.get(0);
519     assertThat(encEntry.getKeyId()).isEqualTo(42);
520     assertThat(encEntry.getPrimitive()).isEqualTo("aead");
521     assertThat(encEntry.getApi()).isEqualTo("encrypt");
522     assertThat(encEntry.getNumBytesAsInput()).isEqualTo(plaintext.length);
523     assertThat(encEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
524 
525     FakeMonitoringClient.LogEntry decEntry = logEntries.get(1);
526     assertThat(decEntry.getKeyId()).isEqualTo(42);
527     assertThat(decEntry.getPrimitive()).isEqualTo("aead");
528     assertThat(decEntry.getApi()).isEqualTo("decrypt");
529     assertThat(decEntry.getNumBytesAsInput()).isEqualTo(ciphertext.length);
530     assertThat(decEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
531 
532     FakeMonitoringClient.LogEntry dec2Entry = logEntries.get(2);
533     assertThat(dec2Entry.getKeyId()).isEqualTo(43);
534     assertThat(dec2Entry.getPrimitive()).isEqualTo("aead");
535     assertThat(dec2Entry.getApi()).isEqualTo("decrypt");
536     // ciphertext2 was encrypted with key2, which has a RAW ouput prefix.
537     assertThat(dec2Entry.getNumBytesAsInput()).isEqualTo(ciphertext2.length);
538     assertThat(dec2Entry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
539 
540     List<FakeMonitoringClient.LogFailureEntry> failures =
541         fakeMonitoringClient.getLogFailureEntries();
542     assertThat(failures).hasSize(1);
543     FakeMonitoringClient.LogFailureEntry decFailure = failures.get(0);
544     assertThat(decFailure.getPrimitive()).isEqualTo("aead");
545     assertThat(decFailure.getApi()).isEqualTo("decrypt");
546     assertThat(decFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42);
547     assertThat(decFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
548   }
549 
550   private static class AlwaysFailingAead implements Aead {
AlwaysFailingAead(com.google.crypto.tink.aead.AesCtrHmacAeadKey key)551     public AlwaysFailingAead(com.google.crypto.tink.aead.AesCtrHmacAeadKey key) {}
552 
553     @Override
encrypt(byte[] plaintext, byte[] aad)554     public byte[] encrypt(byte[] plaintext, byte[] aad) throws GeneralSecurityException {
555       throw new GeneralSecurityException("fail");
556     }
557 
558     @Override
decrypt(byte[] ciphertext, byte[] aad)559     public byte[] decrypt(byte[] ciphertext, byte[] aad) throws GeneralSecurityException {
560       throw new GeneralSecurityException("fail");
561     }
562   }
563 
564   @Test
testFailingAeadWithAnnotations_hasMonitoring()565   public void testFailingAeadWithAnnotations_hasMonitoring() throws Exception {
566     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
567     MutablePrimitiveRegistry.globalInstance()
568         .registerPrimitiveConstructor(
569             PrimitiveConstructor.create(
570                 AlwaysFailingAead::new,
571                 com.google.crypto.tink.aead.AesCtrHmacAeadKey.class,
572                 Aead.class));
573     AeadWrapper.register();
574     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
575     MutableMonitoringRegistry.globalInstance().clear();
576     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
577 
578     MonitoringAnnotations annotations =
579         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
580     KeysetHandle aesCtrHmacAeadKeysetHandle =
581         KeysetHandle.newBuilder()
582             .addEntry(KeysetHandle.importKey(tinkKey).makePrimary())
583             .setMonitoringAnnotations(annotations)
584             .build();
585     Aead aead = aesCtrHmacAeadKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
586 
587     byte[] randomBytes = Random.randBytes(20);
588     byte[] associatedData = Random.randBytes(20);
589     assertThrows(GeneralSecurityException.class, () -> aead.encrypt(randomBytes, associatedData));
590     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(randomBytes, associatedData));
591 
592     assertThat(fakeMonitoringClient.getLogEntries()).isEmpty();
593 
594     List<FakeMonitoringClient.LogFailureEntry> failures =
595         fakeMonitoringClient.getLogFailureEntries();
596     assertThat(failures).hasSize(2);
597     FakeMonitoringClient.LogFailureEntry encFailure = failures.get(0);
598     assertThat(encFailure.getPrimitive()).isEqualTo("aead");
599     assertThat(encFailure.getApi()).isEqualTo("encrypt");
600     assertThat(encFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42);
601     assertThat(encFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
602 
603     FakeMonitoringClient.LogFailureEntry decFailure = failures.get(1);
604     assertThat(decFailure.getPrimitive()).isEqualTo("aead");
605     assertThat(decFailure.getApi()).isEqualTo("decrypt");
606     assertThat(decFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42);
607     assertThat(decFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
608   }
609 
610   @Test
registerToInternalPrimitiveRegistry_works()611   public void registerToInternalPrimitiveRegistry_works() throws Exception {
612     PrimitiveRegistry.Builder initialBuilder = PrimitiveRegistry.builder();
613     PrimitiveRegistry initialRegistry = initialBuilder.build();
614     PrimitiveRegistry.Builder processedBuilder = PrimitiveRegistry.builder(initialRegistry);
615 
616     AeadWrapper.registerToInternalPrimitiveRegistry(processedBuilder);
617     PrimitiveRegistry processedRegistry = processedBuilder.build();
618 
619     assertThrows(
620         GeneralSecurityException.class,
621         () -> initialRegistry.getInputPrimitiveClass(Aead.class));
622     assertThat(processedRegistry.getInputPrimitiveClass(Aead.class))
623         .isEqualTo(Aead.class);
624   }
625 }
626