• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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.KeysetHandle;
26 import com.google.crypto.tink.aead.AesGcmParameters;
27 import com.google.crypto.tink.aead.internal.AesCtrHmacAeadProtoSerialization;
28 import com.google.crypto.tink.aead.internal.AesGcmProtoSerialization;
29 import com.google.crypto.tink.config.internal.TinkFipsUtil;
30 import com.google.crypto.tink.daead.internal.AesSivProtoSerialization;
31 import com.google.crypto.tink.hybrid.EciesParameters.CurveType;
32 import com.google.crypto.tink.hybrid.EciesParameters.PointFormat;
33 import com.google.crypto.tink.hybrid.HpkeParameters.AeadId;
34 import com.google.crypto.tink.hybrid.HpkeParameters.KdfId;
35 import com.google.crypto.tink.hybrid.HpkeParameters.KemId;
36 import com.google.crypto.tink.hybrid.internal.EciesProtoSerialization;
37 import com.google.crypto.tink.hybrid.internal.testing.EciesAeadHkdfTestUtil;
38 import com.google.crypto.tink.hybrid.internal.testing.HpkeTestUtil;
39 import com.google.crypto.tink.hybrid.internal.testing.HybridTestVector;
40 import com.google.crypto.tink.internal.BigIntegerEncoding;
41 import com.google.crypto.tink.subtle.EllipticCurves;
42 import com.google.crypto.tink.subtle.EllipticCurves.PointFormatType;
43 import com.google.crypto.tink.subtle.Hex;
44 import com.google.crypto.tink.util.Bytes;
45 import com.google.crypto.tink.util.SecretBigInteger;
46 import com.google.crypto.tink.util.SecretBytes;
47 import java.security.GeneralSecurityException;
48 import java.security.KeyPair;
49 import java.security.interfaces.ECPrivateKey;
50 import java.security.interfaces.ECPublicKey;
51 import java.util.Arrays;
52 import java.util.stream.Stream;
53 import javax.annotation.Nullable;
54 import org.junit.Assume;
55 import org.junit.BeforeClass;
56 import org.junit.Test;
57 import org.junit.experimental.theories.DataPoints;
58 import org.junit.experimental.theories.FromDataPoints;
59 import org.junit.experimental.theories.Theories;
60 import org.junit.experimental.theories.Theory;
61 import org.junit.runner.RunWith;
62 
63 @RunWith(Theories.class)
64 public class HybridConfigurationV0Test {
65   @BeforeClass
setUp()66   public static void setUp() throws Exception {
67     EciesProtoSerialization.register();
68     AesGcmProtoSerialization.register();
69     AesCtrHmacAeadProtoSerialization.register();
70     AesSivProtoSerialization.register();
71 
72     HpkeProtoSerialization.register();
73   }
74 
75   @Test
config_throwsIfInFipsMode()76   public void config_throwsIfInFipsMode() throws Exception {
77     Assume.assumeTrue(TinkFipsUtil.useOnlyFips());
78 
79     assertThrows(GeneralSecurityException.class, HybridConfigurationV0::get);
80   }
81 
82   @Test
config_containsEciesAeadHkdfForHybridEncrypt()83   public void config_containsEciesAeadHkdfForHybridEncrypt() throws Exception {
84     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
85 
86     AesGcmParameters demParameters =
87         AesGcmParameters.builder()
88             .setIvSizeBytes(12)
89             .setKeySizeBytes(32)
90             .setTagSizeBytes(16)
91             .setVariant(AesGcmParameters.Variant.NO_PREFIX)
92             .build();
93     EciesParameters parameters =
94         EciesParameters.builder()
95             .setCurveType(CurveType.NIST_P384)
96             .setHashType(EciesParameters.HashType.SHA256)
97             .setNistCurvePointFormat(PointFormat.UNCOMPRESSED)
98             .setDemParameters(demParameters)
99             .setVariant(EciesParameters.Variant.TINK)
100             .build();
101     KeyPair keyPair = EllipticCurves.generateKeyPair(EllipticCurves.CurveType.NIST_P384);
102     EciesPublicKey publicKey =
103         EciesPublicKey.createForNistCurve(
104             parameters, ((ECPublicKey) keyPair.getPublic()).getW(), /* idRequirement= */ 42);
105     KeysetHandle keysetHandle =
106         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(publicKey).makePrimary()).build();
107 
108     assertThat(keysetHandle.getPrimitive(HybridConfigurationV0.get(), HybridEncrypt.class))
109         .isNotNull();
110   }
111 
112   @Test
config_containsEciesAeadHkdfForHybridDecrypt()113   public void config_containsEciesAeadHkdfForHybridDecrypt() throws Exception {
114     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
115 
116     AesGcmParameters demParameters =
117         AesGcmParameters.builder()
118             .setIvSizeBytes(12)
119             .setKeySizeBytes(32)
120             .setTagSizeBytes(16)
121             .setVariant(AesGcmParameters.Variant.NO_PREFIX)
122             .build();
123     EciesParameters parameters =
124         EciesParameters.builder()
125             .setCurveType(CurveType.NIST_P384)
126             .setHashType(EciesParameters.HashType.SHA256)
127             .setNistCurvePointFormat(PointFormat.UNCOMPRESSED)
128             .setDemParameters(demParameters)
129             .setVariant(EciesParameters.Variant.TINK)
130             .build();
131     KeyPair keyPair = EllipticCurves.generateKeyPair(EllipticCurves.CurveType.NIST_P384);
132     EciesPublicKey publicKey =
133         EciesPublicKey.createForNistCurve(
134             parameters, ((ECPublicKey) keyPair.getPublic()).getW(), /* idRequirement= */ 42);
135     EciesPrivateKey privateKey =
136         EciesPrivateKey.createForNistCurve(
137             publicKey,
138             SecretBigInteger.fromBigInteger(
139                 ((ECPrivateKey) keyPair.getPrivate()).getS(), InsecureSecretKeyAccess.get()));
140     KeysetHandle keysetHandle =
141         KeysetHandle.newBuilder()
142             .addEntry(KeysetHandle.importKey(privateKey).makePrimary())
143             .build();
144 
145     assertThat(keysetHandle.getPrimitive(HybridConfigurationV0.get(), HybridDecrypt.class))
146         .isNotNull();
147   }
148 
149   @Test
config_containsHpkeForHybridEncrypt()150   public void config_containsHpkeForHybridEncrypt() throws Exception {
151     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
152 
153     HpkeParameters parameters =
154         HpkeParameters.builder()
155             .setKemId(KemId.DHKEM_P384_HKDF_SHA384)
156             .setKdfId(KdfId.HKDF_SHA384)
157             .setAeadId(AeadId.AES_256_GCM)
158             .setVariant(HpkeParameters.Variant.TINK)
159             .build();
160     KeyPair keyPair = EllipticCurves.generateKeyPair(EllipticCurves.CurveType.NIST_P384);
161     Bytes publicKeyBytes =
162         Bytes.copyFrom(
163             EllipticCurves.pointEncode(
164                 EllipticCurves.CurveType.NIST_P384,
165                 PointFormatType.UNCOMPRESSED,
166                 ((ECPublicKey) keyPair.getPublic()).getW()));
167     HpkePublicKey publicKey = HpkePublicKey.create(parameters, publicKeyBytes, 42);
168     KeysetHandle keysetHandle =
169         KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(publicKey).makePrimary()).build();
170 
171     assertThat(keysetHandle.getPrimitive(HybridConfigurationV0.get(), HybridEncrypt.class))
172         .isNotNull();
173   }
174 
175   @Test
config_containsHpkeForHybridDecrypt()176   public void config_containsHpkeForHybridDecrypt() throws Exception {
177     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
178 
179     HpkeParameters parameters =
180         HpkeParameters.builder()
181             .setKemId(KemId.DHKEM_P384_HKDF_SHA384)
182             .setKdfId(KdfId.HKDF_SHA384)
183             .setAeadId(AeadId.AES_256_GCM)
184             .setVariant(HpkeParameters.Variant.TINK)
185             .build();
186     KeyPair keyPair = EllipticCurves.generateKeyPair(EllipticCurves.CurveType.NIST_P384);
187     Bytes publicKeyBytes =
188         Bytes.copyFrom(
189             EllipticCurves.pointEncode(
190                 EllipticCurves.CurveType.NIST_P384,
191                 PointFormatType.UNCOMPRESSED,
192                 ((ECPublicKey) keyPair.getPublic()).getW()));
193     HpkePublicKey publicKey = HpkePublicKey.create(parameters, publicKeyBytes, 42);
194     byte[] privateKeyBytes =
195         BigIntegerEncoding.toBigEndianBytesOfFixedLength(
196             ((ECPrivateKey) keyPair.getPrivate()).getS(), 48);
197     HpkePrivateKey privateKey =
198         HpkePrivateKey.create(
199             publicKey, SecretBytes.copyFrom(privateKeyBytes, InsecureSecretKeyAccess.get()));
200     KeysetHandle keysetHandle =
201         KeysetHandle.newBuilder()
202             .addEntry(KeysetHandle.importKey(privateKey).makePrimary())
203             .build();
204 
205     assertThat(keysetHandle.getPrimitive(HybridConfigurationV0.get(), HybridDecrypt.class))
206         .isNotNull();
207   }
208 
209   @Theory
decryptCiphertextWorks(@romDataPoints"hybridTests") HybridTestVector v)210   public void decryptCiphertextWorks(@FromDataPoints("hybridTests") HybridTestVector v)
211       throws Exception {
212     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
213 
214     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
215     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
216     if (id == null) {
217       entry.withRandomId();
218     } else {
219       entry.withFixedId(id);
220     }
221     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
222 
223     HybridDecrypt hybridDecrypt =
224         handle.getPrimitive(HybridConfigurationV0.get(), HybridDecrypt.class);
225     byte[] plaintext = hybridDecrypt.decrypt(v.getCiphertext(), v.getContextInfo());
226 
227     assertThat(Hex.encode(plaintext)).isEqualTo(Hex.encode(v.getPlaintext()));
228   }
229 
230   @Theory
decryptWrongContextInfoThrows(@romDataPoints"hybridTests") HybridTestVector v)231   public void decryptWrongContextInfoThrows(@FromDataPoints("hybridTests") HybridTestVector v)
232       throws Exception {
233     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
234 
235     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
236     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
237     if (id == null) {
238       entry.withRandomId();
239     } else {
240       entry.withFixedId(id);
241     }
242     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
243 
244     byte[] contextInfo = v.getContextInfo();
245     if (contextInfo.length > 0) {
246       contextInfo[0] ^= 1;
247     } else {
248       contextInfo = new byte[] {1};
249     }
250     // local variables referenced from a lambda expression must be final or effectively final
251     final byte[] contextInfoCopy = Arrays.copyOf(contextInfo, contextInfo.length);
252 
253     HybridDecrypt hybridDecrypt =
254         handle.getPrimitive(HybridConfigurationV0.get(), HybridDecrypt.class);
255 
256     assertThrows(
257         GeneralSecurityException.class,
258         () -> hybridDecrypt.decrypt(v.getCiphertext(), contextInfoCopy));
259   }
260 
261   @Theory
encryptThenDecryptMessageWorks(@romDataPoints"hybridTests") HybridTestVector v)262   public void encryptThenDecryptMessageWorks(@FromDataPoints("hybridTests") HybridTestVector v)
263       throws Exception {
264     Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
265 
266     KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(v.getPrivateKey()).makePrimary();
267     @Nullable Integer id = v.getPrivateKey().getIdRequirementOrNull();
268     if (id == null) {
269       entry.withRandomId();
270     } else {
271       entry.withFixedId(id);
272     }
273     KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();
274 
275     HybridDecrypt hybridDecrypt =
276         handle.getPrimitive(HybridConfigurationV0.get(), HybridDecrypt.class);
277     HybridEncrypt hybridEncrypt =
278         handle
279             .getPublicKeysetHandle()
280             .getPrimitive(HybridConfigurationV0.get(), HybridEncrypt.class);
281     byte[] ciphertext = hybridEncrypt.encrypt(v.getPlaintext(), v.getContextInfo());
282     byte[] plaintext = hybridDecrypt.decrypt(ciphertext, v.getContextInfo());
283 
284     assertThat(Hex.encode(plaintext)).isEqualTo(Hex.encode(v.getPlaintext()));
285   }
286 
287   @DataPoints("hybridTests")
288   public static final HybridTestVector[] hybridTestVectors =
289       Stream.concat(
290               Arrays.stream(HpkeTestUtil.createHpkeTestVectors()),
291               Arrays.stream(EciesAeadHkdfTestUtil.createEciesTestVectors()))
292           .toArray(HybridTestVector[]::new);
293 }
294