• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
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.prf;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug;
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.assertTrue;
23 
24 import com.google.crypto.tink.InsecureSecretKeyAccess;
25 import com.google.crypto.tink.KeyTemplate;
26 import com.google.crypto.tink.KeyTemplates;
27 import com.google.crypto.tink.KeysetHandle;
28 import com.google.crypto.tink.Parameters;
29 import com.google.crypto.tink.RegistryConfiguration;
30 import com.google.crypto.tink.TinkProtoKeysetFormat;
31 import com.google.crypto.tink.aead.AeadConfig;
32 import com.google.crypto.tink.aead.PredefinedAeadParameters;
33 import com.google.crypto.tink.internal.KeyManagerRegistry;
34 import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
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.subtle.Hex;
40 import com.google.crypto.tink.subtle.Random;
41 import com.google.crypto.tink.subtle.prf.HkdfStreamingPrf;
42 import com.google.crypto.tink.subtle.prf.PrfImpl;
43 import com.google.crypto.tink.subtle.prf.StreamingPrf;
44 import com.google.crypto.tink.util.Bytes;
45 import com.google.crypto.tink.util.SecretBytes;
46 import java.security.GeneralSecurityException;
47 import java.util.Set;
48 import java.util.TreeSet;
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 HkdfPrfKeyManager. */
58 @RunWith(Theories.class)
59 public class HkdfPrfKeyManagerTest {
60   @Before
register()61   public void register() throws Exception {
62     PrfConfig.register();
63     KeyDerivationConfig.register();
64     AeadConfig.register();
65   }
66 
67   @Test
testHkdfSha256Template()68   public void testHkdfSha256Template() throws Exception {
69     KeyTemplate kt = HkdfPrfKeyManager.hkdfSha256Template();
70     assertThat(kt.toParameters())
71         .isEqualTo(
72             HkdfPrfParameters.builder()
73                 .setKeySizeBytes(32)
74                 .setHashType(HkdfPrfParameters.HashType.SHA256)
75                 .setSalt(Bytes.copyFrom(new byte[] {}))
76                 .build());
77   }
78 
79   @Test
testKeyTemplateAndManagerCompatibility()80   public void testKeyTemplateAndManagerCompatibility() throws Exception {
81     Parameters p = HkdfPrfKeyManager.hkdfSha256Template().toParameters();
82     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
83   }
84 
85   @DataPoints("templateNames")
86   public static final String[] KEY_TEMPLATES =
87       new String[] {
88         "HKDF_SHA256",
89       };
90 
91   @Theory
testTemplates(@romDataPoints"templateNames") String templateName)92   public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception {
93     KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName));
94     assertThat(h.size()).isEqualTo(1);
95     assertThat(h.getAt(0).getKey().getParameters())
96         .isEqualTo(KeyTemplates.get(templateName).toParameters());
97   }
98 
99   @Test
register_registersPrfPrimitiveConstructor()100   public void register_registersPrfPrimitiveConstructor() throws Exception {
101     HkdfPrfParameters hkdfPrfParameters =
102         HkdfPrfParameters.builder()
103             .setHashType(HkdfPrfParameters.HashType.SHA256)
104             .setKeySizeBytes(32)
105             .setSalt(Bytes.copyFrom(Random.randBytes(5)))
106             .build();
107     com.google.crypto.tink.prf.HkdfPrfKey hkdfPrfKey =
108         com.google.crypto.tink.prf.HkdfPrfKey.builder()
109             .setParameters(hkdfPrfParameters)
110             .setKeyBytes(SecretBytes.copyFrom(Random.randBytes(32), InsecureSecretKeyAccess.get()))
111             .build();
112 
113     Prf prf = MutablePrimitiveRegistry.globalInstance().getPrimitive(hkdfPrfKey, Prf.class);
114 
115     assertThat(prf).isNotNull();
116   }
117 
118   @Test
register_registersStreamingPrfPrimitiveConstructor()119   public void register_registersStreamingPrfPrimitiveConstructor() throws Exception {
120     HkdfPrfParameters hkdfPrfParameters =
121         HkdfPrfParameters.builder()
122             .setHashType(HkdfPrfParameters.HashType.SHA256)
123             .setKeySizeBytes(32)
124             .setSalt(Bytes.copyFrom(Random.randBytes(5)))
125             .build();
126     com.google.crypto.tink.prf.HkdfPrfKey hkdfPrfKey =
127         com.google.crypto.tink.prf.HkdfPrfKey.builder()
128             .setParameters(hkdfPrfParameters)
129             .setKeyBytes(SecretBytes.copyFrom(Random.randBytes(32), InsecureSecretKeyAccess.get()))
130             .build();
131 
132     StreamingPrf streamingPrf =
133         MutablePrimitiveRegistry.globalInstance().getPrimitive(hkdfPrfKey, StreamingPrf.class);
134 
135     assertThat(streamingPrf).isNotNull();
136   }
137 
138   @Test
testKeyManagerRegistered()139   public void testKeyManagerRegistered() throws Exception {
140     assertThat(
141             KeyManagerRegistry.globalInstance()
142                 .getKeyManager("type.googleapis.com/google.crypto.tink.HkdfPrfKey", Prf.class))
143         .isNotNull();
144   }
145 
146   @Test
createKey_works()147   public void createKey_works() throws Exception {
148     HkdfPrfParameters params =
149         HkdfPrfParameters.builder()
150             .setHashType(HkdfPrfParameters.HashType.SHA256)
151             .setKeySizeBytes(32)
152             .build();
153     KeysetHandle handle = KeysetHandle.generateNew(params);
154     assertThat(handle.size()).isEqualTo(1);
155     com.google.crypto.tink.prf.HkdfPrfKey key =
156         (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey();
157     assertThat(key.getParameters()).isEqualTo(params);
158   }
159 
160   @Test
createKey_otherParams_works()161   public void createKey_otherParams_works() throws Exception {
162     HkdfPrfParameters params =
163         HkdfPrfParameters.builder()
164             .setHashType(HkdfPrfParameters.HashType.SHA512)
165             .setKeySizeBytes(32)
166             .build();
167     KeysetHandle handle = KeysetHandle.generateNew(params);
168     assertThat(handle.size()).isEqualTo(1);
169     com.google.crypto.tink.prf.HkdfPrfKey key =
170         (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey();
171     assertThat(key.getParameters()).isEqualTo(params);
172   }
173 
174   @Test
createKey_differentKeyValues_alwaysDifferent()175   public void createKey_differentKeyValues_alwaysDifferent() throws Exception {
176     HkdfPrfParameters params =
177         HkdfPrfParameters.builder()
178             .setHashType(HkdfPrfParameters.HashType.SHA512)
179             .setKeySizeBytes(32)
180             .build();
181 
182     int numKeys = 100;
183     Set<String> keys = new TreeSet<>();
184     for (int i = 0; i < numKeys; i++) {
185       KeysetHandle handle = KeysetHandle.generateNew(params);
186       assertThat(handle.size()).isEqualTo(1);
187       com.google.crypto.tink.prf.HkdfPrfKey key =
188           (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey();
189       keys.add(Hex.encode(key.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get())));
190     }
191     assertThat(keys).hasSize(numKeys);
192   }
193 
194   @Test
createPrimitiveAndUseIt_works()195   public void createPrimitiveAndUseIt_works() throws Exception {
196     HkdfPrfParameters params =
197         HkdfPrfParameters.builder()
198             .setHashType(HkdfPrfParameters.HashType.SHA512)
199             .setKeySizeBytes(32)
200             .build();
201     KeysetHandle handle = KeysetHandle.generateNew(params);
202     assertThat(handle.size()).isEqualTo(1);
203     PrfSet prfSet = handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class);
204     Prf directPrf =
205         PrfImpl.wrap(
206             HkdfStreamingPrf.create(
207                 (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey()));
208     assertThat(prfSet.computePrimary(new byte[0], 16))
209         .isEqualTo(directPrf.compute(new byte[0], 16));
210   }
211 
212   @Test
serializeAndDeserializeKeysets()213   public void serializeAndDeserializeKeysets() throws Exception {
214     HkdfPrfParameters params =
215         HkdfPrfParameters.builder()
216             .setHashType(HkdfPrfParameters.HashType.SHA512)
217             .setKeySizeBytes(32)
218             .build();
219     KeysetHandle handle = KeysetHandle.generateNew(params);
220 
221     byte[] serializedKeyset =
222         TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get());
223     KeysetHandle parsed =
224         TinkProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get());
225     assertTrue(parsed.equalsKeyset(handle));
226   }
227 
228   @Theory
createKeyWithRejectedParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)229   public void createKeyWithRejectedParameters_throws(
230       @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception {
231     assertThrows(GeneralSecurityException.class, () -> KeysetHandle.generateNew(params));
232   }
233 
234   @Theory
createPrimitiveWithRejectedParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)235   public void createPrimitiveWithRejectedParameters_throws(
236       @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception {
237     com.google.crypto.tink.prf.HkdfPrfKey key =
238         com.google.crypto.tink.prf.HkdfPrfKey.builder()
239             .setParameters(params)
240             .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes()))
241             .build();
242     KeysetHandle handle =
243         KeysetHandle.newBuilder()
244             .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary())
245             .build();
246     assertThrows(
247         GeneralSecurityException.class,
248         () -> handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class));
249   }
250 
251   /** We allow serialization and deserialization with parameters which are otherwise rejected */
252   @Theory
serializeDeserializeKeysetsWithRejectedParams_works( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)253   public void serializeDeserializeKeysetsWithRejectedParams_works(
254       @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception {
255     com.google.crypto.tink.prf.HkdfPrfKey key =
256         com.google.crypto.tink.prf.HkdfPrfKey.builder()
257             .setParameters(params)
258             .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes()))
259             .build();
260     KeysetHandle handle =
261         KeysetHandle.newBuilder()
262             .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary())
263             .build();
264     byte[] serializedKeyset =
265         TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get());
266     KeysetHandle parsed =
267         TinkProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get());
268     assertTrue(parsed.equalsKeyset(handle));
269   }
270 
271   @Theory
deriveAesGcmKey_withInvalidPrfParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)272   public void deriveAesGcmKey_withInvalidPrfParameters_throws(
273       @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception {
274     PrfKey prfKeyForDeriver =
275         HkdfPrfKey.builder()
276             .setParameters(params)
277             .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes()))
278             .build();
279     PrfBasedKeyDerivationParameters derivationParameters =
280         PrfBasedKeyDerivationParameters.builder()
281             .setDerivedKeyParameters(PredefinedAeadParameters.AES128_GCM)
282             .setPrfParameters(prfKeyForDeriver.getParameters())
283             .build();
284     PrfBasedKeyDerivationKey key =
285         PrfBasedKeyDerivationKey.create(
286             derivationParameters, prfKeyForDeriver, /* idRequirement= */ 112233);
287 
288     KeysetHandle keyset =
289         KeysetHandle.newBuilder()
290             .addEntry(KeysetHandle.importKey(key).withFixedId(112233).makePrimary())
291             .build();
292     assertThrows(
293         GeneralSecurityException.class,
294         () -> keyset.getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class));
295   }
296 
createRejectedParameters()297   private static HkdfPrfParameters[] createRejectedParameters() {
298     return exceptionIsBug(
299         () ->
300             new HkdfPrfParameters[] {
301               // Key Size 16 is rejected
302               HkdfPrfParameters.builder()
303                   .setHashType(HkdfPrfParameters.HashType.SHA512)
304                   .setKeySizeBytes(16)
305                   .build(),
306               // Only SHA256 and SHA512 are accepted
307               HkdfPrfParameters.builder()
308                   .setHashType(HkdfPrfParameters.HashType.SHA1)
309                   .setKeySizeBytes(32)
310                   .build(),
311               HkdfPrfParameters.builder()
312                   .setHashType(HkdfPrfParameters.HashType.SHA224)
313                   .setKeySizeBytes(32)
314                   .build(),
315               HkdfPrfParameters.builder()
316                   .setHashType(HkdfPrfParameters.HashType.SHA384)
317                   .setKeySizeBytes(32)
318                   .build()
319             });
320   }
321 
322   @DataPoints("KeyManager rejected")
323   public static final HkdfPrfParameters[] RECJECTED_PARAMETERS = createRejectedParameters();
324 }
325