• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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;
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.aead.AeadConfig;
24 import com.google.crypto.tink.mac.MacConfig;
25 import com.google.crypto.tink.signature.SignatureConfig;
26 import com.google.crypto.tink.subtle.Hex;
27 import java.io.ByteArrayOutputStream;
28 import java.security.GeneralSecurityException;
29 import org.junit.BeforeClass;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.JUnit4;
33 
34 @RunWith(JUnit4.class)
35 public final class TinkJsonProtoKeysetFormatTest {
36 
37   @BeforeClass
setUp()38   public static void setUp() throws GeneralSecurityException {
39     MacConfig.register();
40     AeadConfig.register();
41     SignatureConfig.register();
42   }
43 
assertKeysetHandleAreEqual(KeysetHandle keysetHandle1, KeysetHandle keysetHandle2)44   private void assertKeysetHandleAreEqual(KeysetHandle keysetHandle1, KeysetHandle keysetHandle2)
45       throws Exception {
46     // This assertion is too strong, but it works here because we don't parse or serialize
47     // keydata.value fields.
48     assertThat(CleartextKeysetHandle.getKeyset(keysetHandle2))
49         .isEqualTo(CleartextKeysetHandle.getKeyset(keysetHandle1));
50   }
51 
generateKeyset()52   private KeysetHandle generateKeyset() throws GeneralSecurityException {
53     return KeysetHandle.newBuilder()
54         .addEntry(
55             KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG")
56                 .withRandomId()
57                 .makePrimary())
58         .addEntry(
59             KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG_RAW")
60                 .withRandomId())
61         .addEntry(
62             KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_256BITTAG")
63                 .withRandomId()
64                 .setStatus(KeyStatus.DESTROYED))
65         .addEntry(
66             KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_256BITTAG_RAW")
67                 .withRandomId()
68                 .setStatus(KeyStatus.DISABLED))
69         .addEntry(KeysetHandle.generateEntryFromParametersName("AES256_CMAC").withRandomId())
70         .build();
71   }
72 
generatePublicKeyset()73   private KeysetHandle generatePublicKeyset() throws GeneralSecurityException {
74     return KeysetHandle.newBuilder()
75         .addEntry(
76             KeysetHandle.generateEntryFromParametersName("ECDSA_P256_RAW")
77                 .withRandomId()
78                 .setStatus(KeyStatus.DISABLED))
79         .addEntry(
80             KeysetHandle.generateEntryFromParametersName("ECDSA_P256").withRandomId().makePrimary())
81         .addEntry(
82             KeysetHandle.generateEntryFromParametersName("ECDSA_P521")
83                 .withRandomId()
84                 .setStatus(KeyStatus.DESTROYED))
85         .build()
86         .getPublicKeysetHandle();
87   }
88 
generateAead()89   private Aead generateAead() throws GeneralSecurityException {
90     KeysetHandle handle =
91         KeysetHandle.newBuilder()
92             .addEntry(
93                 KeysetHandle.generateEntryFromParametersName("AES128_CTR_HMAC_SHA256")
94                     .withRandomId()
95                     .makePrimary())
96             .build();
97     return handle.getPrimitive(RegistryConfiguration.get(), Aead.class);
98   }
99 
100   @Test
serializeAndParse_successWithSameKeyset()101   public void serializeAndParse_successWithSameKeyset() throws Exception {
102     KeysetHandle keysetHandle = generateKeyset();
103 
104     String serializedKeyset =
105         TinkJsonProtoKeysetFormat.serializeKeyset(keysetHandle, InsecureSecretKeyAccess.get());
106     KeysetHandle parseKeysetHandle =
107         TinkJsonProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get());
108 
109     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
110   }
111 
112   @Test
serializeKeyset_withoutInsecureSecretKeyAccess_fails()113   public void serializeKeyset_withoutInsecureSecretKeyAccess_fails() throws Exception {
114     KeysetHandle keysetHandle = generateKeyset();
115 
116     assertThrows(
117         NullPointerException.class,
118         () -> TinkJsonProtoKeysetFormat.serializeKeyset(keysetHandle, null));
119   }
120 
121   @Test
parseKeyset_withoutInsecureSecretKeyAccess_fails()122   public void parseKeyset_withoutInsecureSecretKeyAccess_fails() throws Exception {
123     String serializedKeyset =
124         TinkJsonProtoKeysetFormat.serializeKeyset(generateKeyset(), InsecureSecretKeyAccess.get());
125 
126     assertThrows(
127         NullPointerException.class,
128         () -> TinkJsonProtoKeysetFormat.parseKeyset(serializedKeyset, null));
129   }
130 
131   @Test
parseInvalidSerializedKeyset_fails()132   public void parseInvalidSerializedKeyset_fails() throws Exception {
133     String invalidSerializedKeyset = "invalid";
134     assertThrows(
135         GeneralSecurityException.class,
136         () ->
137             TinkJsonProtoKeysetFormat.parseKeyset(
138                 invalidSerializedKeyset, InsecureSecretKeyAccess.get()));
139   }
140 
141   @Test
serializeEncryptedAndParseEncrypted_successWithSameKeyset()142   public void serializeEncryptedAndParseEncrypted_successWithSameKeyset() throws Exception {
143     Aead keyEncryptionAead = generateAead();
144     KeysetHandle keysetHandle = generateKeyset();
145     byte[] associatedData = "associatedData".getBytes(UTF_8);
146 
147     String serializedKeyset =
148         TinkJsonProtoKeysetFormat.serializeEncryptedKeyset(
149             keysetHandle, keyEncryptionAead, associatedData);
150     KeysetHandle parseKeysetHandle =
151         TinkJsonProtoKeysetFormat.parseEncryptedKeyset(
152             serializedKeyset, keyEncryptionAead, associatedData);
153 
154     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
155   }
156 
157   @Test
parseEncryptedKeysetWithInvalidKey_fails()158   public void parseEncryptedKeysetWithInvalidKey_fails() throws Exception {
159     Aead keyEncryptionAead = generateAead();
160     Aead invalidKeyEncryptionAead = generateAead();
161     KeysetHandle keysetHandle = generateKeyset();
162     byte[] associatedData = "associatedData".getBytes(UTF_8);
163 
164     String serializedKeyset =
165         TinkJsonProtoKeysetFormat.serializeEncryptedKeyset(
166             keysetHandle, keyEncryptionAead, associatedData);
167 
168     assertThrows(
169         GeneralSecurityException.class,
170         () ->
171             TinkJsonProtoKeysetFormat.parseEncryptedKeyset(
172                 serializedKeyset, invalidKeyEncryptionAead, associatedData));
173   }
174 
175   @Test
parseEncryptedKeysetWithInvalidAssociatedData_fails()176   public void parseEncryptedKeysetWithInvalidAssociatedData_fails() throws Exception {
177     Aead keyEncryptionAead = generateAead();
178     KeysetHandle keysetHandle = generateKeyset();
179 
180     String serializedKeyset =
181         TinkJsonProtoKeysetFormat.serializeEncryptedKeyset(
182             keysetHandle, keyEncryptionAead, "associatedData".getBytes(UTF_8));
183 
184     assertThrows(
185         GeneralSecurityException.class,
186         () ->
187             TinkJsonProtoKeysetFormat.parseEncryptedKeyset(
188                 serializedKeyset, keyEncryptionAead, "invalidAssociatedData".getBytes(UTF_8)));
189   }
190 
191   @Test
serializeAndParseWithoutSecret_successWithSameKeyset()192   public void serializeAndParseWithoutSecret_successWithSameKeyset() throws Exception {
193     KeysetHandle publicKeysetHandle = generatePublicKeyset();
194 
195     String serializedKeyset =
196         TinkJsonProtoKeysetFormat.serializeKeysetWithoutSecret(publicKeysetHandle);
197     KeysetHandle parsePublicKeysetHandle =
198         TinkJsonProtoKeysetFormat.parseKeysetWithoutSecret(serializedKeyset);
199 
200     assertKeysetHandleAreEqual(publicKeysetHandle, parsePublicKeysetHandle);
201   }
202 
203   @Test
serializeWithoutSecret_keysetWithSecretKeys_fails()204   public void serializeWithoutSecret_keysetWithSecretKeys_fails() throws Exception {
205     KeysetHandle secretKeysetHandle = generateKeyset();
206 
207     assertThrows(
208         GeneralSecurityException.class,
209         () ->
210             TinkJsonProtoKeysetFormat.serializeKeysetWithoutSecret(secretKeysetHandle));
211   }
212 
213   @Test
parseWithoutSecret_keysetWithSecretKeys_fails()214   public void parseWithoutSecret_keysetWithSecretKeys_fails() throws Exception {
215     KeysetHandle secretKeysetHandle = generateKeyset();
216     String serializedSecretKeyset =
217         TinkJsonProtoKeysetFormat.serializeKeyset(
218             secretKeysetHandle, InsecureSecretKeyAccess.get());
219 
220     assertThrows(
221         GeneralSecurityException.class,
222         () ->
223             TinkJsonProtoKeysetFormat.parseKeysetWithoutSecret(serializedSecretKeyset));
224   }
225 
226   @Test
parseWithoutSecretInvalidSerializedKeyset_fails()227   public void parseWithoutSecretInvalidSerializedKeyset_fails() throws Exception {
228     String invalidSerializedKeyset = "invalid";
229     assertThrows(
230         GeneralSecurityException.class,
231         () -> TinkJsonProtoKeysetFormat.parseKeysetWithoutSecret(invalidSerializedKeyset));
232   }
233 
234   @Test
serializeKeyset_worksWithCleartextKeysetHandleReadAndJsonKeysetReader()235   public void serializeKeyset_worksWithCleartextKeysetHandleReadAndJsonKeysetReader()
236       throws Exception {
237     KeysetHandle keysetHandle = generateKeyset();
238 
239     String serializedKeyset =
240         TinkJsonProtoKeysetFormat.serializeKeyset(keysetHandle, InsecureSecretKeyAccess.get());
241 
242     KeysetHandle parseKeysetHandle =
243         CleartextKeysetHandle.read(JsonKeysetReader.withString(serializedKeyset));
244 
245     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
246   }
247 
248   @Test
parseKeyset_worksWithCleartextKeysetHandleWriteAndJsonKeysetWriter()249   public void parseKeyset_worksWithCleartextKeysetHandleWriteAndJsonKeysetWriter()
250       throws Exception {
251     KeysetHandle keysetHandle = generateKeyset();
252 
253     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
254     CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
255     String serializedKeyset = new String(outputStream.toByteArray(), UTF_8);
256 
257     KeysetHandle parseKeysetHandle =
258         TinkJsonProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get());
259 
260     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
261   }
262 
263   @Test
serializeKeysetWithoutSecret_worksWithKeysetHandleReadNoSecretAndJsonKeysetReader()264   public void serializeKeysetWithoutSecret_worksWithKeysetHandleReadNoSecretAndJsonKeysetReader()
265       throws Exception {
266     KeysetHandle publicKeysetHandle = generatePublicKeyset();
267 
268     String serializedKeyset =
269         TinkJsonProtoKeysetFormat.serializeKeysetWithoutSecret(publicKeysetHandle);
270 
271     KeysetHandle parsePublicKeysetHandle =
272         KeysetHandle.readNoSecret(JsonKeysetReader.withString(serializedKeyset));
273 
274     assertKeysetHandleAreEqual(publicKeysetHandle, parsePublicKeysetHandle);
275   }
276 
277   @Test
parseKeysetWithoutSecret_worksWithKeysetHandleWriteNoSecretAndJsonKeysetWriter()278   public void parseKeysetWithoutSecret_worksWithKeysetHandleWriteNoSecretAndJsonKeysetWriter()
279       throws Exception {
280     KeysetHandle publicKeysetHandle = generatePublicKeyset();
281 
282     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
283     publicKeysetHandle.writeNoSecret(JsonKeysetWriter.withOutputStream(outputStream));
284     String serializedKeyset = new String(outputStream.toByteArray(), UTF_8);
285 
286     KeysetHandle parsePublicKeysetHandle =
287         TinkJsonProtoKeysetFormat.parseKeysetWithoutSecret(serializedKeyset);
288 
289     assertKeysetHandleAreEqual(publicKeysetHandle, parsePublicKeysetHandle);
290   }
291 
292   @Test
serializeEncrypted_worksWithKeysetHandleReadWithAssociatedDataAndJsonKeysetReader()293   public void serializeEncrypted_worksWithKeysetHandleReadWithAssociatedDataAndJsonKeysetReader()
294       throws Exception {
295     Aead keyEncryptionAead = generateAead();
296     KeysetHandle keysetHandle = generateKeyset();
297     byte[] associatedData = "associatedData".getBytes(UTF_8);
298 
299     String serializedKeyset =
300         TinkJsonProtoKeysetFormat.serializeEncryptedKeyset(
301             keysetHandle, keyEncryptionAead, associatedData);
302 
303     KeysetHandle parseKeysetHandle =
304         KeysetHandle.readWithAssociatedData(
305             JsonKeysetReader.withString(serializedKeyset), keyEncryptionAead, associatedData);
306 
307     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
308   }
309 
310   @Test
parseEncrypted_worksWithKeysetHandleWriteWithAssociatedDataAndJsonKeysetWriter()311   public void parseEncrypted_worksWithKeysetHandleWriteWithAssociatedDataAndJsonKeysetWriter()
312       throws Exception {
313     Aead keyEncryptionAead = generateAead();
314     KeysetHandle keysetHandle = generateKeyset();
315     byte[] associatedData = "associatedData".getBytes(UTF_8);
316 
317     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
318     keysetHandle.writeWithAssociatedData(
319         JsonKeysetWriter.withOutputStream(outputStream), keyEncryptionAead, associatedData);
320     String serializedKeyset = new String(outputStream.toByteArray(), UTF_8);
321 
322     KeysetHandle parseKeysetHandle =
323         TinkJsonProtoKeysetFormat.parseEncryptedKeyset(
324             serializedKeyset, keyEncryptionAead, associatedData);
325 
326     assertKeysetHandleAreEqual(keysetHandle, parseKeysetHandle);
327   }
328 
329   @Test
parseKeysetFromTestVector()330   public void parseKeysetFromTestVector()
331       throws Exception {
332     // The same key as in JsonKeysetReaderTest.
333     String serializedKeyset =
334         "{"
335             + "\"primaryKeyId\": 547623039,"
336             + "\"key\": [{"
337             + "\"keyData\": {"
338             + "\"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
339             + "\"keyMaterialType\": \"SYMMETRIC\","
340             + "\"value\": \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac+bwFOGSkENGmU+1EYgocg==\""
341             + "},"
342             + "\"outputPrefixType\": \"TINK\","
343             + "\"keyId\": 547623039,"
344             + "\"status\": \"ENABLED\""
345             + "}]}";
346     KeysetHandle handle =
347         TinkJsonProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get());
348     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
349     mac.verifyMac(Hex.decode("0120a4107f3549e4fb3137415a63f5c8a0524f8ca7"), "data".getBytes(UTF_8));
350   }
351 
352   @Test
parseEncryptedKeysetFromTestVector()353   public void parseEncryptedKeysetFromTestVector() throws Exception {
354     // This is the same test vector as in KeysetHandleTest.
355     // An AEAD key, with which we encrypted the mac keyset below.
356     byte[] serializedKeysetEncryptionKeyset =
357         Hex.decode(
358             "08b891f5a20412580a4c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e6372797"
359                 + "0746f2e74696e6b2e4165734561784b65791216120208101a10e5d7d0cdd649e81e7952260689b2"
360                 + "e1971801100118b891f5a2042001");
361     KeysetHandle keysetEncryptionHandle = TinkProtoKeysetFormat.parseKeyset(
362         serializedKeysetEncryptionKeyset, InsecureSecretKeyAccess.get());
363     Aead keysetEncryptionAead =
364         keysetEncryptionHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
365 
366     // A keyset that contains one HMAC key, encrypted with the above, using associatedData
367     String encryptedKeyset =
368         "{\"encryptedKeyset\":"
369             + "\"AURdSLhZcFEgMBptDyi4/D8hL3h+Iz7ICgLrdeVRH26Fi3uSeewFoFA5cV5wfNueme3/BBR60yJ4hGpQ"
370             + "p+/248ZIgfuWyfmAGZ4dmYnYC1qd/IWkZZfVr3aOsx4j4kFZHkkvA+XIZUh/INbdPsMUNJy9cmu6s8osdH"
371             + "zu0XzP2ltWUowbr0fLQJwy92eAvU6gv91k6Tc=\","
372             + "\"keysetInfo\":{\"primaryKeyId\":547623039,\"keyInfo\":[{\"typeUrl\":"
373             + "\"type.googleapis.com/google.crypto.tink.HmacKey\",\"status\":\"ENABLED\","
374             + "\"keyId\":547623039,\"outputPrefixType\":\"TINK\"}]}}";
375     byte[] associatedData = Hex.decode("abcdef330012");
376 
377     KeysetHandle handle =
378         TinkJsonProtoKeysetFormat.parseEncryptedKeyset(
379             encryptedKeyset, keysetEncryptionAead, associatedData);
380 
381     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
382     byte[] data = "data".getBytes(UTF_8);
383     byte[] tag = Hex.decode("0120a4107f3549e4fb3137415a63f5c8a0524f8ca7");
384     mac.verifyMac(tag, data);
385   }
386 }
387