• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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.hybrid;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import com.google.crypto.tink.HybridDecrypt;
23 import com.google.crypto.tink.HybridEncrypt;
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.AesCtrHmacAeadParameters;
33 import com.google.crypto.tink.aead.AesGcmParameters;
34 import com.google.crypto.tink.aead.XChaCha20Poly1305Parameters;
35 import com.google.crypto.tink.hybrid.internal.testing.EciesAeadHkdfTestUtil;
36 import com.google.crypto.tink.hybrid.internal.testing.HybridTestVector;
37 import com.google.crypto.tink.internal.KeyManagerRegistry;
38 import com.google.crypto.tink.subtle.Hex;
39 import com.google.crypto.tink.subtle.X25519;
40 import com.google.crypto.tink.util.Bytes;
41 import com.google.crypto.tink.util.SecretBytes;
42 import java.math.BigInteger;
43 import java.security.GeneralSecurityException;
44 import java.util.Arrays;
45 import java.util.Set;
46 import java.util.TreeSet;
47 import javax.annotation.Nullable;
48 import org.junit.BeforeClass;
49 import org.junit.Test;
50 import org.junit.experimental.theories.DataPoints;
51 import org.junit.experimental.theories.FromDataPoints;
52 import org.junit.experimental.theories.Theories;
53 import org.junit.experimental.theories.Theory;
54 import org.junit.runner.RunWith;
55 
56 /** Tests for EciesAeadHkdfPrivateKeyManager. */
57 @RunWith(Theories.class)
58 public class EciesAeadHkdfPrivateKeyManagerTest {
59   @BeforeClass
setUp()60   public static void setUp() throws Exception {
61     AeadConfig.register();
62     HybridConfig.register();
63   }
64 
65   @Test
testEciesP256HkdfHmacSha256Aes128GcmTemplate()66   public void testEciesP256HkdfHmacSha256Aes128GcmTemplate() throws Exception {
67     KeyTemplate template =
68         EciesAeadHkdfPrivateKeyManager.eciesP256HkdfHmacSha256Aes128GcmTemplate();
69     assertThat(template.toParameters())
70         .isEqualTo(
71             EciesParameters.builder()
72                 .setCurveType(EciesParameters.CurveType.NIST_P256)
73                 .setHashType(EciesParameters.HashType.SHA256)
74                 .setNistCurvePointFormat(EciesParameters.PointFormat.UNCOMPRESSED)
75                 .setVariant(EciesParameters.Variant.TINK)
76                 .setDemParameters(
77                     AesGcmParameters.builder()
78                         .setIvSizeBytes(12)
79                         .setKeySizeBytes(16)
80                         .setTagSizeBytes(16)
81                         .setVariant(AesGcmParameters.Variant.NO_PREFIX)
82                         .build())
83                 .build());
84   }
85 
86   @Test
testRawEciesP256HkdfHmacSha256Aes128GcmCompressedTemplate()87   public void testRawEciesP256HkdfHmacSha256Aes128GcmCompressedTemplate() throws Exception {
88     KeyTemplate template =
89         EciesAeadHkdfPrivateKeyManager.rawEciesP256HkdfHmacSha256Aes128GcmCompressedTemplate();
90     assertThat(template.toParameters())
91         .isEqualTo(
92             EciesParameters.builder()
93                 .setCurveType(EciesParameters.CurveType.NIST_P256)
94                 .setHashType(EciesParameters.HashType.SHA256)
95                 .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
96                 .setVariant(EciesParameters.Variant.NO_PREFIX)
97                 .setDemParameters(
98                     AesGcmParameters.builder()
99                         .setIvSizeBytes(12)
100                         .setKeySizeBytes(16)
101                         .setTagSizeBytes(16)
102                         .setVariant(AesGcmParameters.Variant.NO_PREFIX)
103                         .build())
104                 .build());
105   }
106 
107   @Test
testEciesP256HkdfHmacSha256Aes128CtrHmacSha256Template()108   public void testEciesP256HkdfHmacSha256Aes128CtrHmacSha256Template() throws Exception {
109     KeyTemplate template =
110         EciesAeadHkdfPrivateKeyManager.eciesP256HkdfHmacSha256Aes128CtrHmacSha256Template();
111 
112     assertThat(template.toParameters())
113         .isEqualTo(
114             EciesParameters.builder()
115                 .setCurveType(EciesParameters.CurveType.NIST_P256)
116                 .setHashType(EciesParameters.HashType.SHA256)
117                 .setNistCurvePointFormat(EciesParameters.PointFormat.UNCOMPRESSED)
118                 .setVariant(EciesParameters.Variant.TINK)
119                 .setDemParameters(
120                     AesCtrHmacAeadParameters.builder()
121                         .setAesKeySizeBytes(16)
122                         .setHmacKeySizeBytes(32)
123                         .setTagSizeBytes(16)
124                         .setIvSizeBytes(16)
125                         .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
126                         .setVariant(AesCtrHmacAeadParameters.Variant.NO_PREFIX)
127                         .build())
128                 .build());
129   }
130 
131   @Test
testRawEciesP256HkdfHmacSha256Aes128CtrHmacSha256CompressedTemplate()132   public void testRawEciesP256HkdfHmacSha256Aes128CtrHmacSha256CompressedTemplate()
133       throws Exception {
134     KeyTemplate template =
135         EciesAeadHkdfPrivateKeyManager
136             .rawEciesP256HkdfHmacSha256Aes128CtrHmacSha256CompressedTemplate();
137 
138     assertThat(template.toParameters())
139         .isEqualTo(
140             EciesParameters.builder()
141                 .setCurveType(EciesParameters.CurveType.NIST_P256)
142                 .setHashType(EciesParameters.HashType.SHA256)
143                 .setNistCurvePointFormat(EciesParameters.PointFormat.COMPRESSED)
144                 .setVariant(EciesParameters.Variant.NO_PREFIX)
145                 .setDemParameters(
146                     AesCtrHmacAeadParameters.builder()
147                         .setAesKeySizeBytes(16)
148                         .setHmacKeySizeBytes(32)
149                         .setTagSizeBytes(16)
150                         .setIvSizeBytes(16)
151                         .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
152                         .setVariant(AesCtrHmacAeadParameters.Variant.NO_PREFIX)
153                         .build())
154                 .build());
155   }
156 
157   @Test
testKeyTemplatesWork()158   public void testKeyTemplatesWork() throws Exception {
159     Parameters p =
160         EciesAeadHkdfPrivateKeyManager.eciesP256HkdfHmacSha256Aes128GcmTemplate().toParameters();
161     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
162 
163     p =
164         EciesAeadHkdfPrivateKeyManager.rawEciesP256HkdfHmacSha256Aes128GcmCompressedTemplate()
165             .toParameters();
166     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
167 
168     p =
169         EciesAeadHkdfPrivateKeyManager.eciesP256HkdfHmacSha256Aes128CtrHmacSha256Template()
170             .toParameters();
171     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
172 
173     p =
174         EciesAeadHkdfPrivateKeyManager
175             .rawEciesP256HkdfHmacSha256Aes128CtrHmacSha256CompressedTemplate()
176             .toParameters();
177     assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p);
178   }
179 
180   @DataPoints("templateNames")
181   public static final String[] KEY_TEMPLATES =
182       new String[] {
183         "ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM",
184         "ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM_RAW",
185         "ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM",
186         "ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM_RAW",
187         "ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256",
188         "ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_RAW",
189         "ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256",
190         "ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256_RAW",
191       };
192 
193   @Theory
testTemplates(@romDataPoints"templateNames") String templateName)194   public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception {
195     KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName));
196     assertThat(h.size()).isEqualTo(1);
197     assertThat(h.getAt(0).getKey().getParameters())
198         .isEqualTo(KeyTemplates.get(templateName).toParameters());
199   }
200 
201   @Test
createKey_nistCurve_alwaysDifferent()202   public void createKey_nistCurve_alwaysDifferent() throws Exception {
203     Parameters params = KeyTemplates.get("ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM").toParameters();
204 
205     int numKeys = 10;
206     Set<BigInteger> keys = new TreeSet<>();
207     for (int i = 0; i < numKeys; i++) {
208       KeysetHandle handle = KeysetHandle.generateNew(params);
209       assertThat(handle.size()).isEqualTo(1);
210       EciesPrivateKey key = (EciesPrivateKey) handle.getPrimary().getKey();
211       keys.add(key.getNistPrivateKeyValue().getBigInteger(InsecureSecretKeyAccess.get()));
212     }
213     assertThat(keys).hasSize(numKeys);
214   }
215 
216   @Test
createKey_x25519Curve_throws()217   public void createKey_x25519Curve_throws() throws Exception {
218     Parameters params =
219         EciesParameters.builder()
220             .setCurveType(EciesParameters.CurveType.X25519)
221             .setHashType(EciesParameters.HashType.SHA256)
222             .setVariant(EciesParameters.Variant.NO_PREFIX)
223             .setDemParameters(
224                 AesGcmParameters.builder()
225                     .setIvSizeBytes(12)
226                     .setKeySizeBytes(16)
227                     .setTagSizeBytes(16)
228                     .setVariant(AesGcmParameters.Variant.NO_PREFIX)
229                     .build())
230             .build();
231     assertThrows(GeneralSecurityException.class, () -> KeysetHandle.generateNew(params));
232   }
233 
234   @Test
createPrimitive_x25519Curve_throws()235   public void createPrimitive_x25519Curve_throws() throws Exception {
236     EciesParameters params =
237         EciesParameters.builder()
238             .setHashType(EciesParameters.HashType.SHA256)
239             .setCurveType(EciesParameters.CurveType.X25519)
240             .setVariant(EciesParameters.Variant.NO_PREFIX)
241             .setDemParameters(XChaCha20Poly1305Parameters.create())
242             .build();
243 
244     byte[] privateKeyBytes = X25519.generatePrivateKey();
245     byte[] publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
246 
247     EciesPublicKey publicKey =
248         EciesPublicKey.createForCurveX25519(
249             params, Bytes.copyFrom(publicKeyBytes), /* idRequirement= */ null);
250     EciesPrivateKey privateKey =
251         EciesPrivateKey.createForCurveX25519(
252             publicKey, SecretBytes.copyFrom(privateKeyBytes, InsecureSecretKeyAccess.get()));
253 
254     KeysetHandle.Builder.Entry entry =
255         KeysetHandle.importKey(privateKey).makePrimary().withRandomId();
256     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
257     assertThrows(
258         GeneralSecurityException.class,
259         () -> handle.getPrimitive(RegistryConfiguration.get(), HybridDecrypt.class));
260   }
261 
262   @DataPoints("testVectors")
263   public static final HybridTestVector[] HYBRID_TEST_VECTORS =
264       EciesAeadHkdfTestUtil.createEciesTestVectors();
265 
266   @Theory
test_decryptCiphertext_works(@romDataPoints"testVectors") HybridTestVector v)267   public void test_decryptCiphertext_works(@FromDataPoints("testVectors") HybridTestVector v)
268       throws Exception {
269     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
270     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
271     if (id == null) {
272       entry.withRandomId();
273     } else {
274       entry.withFixedId(id);
275     }
276     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
277     HybridDecrypt hybridDecrypt =
278         handle.getPrimitive(RegistryConfiguration.get(), HybridDecrypt.class);
279     byte[] plaintext = hybridDecrypt.decrypt(v.getCiphertext(), v.getContextInfo());
280     assertThat(Hex.encode(plaintext)).isEqualTo(Hex.encode(v.getPlaintext()));
281   }
282 
283   @Theory
test_decryptWrongContextInfo_throws(@romDataPoints"testVectors") HybridTestVector v)284   public void test_decryptWrongContextInfo_throws(@FromDataPoints("testVectors") HybridTestVector v)
285       throws Exception {
286     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
287     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
288     if (id == null) {
289       entry.withRandomId();
290     } else {
291       entry.withFixedId(id);
292     }
293     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
294     HybridDecrypt hybridDecrypt =
295         handle.getPrimitive(RegistryConfiguration.get(), HybridDecrypt.class);
296     byte[] contextInfo = v.getContextInfo();
297     if (contextInfo.length > 0) {
298       contextInfo[0] ^= 1;
299     } else {
300       contextInfo = new byte[] {1};
301     }
302     // local variables referenced from a lambda expression must be final or effectively final
303     final byte[] contextInfoCopy = Arrays.copyOf(contextInfo, contextInfo.length);
304     assertThrows(
305         GeneralSecurityException.class,
306         () -> hybridDecrypt.decrypt(v.getCiphertext(), contextInfoCopy));
307   }
308 
309   @Theory
test_encryptThenDecryptMessage_works( @romDataPoints"testVectors") HybridTestVector v)310   public void test_encryptThenDecryptMessage_works(
311       @FromDataPoints("testVectors") HybridTestVector v) throws Exception {
312     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
313     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
314     if (id == null) {
315       entry.withRandomId();
316     } else {
317       entry.withFixedId(id);
318     }
319     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
320     HybridDecrypt hybridDecrypt =
321         handle.getPrimitive(RegistryConfiguration.get(), HybridDecrypt.class);
322     HybridEncrypt hybridEncrypt =
323         handle
324             .getPublicKeysetHandle()
325             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
326     byte[] ciphertext = hybridEncrypt.encrypt(v.getPlaintext(), v.getContextInfo());
327     byte[] plaintext = hybridDecrypt.decrypt(ciphertext, v.getContextInfo());
328     assertThat(Hex.encode(plaintext)).isEqualTo(Hex.encode(v.getPlaintext()));
329   }
330 
331   @Test
test_serializeAndParse_works()332   public void test_serializeAndParse_works() throws Exception {
333     HybridTestVector testVector = HYBRID_TEST_VECTORS[0];
334     EciesPrivateKey key = (EciesPrivateKey) testVector.getPrivateKey();
335     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(key).withFixedId(1216).makePrimary();
336     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
337 
338     byte[] serializedHandle =
339         TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get());
340     KeysetHandle parsedHandle =
341         TinkProtoKeysetFormat.parseKeyset(serializedHandle, InsecureSecretKeyAccess.get());
342     assertThat(parsedHandle.equalsKeyset(handle)).isTrue();
343   }
344 
345   @Test
test_serializeAndParse_publicKey_works()346   public void test_serializeAndParse_publicKey_works() throws Exception {
347     HybridTestVector testVector = HYBRID_TEST_VECTORS[0];
348     EciesPrivateKey key = (EciesPrivateKey) testVector.getPrivateKey();
349     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(key).withFixedId(1216).makePrimary();
350     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build().getPublicKeysetHandle();
351 
352     byte[] serializedHandle = TinkProtoKeysetFormat.serializeKeysetWithoutSecret(handle);
353     KeysetHandle parsedHandle = TinkProtoKeysetFormat.parseKeysetWithoutSecret(serializedHandle);
354     assertThat(parsedHandle.equalsKeyset(handle)).isTrue();
355   }
356 
357   @Test
testKeyManagerRegistered()358   public void testKeyManagerRegistered() throws Exception {
359     assertThat(
360             KeyManagerRegistry.globalInstance()
361                 .getKeyManager(
362                     "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey",
363                     HybridDecrypt.class))
364         .isNotNull();
365     assertThat(
366             KeyManagerRegistry.globalInstance()
367                 .getKeyManager(
368                     "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey",
369                     HybridEncrypt.class))
370         .isNotNull();
371   }
372 }
373