1 // Copyright 2021 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.internal;
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.proto.HpkeAead;
25 import com.google.crypto.tink.proto.HpkeKdf;
26 import com.google.crypto.tink.proto.HpkeKem;
27 import com.google.crypto.tink.proto.HpkeParams;
28 import com.google.crypto.tink.proto.HpkePrivateKey;
29 import com.google.crypto.tink.proto.HpkePublicKey;
30 import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
31 import com.google.crypto.tink.subtle.Random;
32 import com.google.crypto.tink.subtle.X25519;
33 import com.google.protobuf.ByteString;
34 import java.security.GeneralSecurityException;
35 import org.junit.Before;
36 import org.junit.BeforeClass;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 /** Unit tests for {@link HpkePublicKeyManager}. */
42 @RunWith(JUnit4.class)
43 public final class HpkePublicKeyManagerTest {
44   private static byte[] privateKeyBytes;
45   private static byte[] publicKeyBytes;
46 
47   private HpkePublicKeyManager keyManager;
48 
49   @BeforeClass
generateKeyMaterial()50   public static void generateKeyMaterial() throws GeneralSecurityException {
51     privateKeyBytes = X25519.generatePrivateKey();
52     publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
53   }
54 
55   @Before
setUp()56   public void setUp() {
57     keyManager = new HpkePublicKeyManager();
58   }
59 
createValidHpkeParams()60   private HpkeParams createValidHpkeParams() {
61     return HpkeParams.newBuilder()
62         .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256)
63         .setKdf(HpkeKdf.HKDF_SHA256)
64         .setAead(HpkeAead.AES_256_GCM)
65         .build();
66   }
67 
createHpkePublicKey(HpkeParams params)68   private HpkePublicKey createHpkePublicKey(HpkeParams params) {
69     return HpkePublicKey.newBuilder()
70         .setVersion(0)
71         .setPublicKey(ByteString.copyFrom(publicKeyBytes))
72         .setParams(params)
73         .build();
74   }
75 
createValidHpkePublicKey()76   private HpkePublicKey createValidHpkePublicKey() {
77     return createHpkePublicKey(createValidHpkeParams());
78   }
79 
80   @Test
basics()81   public void basics() throws Exception {
82     assertThat(keyManager.getKeyType())
83         .isEqualTo("type.googleapis.com/google.crypto.tink.HpkePublicKey");
84     assertThat(keyManager.getVersion()).isEqualTo(0);
85     assertThat(keyManager.keyMaterialType()).isEqualTo(KeyMaterialType.ASYMMETRIC_PUBLIC);
86   }
87 
88   @Test
validateKey_succeedsWithValidKey()89   public void validateKey_succeedsWithValidKey() throws Exception {
90     keyManager.validateKey(createValidHpkePublicKey());
91   }
92 
93   @Test
validateKey_failsWithEmptyKey()94   public void validateKey_failsWithEmptyKey() throws Exception {
95     assertThrows(
96         GeneralSecurityException.class,
97         () -> keyManager.validateKey(HpkePublicKey.getDefaultInstance()));
98   }
99 
100   @Test
validateKey_failsWithWrongVersion()101   public void validateKey_failsWithWrongVersion() throws Exception {
102     HpkePublicKey wrongVersionKey =
103         HpkePublicKey.newBuilder(createValidHpkePublicKey()).setVersion(1).build();
104     assertThrows(GeneralSecurityException.class, () -> keyManager.validateKey(wrongVersionKey));
105   }
106 
107   @Test
validateKey_failsWithMissingParams()108   public void validateKey_failsWithMissingParams() throws Exception {
109     HpkePublicKey missingParamsKey =
110         HpkePublicKey.newBuilder(createValidHpkePublicKey()).clearParams().build();
111     assertThrows(GeneralSecurityException.class, () -> keyManager.validateKey(missingParamsKey));
112   }
113 
114   @Test
validateKey_failsWithInvalidKem()115   public void validateKey_failsWithInvalidKem() throws Exception {
116     HpkeParams invalidKemParams =
117         HpkeParams.newBuilder()
118             .setKem(HpkeKem.KEM_UNKNOWN)
119             .setKdf(HpkeKdf.HKDF_SHA256)
120             .setAead(HpkeAead.AES_256_GCM)
121             .build();
122     assertThrows(
123         GeneralSecurityException.class,
124         () -> keyManager.validateKey(createHpkePublicKey(invalidKemParams)));
125   }
126 
127   @Test
validateKey_failsWithInvalidKdf()128   public void validateKey_failsWithInvalidKdf() throws Exception {
129     HpkeParams invalidKdfParams =
130         HpkeParams.newBuilder()
131             .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256)
132             .setKdf(HpkeKdf.KDF_UNKNOWN)
133             .setAead(HpkeAead.AES_256_GCM)
134             .build();
135     assertThrows(
136         GeneralSecurityException.class,
137         () -> keyManager.validateKey(createHpkePublicKey(invalidKdfParams)));
138   }
139 
140   @Test
validateKey_failsWithInvalidAead()141   public void validateKey_failsWithInvalidAead() throws Exception {
142     HpkeParams invalidAeadParams =
143         HpkeParams.newBuilder()
144             .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256)
145             .setKdf(HpkeKdf.HKDF_SHA256)
146             .setAead(HpkeAead.AEAD_UNKNOWN)
147             .build();
148     assertThrows(
149         GeneralSecurityException.class,
150         () -> keyManager.validateKey(createHpkePublicKey(invalidAeadParams)));
151   }
152 
153   @Test
createPrimitive()154   public void createPrimitive() throws Exception {
155     HpkePublicKey publicKey = createValidHpkePublicKey();
156     HpkePrivateKey privateKey =
157         HpkePrivateKey.newBuilder()
158             .setVersion(keyManager.getVersion())
159             .setPublicKey(publicKey)
160             .setPrivateKey(ByteString.copyFrom(privateKeyBytes))
161             .build();
162     HybridEncrypt hybridEncrypt = keyManager.getPrimitive(publicKey, HybridEncrypt.class);
163     HybridDecrypt hybridDecrypt = HpkeDecrypt.createHpkeDecrypt(privateKey);
164 
165     byte[] input = Random.randBytes(200);
166     byte[] contextInfo = Random.randBytes(100);
167     byte[] ciphertext = hybridEncrypt.encrypt(input, contextInfo);
168     byte[] plaintext = hybridDecrypt.decrypt(ciphertext, contextInfo);
169 
170     assertThat(plaintext).isEqualTo(input);
171   }
172 }
173