• 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 import static org.junit.Assert.assertTrue;
23 
24 import com.google.crypto.tink.Aead;
25 import com.google.crypto.tink.InsecureSecretKeyAccess;
26 import com.google.crypto.tink.Key;
27 import com.google.crypto.tink.KeyTemplate;
28 import com.google.crypto.tink.KeyTemplates;
29 import com.google.crypto.tink.KeysetHandle;
30 import com.google.crypto.tink.Parameters;
31 import com.google.crypto.tink.RegistryConfiguration;
32 import com.google.crypto.tink.TinkProtoKeysetFormat;
33 import com.google.crypto.tink.internal.KeyManagerRegistry;
34 import com.google.crypto.tink.internal.SlowInputStream;
35 import com.google.crypto.tink.keyderivation.KeyDerivationConfig;
36 import com.google.crypto.tink.keyderivation.KeysetDeriver;
37 import com.google.crypto.tink.keyderivation.PrfBasedKeyDerivationKey;
38 import com.google.crypto.tink.keyderivation.PrfBasedKeyDerivationParameters;
39 import com.google.crypto.tink.prf.HkdfPrfKey;
40 import com.google.crypto.tink.prf.HkdfPrfParameters;
41 import com.google.crypto.tink.prf.PrfKey;
42 import com.google.crypto.tink.subtle.EncryptThenAuthenticate;
43 import com.google.crypto.tink.subtle.Hex;
44 import com.google.crypto.tink.subtle.Random;
45 import com.google.crypto.tink.util.SecretBytes;
46 import java.io.ByteArrayInputStream;
47 import java.security.GeneralSecurityException;
48 import java.util.Arrays;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.experimental.theories.DataPoints;
52 import org.junit.experimental.theories.FromDataPoints;
53 import org.junit.experimental.theories.Theories;
54 import org.junit.experimental.theories.Theory;
55 import org.junit.runner.RunWith;
56 
57 /** Tests for AesCtrHmacAeadKeyManager. */
58 @RunWith(Theories.class)
59 public class AesCtrHmacAeadKeyManagerTest {
60   @Before
register()61   public void register() throws Exception {
62     AeadConfig.register();
63     KeyDerivationConfig.register();
64   }
65 
66   @Test
testKeyManagerRegistered()67   public void testKeyManagerRegistered() throws Exception {
68     assertThat(
69             KeyManagerRegistry.globalInstance()
70                 .getKeyManager(
71                     "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", Aead.class))
72         .isNotNull();
73   }
74 
75   @Test
getPrimtive_encryptDecryptTink_worksAsDirectlyCreated()76   public void getPrimtive_encryptDecryptTink_worksAsDirectlyCreated() throws Exception {
77     AesCtrHmacAeadParameters parameters =
78         AesCtrHmacAeadParameters.builder()
79             .setAesKeySizeBytes(32)
80             .setHmacKeySizeBytes(32)
81             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
82             .setTagSizeBytes(17)
83             .setIvSizeBytes(14)
84             .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
85             .build();
86     com.google.crypto.tink.aead.AesCtrHmacAeadKey key =
87         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
88             .setAesKeyBytes(SecretBytes.randomBytes(32))
89             .setHmacKeyBytes(SecretBytes.randomBytes(32))
90             .setParameters(parameters)
91             .setIdRequirement(42)
92             .build();
93     KeysetHandle keysetHandle =
94         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build();
95     byte[] plaintext = "plaintext".getBytes(UTF_8);
96     byte[] aad = "aad".getBytes(UTF_8);
97 
98     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
99     Aead directAead = EncryptThenAuthenticate.create(key);
100 
101     assertThat(directAead.decrypt(aead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext);
102     assertThat(aead.decrypt(directAead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext);
103   }
104 
105   @Test
getPrimitive_encryptDecryptCrunchy_works()106   public void getPrimitive_encryptDecryptCrunchy_works() throws Exception {
107     AesCtrHmacAeadParameters parameters =
108         AesCtrHmacAeadParameters.builder()
109             .setAesKeySizeBytes(16)
110             .setHmacKeySizeBytes(32)
111             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
112             .setTagSizeBytes(18)
113             .setIvSizeBytes(13)
114             .setVariant(AesCtrHmacAeadParameters.Variant.CRUNCHY)
115             .build();
116     com.google.crypto.tink.aead.AesCtrHmacAeadKey key =
117         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
118             .setAesKeyBytes(SecretBytes.randomBytes(16))
119             .setHmacKeyBytes(SecretBytes.randomBytes(32))
120             .setParameters(parameters)
121             .setIdRequirement(42)
122             .build();
123     KeysetHandle keysetHandle =
124         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build();
125     byte[] plaintext = "plaintext".getBytes(UTF_8);
126     byte[] aad = "aad".getBytes(UTF_8);
127 
128     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
129 
130     assertThat(aead.decrypt(aead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext);
131   }
132 
133   @Test
getPrimitive_bitFlipCiphertext_throws()134   public void getPrimitive_bitFlipCiphertext_throws() throws Exception {
135     AesCtrHmacAeadParameters parameters =
136         AesCtrHmacAeadParameters.builder()
137             .setAesKeySizeBytes(32)
138             .setHmacKeySizeBytes(16)
139             .setHashType(AesCtrHmacAeadParameters.HashType.SHA512)
140             .setTagSizeBytes(16)
141             .setIvSizeBytes(12)
142             .setVariant(AesCtrHmacAeadParameters.Variant.CRUNCHY)
143             .build();
144     com.google.crypto.tink.aead.AesCtrHmacAeadKey key =
145         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
146             .setAesKeyBytes(SecretBytes.randomBytes(32))
147             .setHmacKeyBytes(SecretBytes.randomBytes(16))
148             .setParameters(parameters)
149             .setIdRequirement(42)
150             .build();
151     KeysetHandle keysetHandle =
152         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build();
153     byte[] plaintext = Random.randBytes(1001);
154     byte[] aad = Random.randBytes(13);
155 
156     Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
157     byte[] ciphertext = aead.encrypt(plaintext, aad);
158 
159     for (int i = 0; i < ciphertext.length; i++) {
160       for (int j = 0; j < 8; j++) {
161         byte[] c1 = Arrays.copyOf(ciphertext, ciphertext.length);
162         c1[i] = (byte) (c1[i] ^ (1 << j));
163         assertThrows(GeneralSecurityException.class, () -> aead.decrypt(c1, aad));
164       }
165     }
166   }
167 
168   @Test
testAes128CtrHmacSha256Template()169   public void testAes128CtrHmacSha256Template() throws Exception {
170     KeyTemplate template = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
171     assertThat(template.toParameters())
172         .isEqualTo(
173             AesCtrHmacAeadParameters.builder()
174                 .setAesKeySizeBytes(16)
175                 .setHmacKeySizeBytes(32)
176                 .setIvSizeBytes(16)
177                 .setTagSizeBytes(16)
178                 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
179                 .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
180                 .build());
181   }
182 
183   @Test
testAes256CtrHmacSha256Template()184   public void testAes256CtrHmacSha256Template() throws Exception {
185     KeyTemplate template = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template();
186     assertThat(template.toParameters())
187         .isEqualTo(
188             AesCtrHmacAeadParameters.builder()
189                 .setAesKeySizeBytes(32)
190                 .setHmacKeySizeBytes(32)
191                 .setIvSizeBytes(16)
192                 .setTagSizeBytes(32)
193                 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
194                 .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
195                 .build());
196   }
197 
198   @Test
testKeyTemplatesWork()199   public void testKeyTemplatesWork() throws Exception {
200     Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters();
201     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
202 
203     p = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template().toParameters();
204     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
205   }
206 
207   @DataPoints("templateNames")
208   public static final String[] KEY_TEMPLATES =
209       new String[] {
210         "AES128_CTR_HMAC_SHA256",
211         "AES128_CTR_HMAC_SHA256_RAW",
212         "AES256_CTR_HMAC_SHA256",
213         "AES256_CTR_HMAC_SHA256_RAW",
214       };
215 
216   @Theory
testTemplates(@romDataPoints"templateNames") String templateName)217   public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception {
218     KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName));
219     assertThat(h.size()).isEqualTo(1);
220     assertThat(h.getAt(0).getKey().getParameters())
221         .isEqualTo(KeyTemplates.get(templateName).toParameters());
222   }
223 
224   @Test
callingCreateTwiceGivesDifferentKeys()225   public void callingCreateTwiceGivesDifferentKeys() throws Exception {
226     Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters();
227     Key key = KeysetHandle.generateNew(p).getAt(0).getKey();
228     for (int i = 0; i < 1000; ++i) {
229       assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().equalsKey(key)).isFalse();
230     }
231   }
232 
233   @Theory
testCreateKeyFromRandomness(@romDataPoints"templateNames") String templateName)234   public void testCreateKeyFromRandomness(@FromDataPoints("templateNames") String templateName)
235       throws Exception {
236     byte[] keyMaterial =
237         new byte[] {
238           0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
239           25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
240           47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
241         };
242     AesCtrHmacAeadParameters parameters =
243         (AesCtrHmacAeadParameters) KeyTemplates.get(templateName).toParameters();
244     com.google.crypto.tink.aead.AesCtrHmacAeadKey key =
245         AesCtrHmacAeadKeyManager.createAesCtrHmacAeadKeyFromRandomness(
246             parameters,
247             new ByteArrayInputStream(keyMaterial),
248             parameters.hasIdRequirement() ? 123 : null,
249             InsecureSecretKeyAccess.get());
250     byte[] expectedAesKey = Arrays.copyOf(keyMaterial, parameters.getAesKeySizeBytes());
251     byte[] expectedHmacKey =
252         Arrays.copyOfRange(
253             keyMaterial,
254             parameters.getAesKeySizeBytes(),
255             parameters.getAesKeySizeBytes() + parameters.getHmacKeySizeBytes());
256     Key expectedKey =
257         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
258             .setParameters(parameters)
259             .setIdRequirement(parameters.hasIdRequirement() ? 123 : null)
260             .setAesKeyBytes(SecretBytes.copyFrom(expectedAesKey, InsecureSecretKeyAccess.get()))
261             .setHmacKeyBytes(SecretBytes.copyFrom(expectedHmacKey, InsecureSecretKeyAccess.get()))
262             .build();
263     assertTrue(key.equalsKey(expectedKey));
264   }
265 
266   @Test
testCreateKeyFromRandomness_slowInputStream_works()267   public void testCreateKeyFromRandomness_slowInputStream_works() throws Exception {
268     byte[] keyMaterial =
269         new byte[] {
270           0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
271           25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
272           47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
273         };
274     AesCtrHmacAeadParameters parameters =
275         AesCtrHmacAeadParameters.builder()
276             .setAesKeySizeBytes(32)
277             .setHmacKeySizeBytes(32)
278             .setIvSizeBytes(16)
279             .setTagSizeBytes(32)
280             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
281             .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
282             .build();
283     com.google.crypto.tink.aead.AesCtrHmacAeadKey key =
284         AesCtrHmacAeadKeyManager.createAesCtrHmacAeadKeyFromRandomness(
285             parameters,
286             SlowInputStream.copyFrom(keyMaterial),
287             12347,
288             InsecureSecretKeyAccess.get());
289     byte[] expectedAesKey = Arrays.copyOf(keyMaterial, parameters.getAesKeySizeBytes());
290     byte[] expectedHmacKey =
291         Arrays.copyOfRange(
292             keyMaterial,
293             parameters.getAesKeySizeBytes(),
294             parameters.getAesKeySizeBytes() + parameters.getHmacKeySizeBytes());
295     Key expectedKey =
296         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
297             .setParameters(parameters)
298             .setIdRequirement(12347)
299             .setAesKeyBytes(SecretBytes.copyFrom(expectedAesKey, InsecureSecretKeyAccess.get()))
300             .setHmacKeyBytes(SecretBytes.copyFrom(expectedHmacKey, InsecureSecretKeyAccess.get()))
301             .build();
302     assertTrue(key.equalsKey(expectedKey));
303   }
304 
createDerivationKey(Parameters derivedParameters, int id)305   private static PrfBasedKeyDerivationKey createDerivationKey(Parameters derivedParameters, int id)
306       throws Exception {
307     PrfKey prfKey =
308         HkdfPrfKey.builder()
309             .setParameters(
310                 HkdfPrfParameters.builder()
311                     .setKeySizeBytes(32)
312                     .setHashType(HkdfPrfParameters.HashType.SHA256)
313                     .build())
314             .setKeyBytes(
315                 SecretBytes.copyFrom(
316                     Hex.decode("0102030405060708091011121314151617181920212123242526272829303132"),
317                     InsecureSecretKeyAccess.get()))
318             .build();
319     PrfBasedKeyDerivationParameters derivationParameters =
320         PrfBasedKeyDerivationParameters.builder()
321             .setDerivedKeyParameters(derivedParameters)
322             .setPrfParameters(prfKey.getParameters())
323             .build();
324     return PrfBasedKeyDerivationKey.create(derivationParameters, prfKey, /* idRequirement= */ id);
325   }
326 
secretBytesFromHex(String hex)327   private static final SecretBytes secretBytesFromHex(String hex) {
328     return SecretBytes.copyFrom(Hex.decode(hex), InsecureSecretKeyAccess.get());
329   }
330 
331   @Test
testDeriveKey_predefinedKey_works()332   public void testDeriveKey_predefinedKey_works() throws Exception {
333     // Same test vector as in PrfBasedKeyDeriverTest
334     KeysetDeriver deriver =
335         KeysetHandle.newBuilder()
336             .addEntry(
337                 KeysetHandle.importKey(
338                         createDerivationKey(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256, 24680))
339                     .makePrimary())
340             .build()
341             .getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class);
342     KeysetHandle derivedKeyset = deriver.deriveKeyset(Hex.decode("000102"));
343     assertThat(derivedKeyset.size()).isEqualTo(1);
344     assertThat(derivedKeyset.getAt(0).getKey().getParameters())
345         .isEqualTo(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256);
346     Key expectedKey =
347         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
348             .setParameters(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256)
349             .setIdRequirement(24680)
350             .setAesKeyBytes(secretBytesFromHex("94e397d674deda6e965295698491a3fe"))
351             .setHmacKeyBytes(
352                 secretBytesFromHex(
353                     "b69838a35f1d48143f3c4cbad90eeb249c8ddea6d09adc5f89a9a190122b095d"))
354             .build();
355 
356     KeysetHandle expectedKeyset =
357         KeysetHandle.newBuilder()
358             .addEntry(KeysetHandle.importKey(expectedKey).makePrimary())
359             .build();
360     assertTrue(derivedKeyset.equalsKeyset(expectedKeyset));
361   }
362 
363   @Test
testDeriveKey_24byteAes_throws()364   public void testDeriveKey_24byteAes_throws() throws Exception {
365     KeysetHandle derivationHandle =
366         KeysetHandle.newBuilder()
367             .addEntry(
368                 KeysetHandle.importKey(
369                         createDerivationKey(
370                             AesCtrHmacAeadParameters.builder()
371                                 .setAesKeySizeBytes(24)
372                                 .setHmacKeySizeBytes(32)
373                                 .setTagSizeBytes(16)
374                                 .setIvSizeBytes(16)
375                                 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
376                                 .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
377                                 .build(),
378                             24680))
379                     .makePrimary())
380             .build();
381     // TODO(tholenst): This should throw.
382     Object unused =
383         derivationHandle
384             .getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class)
385             .deriveKeyset(Hex.decode("000102"));
386   }
387 
388   @Test
testNewKey_validationHappens_throws()389   public void testNewKey_validationHappens_throws() throws Exception {
390     AesCtrHmacAeadParameters rejectedParameters =
391         AesCtrHmacAeadParameters.builder()
392             .setAesKeySizeBytes(24)
393             .setHmacKeySizeBytes(32)
394             .setIvSizeBytes(16)
395             .setTagSizeBytes(32)
396             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
397             .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
398             .build();
399     assertThrows(
400         GeneralSecurityException.class, () -> KeysetHandle.generateNew(rejectedParameters));
401   }
402 
403   @Test
testGetPrimitive_validationHappens_throws()404   public void testGetPrimitive_validationHappens_throws() throws Exception {
405     AesCtrHmacAeadParameters rejectedParameters =
406         AesCtrHmacAeadParameters.builder()
407             .setAesKeySizeBytes(24)
408             .setHmacKeySizeBytes(32)
409             .setIvSizeBytes(16)
410             .setTagSizeBytes(32)
411             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
412             .setVariant(AesCtrHmacAeadParameters.Variant.TINK)
413             .build();
414     com.google.crypto.tink.aead.AesCtrHmacAeadKey rejectedKey =
415         com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder()
416             .setParameters(rejectedParameters)
417             .setIdRequirement(123456)
418             .setAesKeyBytes(SecretBytes.randomBytes(rejectedParameters.getAesKeySizeBytes()))
419             .setHmacKeyBytes(SecretBytes.randomBytes(rejectedParameters.getHmacKeySizeBytes()))
420             .build();
421     assertThrows(
422         GeneralSecurityException.class,
423         () ->
424             KeysetHandle.newBuilder()
425                 .addEntry(KeysetHandle.importKey(rejectedKey).makePrimary())
426                 .build()
427                 .getPrimitive(RegistryConfiguration.get(), Aead.class));
428   }
429 
430   @Test
serializeAndParse_works()431   public void serializeAndParse_works() throws Exception {
432     Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters();
433     KeysetHandle handle = KeysetHandle.generateNew(p);
434 
435     byte[] serialized =
436         TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get());
437     KeysetHandle parsed =
438         TinkProtoKeysetFormat.parseKeyset(serialized, InsecureSecretKeyAccess.get());
439     assertThat(parsed.equalsKeyset(handle)).isTrue();
440   }
441 }
442