• 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 java.nio.charset.StandardCharsets.UTF_8;
21 import static org.junit.Assert.assertThrows;
22 
23 import com.google.crypto.tink.HybridDecrypt;
24 import com.google.crypto.tink.HybridEncrypt;
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.hybrid.internal.HpkeDecrypt;
29 import com.google.crypto.tink.internal.MonitoringAnnotations;
30 import com.google.crypto.tink.internal.MutableMonitoringRegistry;
31 import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
32 import com.google.crypto.tink.internal.PrimitiveConstructor;
33 import com.google.crypto.tink.internal.PrimitiveRegistry;
34 import com.google.crypto.tink.internal.testing.FakeMonitoringClient;
35 import com.google.crypto.tink.proto.Keyset;
36 import com.google.protobuf.ExtensionRegistryLite;
37 import java.security.GeneralSecurityException;
38 import java.util.List;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 
44 /** Tests for HybridEncryptWrapper. */
45 @RunWith(JUnit4.class)
46 public class HybridEncryptWrapperTest {
47   @Before
setUp()48   public void setUp() throws Exception {
49     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
50     HybridConfig.register();
51   }
52 
53   @Test
encryptNoPrefix_works()54   public void encryptNoPrefix_works() throws Exception {
55     HpkeParameters parameters =
56         HpkeParameters.builder()
57             .setVariant(HpkeParameters.Variant.NO_PREFIX)
58             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
59             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
60             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
61             .build();
62     KeysetHandle handle = KeysetHandle.generateNew(parameters);
63     HybridEncrypt encrypter =
64         handle
65             .getPublicKeysetHandle()
66             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
67     HybridDecrypt decrypter = HpkeDecrypt.create((HpkePrivateKey) handle.getPrimary().getKey());
68 
69     byte[] message = "data".getBytes(UTF_8);
70     byte[] context = "context".getBytes(UTF_8);
71     assertThat(decrypter.decrypt(encrypter.encrypt(message, context), context)).isEqualTo(message);
72   }
73 
74   @Test
encryptTinkPrefix_works()75   public void encryptTinkPrefix_works() throws Exception {
76     HpkeParameters parameters =
77         HpkeParameters.builder()
78             .setVariant(HpkeParameters.Variant.TINK)
79             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
80             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
81             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
82             .build();
83     KeysetHandle handle = KeysetHandle.generateNew(parameters);
84     HybridEncrypt encrypter =
85         handle
86             .getPublicKeysetHandle()
87             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
88     HybridDecrypt decrypter = HpkeDecrypt.create((HpkePrivateKey) handle.getPrimary().getKey());
89 
90     byte[] message = "data".getBytes(UTF_8);
91     byte[] context = "context".getBytes(UTF_8);
92     assertThat(decrypter.decrypt(encrypter.encrypt(message, context), context)).isEqualTo(message);
93   }
94 
95   @Test
encryptCrunchyPrefix_works()96   public void encryptCrunchyPrefix_works() throws Exception {
97     HpkeParameters parameters =
98         HpkeParameters.builder()
99             .setVariant(HpkeParameters.Variant.CRUNCHY)
100             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
101             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
102             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
103             .build();
104     KeysetHandle handle = KeysetHandle.generateNew(parameters);
105     HybridEncrypt encrypter =
106         handle
107             .getPublicKeysetHandle()
108             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
109     HybridDecrypt decrypter = HpkeDecrypt.create((HpkePrivateKey) handle.getPrimary().getKey());
110 
111     byte[] message = "data".getBytes(UTF_8);
112     byte[] context = "context".getBytes(UTF_8);
113     assertThat(decrypter.decrypt(encrypter.encrypt(message, context), context)).isEqualTo(message);
114   }
115 
116   @Test
encryptEncryptsWithPrimary()117   public void encryptEncryptsWithPrimary() throws Exception {
118     HpkeParameters parameters =
119         HpkeParameters.builder()
120             .setVariant(HpkeParameters.Variant.NO_PREFIX)
121             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
122             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
123             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
124             .build();
125     KeysetHandle handle =
126         KeysetHandle.newBuilder()
127             .addEntry(KeysetHandle.generateEntryFromParameters(parameters).withRandomId())
128             .addEntry(
129                 KeysetHandle.generateEntryFromParameters(parameters).withRandomId().makePrimary())
130             .addEntry(KeysetHandle.generateEntryFromParameters(parameters).withRandomId())
131             .build();
132 
133     HybridEncrypt encrypter =
134         handle
135             .getPublicKeysetHandle()
136             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
137     HybridDecrypt decrypter = HpkeDecrypt.create((HpkePrivateKey) handle.getPrimary().getKey());
138 
139     byte[] message = "data".getBytes(UTF_8);
140     byte[] context = "context".getBytes(UTF_8);
141     assertThat(decrypter.decrypt(encrypter.encrypt(message, context), context)).isEqualTo(message);
142   }
143 
144   @Test
getPrimitiveNoPrimary_throwsNullPointerException()145   public void getPrimitiveNoPrimary_throwsNullPointerException() throws Exception {
146     HpkeParameters parameters =
147         HpkeParameters.builder()
148             .setVariant(HpkeParameters.Variant.NO_PREFIX)
149             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
150             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
151             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
152             .build();
153 
154     KeysetHandle handle = KeysetHandle.generateNew(parameters).getPublicKeysetHandle();
155     Keyset keyset =
156         Keyset.parseFrom(
157             TinkProtoKeysetFormat.serializeKeysetWithoutSecret(handle),
158             ExtensionRegistryLite.getEmptyRegistry());
159     Keyset keysetWithoutPrimary = keyset.toBuilder().clearPrimaryKeyId().build();
160     // TODO(b/228140127) This should throw at primitive creation time.
161     assertThrows(
162         GeneralSecurityException.class,
163         () ->
164             TinkProtoKeysetFormat.parseKeysetWithoutSecret(keysetWithoutPrimary.toByteArray())
165                 .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class)
166                 .encrypt(new byte[0], new byte[0]));
167   }
168 
169   @Test
doesNotMonitorWithoutAnnotations()170   public void doesNotMonitorWithoutAnnotations() throws Exception {
171     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
172     MutableMonitoringRegistry.globalInstance().clear();
173     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
174     HybridEncryptWrapper.register();
175 
176     HpkeParameters parameters =
177         HpkeParameters.builder()
178             .setVariant(HpkeParameters.Variant.NO_PREFIX)
179             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
180             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
181             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
182             .build();
183 
184     KeysetHandle handle = KeysetHandle.generateNew(parameters);
185     HybridEncrypt encrypt =
186         handle
187             .getPublicKeysetHandle()
188             .getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
189 
190     byte[] message = "data".getBytes(UTF_8);
191     byte[] context = "context".getBytes(UTF_8);
192     Object unused = encrypt.encrypt(message, context);
193 
194     assertThat(fakeMonitoringClient.getLogEntries()).isEmpty();
195     assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty();
196   }
197 
198   @Test
monitorsWithAnnotations()199   public void monitorsWithAnnotations() throws Exception {
200     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
201     MutableMonitoringRegistry.globalInstance().clear();
202     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
203     HybridEncryptWrapper.register();
204 
205     MonitoringAnnotations annotations =
206         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
207 
208     HpkeParameters parameters =
209         HpkeParameters.builder()
210             .setVariant(HpkeParameters.Variant.NO_PREFIX)
211             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
212             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
213             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
214             .build();
215 
216     KeysetHandle privateHandle =
217         KeysetHandle.newBuilder()
218             .addEntry(
219                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(123).makePrimary())
220             .build();
221     KeysetHandle publicHandle =
222         KeysetHandle.newBuilder(privateHandle.getPublicKeysetHandle())
223             .setMonitoringAnnotations(annotations)
224             .build();
225 
226     HybridEncrypt encrypt =
227         publicHandle.getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
228 
229     byte[] message = "data".getBytes(UTF_8);
230     byte[] context = "context".getBytes(UTF_8);
231     Object unused = encrypt.encrypt(message, context);
232 
233     List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries();
234     assertThat(logEntries).hasSize(1);
235     FakeMonitoringClient.LogEntry encryptEntry = logEntries.get(0);
236     assertThat(encryptEntry.getKeyId()).isEqualTo(123);
237     assertThat(encryptEntry.getPrimitive()).isEqualTo("hybrid_encrypt");
238     assertThat(encryptEntry.getApi()).isEqualTo("encrypt");
239     assertThat(encryptEntry.getNumBytesAsInput()).isEqualTo(message.length);
240     assertThat(encryptEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
241   }
242 
243   @Test
monitorsWithAnnotations_multipleEncrypters_works()244   public void monitorsWithAnnotations_multipleEncrypters_works() throws Exception {
245     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
246     MutableMonitoringRegistry.globalInstance().clear();
247     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
248     HybridEncryptWrapper.register();
249 
250     MonitoringAnnotations annotations =
251         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
252 
253     HpkeParameters parameters =
254         HpkeParameters.builder()
255             .setVariant(HpkeParameters.Variant.NO_PREFIX)
256             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
257             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
258             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
259             .build();
260 
261     KeysetHandle privateHandle1 =
262         KeysetHandle.newBuilder()
263             .addEntry(
264                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(123).makePrimary())
265             .build();
266     KeysetHandle publicHandle1 =
267         KeysetHandle.newBuilder(privateHandle1.getPublicKeysetHandle())
268             .setMonitoringAnnotations(annotations)
269             .build();
270     KeysetHandle privateHandle2 =
271         KeysetHandle.newBuilder()
272             .addEntry(
273                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(456).makePrimary())
274             .build();
275     KeysetHandle publicHandle2 =
276         KeysetHandle.newBuilder(privateHandle2.getPublicKeysetHandle())
277             .setMonitoringAnnotations(annotations)
278             .build();
279 
280     HybridEncrypt encrypter1 =
281         publicHandle1.getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
282     HybridEncrypt encrypter2 =
283         publicHandle2.getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
284     byte[] message = "data".getBytes(UTF_8);
285     byte[] context = "context".getBytes(UTF_8);
286     Object unused = encrypter1.encrypt(message, context);
287     unused = encrypter2.encrypt(message, context);
288 
289     List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries();
290     assertThat(logEntries).hasSize(2);
291     FakeMonitoringClient.LogEntry encryptEntry1 = logEntries.get(0);
292     assertThat(encryptEntry1.getKeyId()).isEqualTo(123);
293     assertThat(encryptEntry1.getPrimitive()).isEqualTo("hybrid_encrypt");
294     assertThat(encryptEntry1.getApi()).isEqualTo("encrypt");
295     assertThat(encryptEntry1.getNumBytesAsInput()).isEqualTo(message.length);
296     assertThat(encryptEntry1.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
297     FakeMonitoringClient.LogEntry encryptEntry2 = logEntries.get(0);
298     assertThat(encryptEntry2.getKeyId()).isEqualTo(123);
299     assertThat(encryptEntry2.getPrimitive()).isEqualTo("hybrid_encrypt");
300     assertThat(encryptEntry2.getApi()).isEqualTo("encrypt");
301     assertThat(encryptEntry2.getNumBytesAsInput()).isEqualTo(message.length);
302     assertThat(encryptEntry2.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
303   }
304 
305   private static class AlwaysFailingHybridEncrypt implements HybridEncrypt {
AlwaysFailingHybridEncrypt(HpkePublicKey key)306     public AlwaysFailingHybridEncrypt(HpkePublicKey key) {}
307 
308     @Override
encrypt(byte[] message, byte[] contextInfo)309     public byte[] encrypt(byte[] message, byte[] contextInfo) throws GeneralSecurityException {
310       throw new GeneralSecurityException("AlwaysFailingHybridEncrypt always fails");
311     }
312   }
313 
314   @Test
testAlwaysFailingPublicKeySignWithAnnotations_hasMonitoring()315   public void testAlwaysFailingPublicKeySignWithAnnotations_hasMonitoring() throws Exception {
316     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
317     MutableMonitoringRegistry.globalInstance().clear();
318     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
319     MutablePrimitiveRegistry.resetGlobalInstanceTestOnly();
320     MutablePrimitiveRegistry.globalInstance()
321         .registerPrimitiveConstructor(
322             PrimitiveConstructor.create(
323                 AlwaysFailingHybridEncrypt::new, HpkePublicKey.class, HybridEncrypt.class));
324     HybridEncryptWrapper.register();
325 
326     MonitoringAnnotations annotations =
327         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
328     HpkeParameters parameters =
329         HpkeParameters.builder()
330             .setVariant(HpkeParameters.Variant.NO_PREFIX)
331             .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
332             .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
333             .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
334             .build();
335     KeysetHandle privateHandle =
336         KeysetHandle.newBuilder()
337             .addEntry(
338                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(123).makePrimary())
339             .build();
340     KeysetHandle publicHandle =
341         KeysetHandle.newBuilder(privateHandle.getPublicKeysetHandle())
342             .setMonitoringAnnotations(annotations)
343             .build();
344     HybridEncrypt encrypt =
345         publicHandle.getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class);
346 
347     byte[] data = "data".getBytes(UTF_8);
348     byte[] context = "context".getBytes(UTF_8);
349     assertThrows(GeneralSecurityException.class, () -> encrypt.encrypt(data, context));
350 
351     assertThat(fakeMonitoringClient.getLogEntries()).isEmpty();
352 
353     List<FakeMonitoringClient.LogFailureEntry> failures =
354         fakeMonitoringClient.getLogFailureEntries();
355     assertThat(failures).hasSize(1);
356     FakeMonitoringClient.LogFailureEntry signFailure = failures.get(0);
357     assertThat(signFailure.getPrimitive()).isEqualTo("hybrid_encrypt");
358     assertThat(signFailure.getApi()).isEqualTo("encrypt");
359     assertThat(signFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(123);
360     assertThat(signFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
361   }
362 
363   @Test
registerToInternalPrimitiveRegistry_works()364   public void registerToInternalPrimitiveRegistry_works() throws Exception {
365     PrimitiveRegistry.Builder initialBuilder = PrimitiveRegistry.builder();
366     PrimitiveRegistry initialRegistry = initialBuilder.build();
367     PrimitiveRegistry.Builder processedBuilder = PrimitiveRegistry.builder(initialRegistry);
368 
369     HybridEncryptWrapper.registerToInternalPrimitiveRegistry(processedBuilder);
370     PrimitiveRegistry processedRegistry = processedBuilder.build();
371 
372     assertThrows(
373         GeneralSecurityException.class,
374         () -> initialRegistry.getInputPrimitiveClass(HybridEncrypt.class));
375     assertThat(processedRegistry.getInputPrimitiveClass(HybridEncrypt.class))
376         .isEqualTo(HybridEncrypt.class);
377   }
378 }
379