• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 Google Inc.
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 org.junit.Assert.assertThrows;
21 import static org.junit.Assert.fail;
22 
23 import com.google.crypto.tink.aead.PredefinedAeadParameters;
24 import com.google.crypto.tink.config.TinkConfig;
25 import com.google.crypto.tink.mac.PredefinedMacParameters;
26 import com.google.crypto.tink.proto.Keyset;
27 import com.google.crypto.tink.subtle.Hex;
28 import com.google.crypto.tink.subtle.Random;
29 import com.google.gson.JsonArray;
30 import com.google.gson.JsonObject;
31 import com.google.gson.JsonParser;
32 import java.io.ByteArrayInputStream;
33 import java.io.ByteArrayOutputStream;
34 import java.io.IOException;
35 import java.nio.charset.Charset;
36 import java.security.GeneralSecurityException;
37 import org.junit.BeforeClass;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.junit.runners.JUnit4;
41 
42 /** Tests for JsonKeysetReader. */
43 @RunWith(JUnit4.class)
44 public class JsonKeysetReaderTest {
45   private static final Charset UTF_8 = Charset.forName("UTF-8");
46 
createJsonKeysetWithId(String id)47   private static String createJsonKeysetWithId(String id) {
48     return "{"
49         + ("\"primaryKeyId\": " + id + ",")
50         + "\"key\": [{"
51         + "\"keyData\": {"
52         + "\"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
53         + "\"keyMaterialType\": \"SYMMETRIC\","
54         + "\"value\": \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac+bwFOGSkENGmU+1EYgocg==\""
55         + "},"
56         + "\"outputPrefixType\": \"TINK\","
57         + ("\"keyId\": " + id + ",")
58         + "\"status\": \"ENABLED\""
59         + "}]}";
60   }
61 
62   private static final String JSON_KEYSET = createJsonKeysetWithId("547623039");
63 
64   private static final String URL_SAFE_JSON_KEYSET =
65       "{"
66           + "\"primaryKeyId\": 547623039,"
67           + "\"key\": [{"
68           + "\"keyData\": {"
69           + "\"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
70           + "\"keyMaterialType\": \"SYMMETRIC\","
71           + "\"value\": \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac-bwFOGSkENGmU-1EYgocg\""
72           + "},"
73           + "\"outputPrefixType\": \"TINK\","
74           + "\"keyId\": 547623039,"
75           + "\"status\": \"ENABLED\""
76           + "}]}";
77 
78   @BeforeClass
setUp()79   public static void setUp() throws GeneralSecurityException {
80     TinkConfig.register();
81   }
82 
assertKeysetHandle(KeysetHandle handle1, KeysetHandle handle2)83   private void assertKeysetHandle(KeysetHandle handle1, KeysetHandle handle2) throws Exception {
84     Mac mac1 = handle1.getPrimitive(RegistryConfiguration.get(), Mac.class);
85     Mac mac2 = handle2.getPrimitive(RegistryConfiguration.get(), Mac.class);
86     byte[] message = Random.randBytes(20);
87 
88     assertThat(handle2.getKeyset()).isEqualTo(handle1.getKeyset());
89     mac2.verifyMac(mac1.computeMac(message), message);
90   }
91 
92   @Test
testRead_singleKey_shouldWork()93   public void testRead_singleKey_shouldWork() throws Exception {
94     KeysetHandle handle1 = KeysetHandle.generateNew(PredefinedMacParameters.HMAC_SHA256_128BITTAG);
95     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
96     CleartextKeysetHandle.write(handle1, JsonKeysetWriter.withOutputStream(outputStream));
97     KeysetHandle handle2 =
98         CleartextKeysetHandle.read(
99             JsonKeysetReader.withInputStream(new ByteArrayInputStream(outputStream.toByteArray())));
100 
101     assertKeysetHandle(handle1, handle2);
102   }
103 
104   @Test
testRead_multipleKeys_shouldWork()105   public void testRead_multipleKeys_shouldWork() throws Exception {
106     KeysetHandle handle1 =
107         KeysetHandle.newBuilder()
108             .addEntry(
109                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG")
110                     .withRandomId()
111                     .makePrimary())
112             .addEntry(
113                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG")
114                     .withRandomId())
115             .addEntry(
116                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG")
117                     .withRandomId())
118             .build();
119     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
120     CleartextKeysetHandle.write(handle1, JsonKeysetWriter.withOutputStream(outputStream));
121     KeysetHandle handle2 =
122         CleartextKeysetHandle.read(
123             JsonKeysetReader.withInputStream(new ByteArrayInputStream(outputStream.toByteArray())));
124 
125     assertKeysetHandle(handle1, handle2);
126   }
127 
128   @Test
readTestKeysetVerifyTestTag()129   public void readTestKeysetVerifyTestTag() throws Exception {
130     KeysetHandle handle = CleartextKeysetHandle.read(JsonKeysetReader.withString(JSON_KEYSET));
131     byte[] data = "data".getBytes(UTF_8);
132     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
133     byte[] tag = Hex.decode("0120a4107f3549e4fb3137415a63f5c8a0524f8ca7");
134     mac.verifyMac(tag, data);
135   }
136 
137   @Test
readEncryptedTestKeysetVerifyTestTag()138   public void readEncryptedTestKeysetVerifyTestTag() throws Exception {
139     // This is the same test vector as in KeysetHandleTest.
140     // An AEAD key, with which we encrypted the mac keyset below.
141     byte[] serializedKeysetEncryptionKeyset =
142         Hex.decode(
143             "08b891f5a20412580a4c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e6372797"
144                 + "0746f2e74696e6b2e4165734561784b65791216120208101a10e5d7d0cdd649e81e7952260689b2"
145                 + "e1971801100118b891f5a2042001");
146     KeysetHandle keysetEncryptionHandle = TinkProtoKeysetFormat.parseKeyset(
147         serializedKeysetEncryptionKeyset, InsecureSecretKeyAccess.get());
148     Aead keysetEncryptionAead =
149         keysetEncryptionHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
150     byte[] associatedData = Hex.decode("abcdef330012");
151 
152     String encryptedKeyset =
153         "{\"encryptedKeyset\":"
154             + "\"AURdSLhZcFEgMBptDyi4/D8hL3h+Iz7ICgLrdeVRH26Fi3uSeewFoFA5cV5wfNueme3/BBR60yJ4hGpQ"
155             + "p+/248ZIgfuWyfmAGZ4dmYnYC1qd/IWkZZfVr3aOsx4j4kFZHkkvA+XIZUh/INbdPsMUNJy9cmu6s8osdH"
156             + "zu0XzP2ltWUowbr0fLQJwy92eAvU6gv91k6Tc=\","
157             + "\"keysetInfo\":{\"primaryKeyId\":547623039,\"keyInfo\":[{\"typeUrl\":"
158             + "\"type.googleapis.com/google.crypto.tink.HmacKey\",\"status\":\"ENABLED\","
159             + "\"keyId\":547623039,\"outputPrefixType\":\"TINK\"}]}}";
160 
161     KeysetHandle handle = KeysetHandle.readWithAssociatedData(
162         JsonKeysetReader.withString(encryptedKeyset), keysetEncryptionAead, associatedData);
163     byte[] data = "data".getBytes(UTF_8);
164     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
165     byte[] tag = Hex.decode("0120a4107f3549e4fb3137415a63f5c8a0524f8ca7");
166     mac.verifyMac(tag, data);
167   }
168 
169 
170   @Test
testRead_urlSafeKeyset_shouldWork()171   public void testRead_urlSafeKeyset_shouldWork() throws Exception {
172     KeysetHandle handle1 = CleartextKeysetHandle.read(JsonKeysetReader.withString(JSON_KEYSET));
173     KeysetHandle handle2 =
174         CleartextKeysetHandle.read(
175             JsonKeysetReader.withString(URL_SAFE_JSON_KEYSET).withUrlSafeBase64());
176 
177     assertKeysetHandle(handle1, handle2);
178   }
179 
180   @Test
testRead_missingKey_shouldThrowException()181   public void testRead_missingKey_shouldThrowException() throws Exception {
182     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
183     json.remove("key"); // remove key
184 
185     IOException e =
186         assertThrows(IOException.class, () -> JsonKeysetReader.withJsonObject(json).read());
187     assertThat(e.toString()).contains("invalid keyset");
188   }
189 
testReadInvalidKeyShouldThrowException(String name)190   private void testReadInvalidKeyShouldThrowException(String name) throws Exception {
191     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
192     JsonArray keys = json.get("key").getAsJsonArray();
193     JsonObject key = keys.get(0).getAsJsonObject();
194     key.remove(name);
195     keys.set(0, key);
196     json.add("key", keys);
197 
198     try {
199       JsonKeysetReader.withJsonObject(json).read();
200       fail("Expected IOException");
201     } catch (IOException e) {
202       assertThat(e.toString()).contains("invalid key");
203     }
204   }
205 
206   @Test
testRead_invalidKey_shouldThrowException()207   public void testRead_invalidKey_shouldThrowException() throws Exception {
208     testReadInvalidKeyShouldThrowException("keyData");
209     testReadInvalidKeyShouldThrowException("status");
210     testReadInvalidKeyShouldThrowException("keyId");
211     testReadInvalidKeyShouldThrowException("outputPrefixType");
212   }
213 
testRead_invalidKeyData_shouldThrowException(String name)214   private void testRead_invalidKeyData_shouldThrowException(String name) throws Exception {
215     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
216     JsonArray keys = json.get("key").getAsJsonArray();
217     JsonObject key = keys.get(0).getAsJsonObject();
218     JsonObject keyData = key.get("keyData").getAsJsonObject();
219     keyData.remove(name);
220     key.add("keyData", keyData);
221     keys.set(0, key);
222     json.add("key", keys);
223 
224     try {
225       JsonKeysetReader.withJsonObject(json).read();
226       fail("Expected IOException");
227     } catch (IOException e) {
228       assertThat(e.toString()).contains("invalid keyData");
229     }
230   }
231 
232   @Test
testRead_invalidKeyData_shouldThrowException()233   public void testRead_invalidKeyData_shouldThrowException() throws Exception {
234     testRead_invalidKeyData_shouldThrowException("typeUrl");
235     testRead_invalidKeyData_shouldThrowException("value");
236     testRead_invalidKeyData_shouldThrowException("keyMaterialType");
237   }
238 
239   @Test
testRead_invalidKeyMaterialType_shouldThrowException()240   public void testRead_invalidKeyMaterialType_shouldThrowException() throws Exception {
241     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
242     JsonArray keys = json.get("key").getAsJsonArray();
243     JsonObject key = keys.get(0).getAsJsonObject();
244     JsonObject keyData = key.get("keyData").getAsJsonObject();
245     keyData.addProperty("keyMaterialType", "invalid");
246     key.add("keyData", keyData);
247     keys.set(0, key);
248     json.add("key", keys);
249 
250     IOException e =
251         assertThrows(IOException.class, () -> JsonKeysetReader.withJsonObject(json).read());
252     assertThat(e.toString()).contains("unknown key material type");
253   }
254 
255   @Test
testRead_invalidStatus_shouldThrowException()256   public void testRead_invalidStatus_shouldThrowException() throws Exception {
257     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
258     JsonArray keys = json.get("key").getAsJsonArray();
259     JsonObject key = keys.get(0).getAsJsonObject();
260     key.addProperty("status", "invalid");
261     keys.set(0, key);
262     json.add("key", keys);
263 
264     IOException e =
265         assertThrows(IOException.class, () -> JsonKeysetReader.withJsonObject(json).read());
266     assertThat(e.toString()).contains("unknown status");
267   }
268 
269   @Test
testRead_invalidOutputPrefixType_shouldThrowException()270   public void testRead_invalidOutputPrefixType_shouldThrowException() throws Exception {
271     JsonObject json = JsonParser.parseString(JSON_KEYSET).getAsJsonObject();
272     JsonArray keys = json.get("key").getAsJsonArray();
273     JsonObject key = keys.get(0).getAsJsonObject();
274     key.addProperty("outputPrefixType", "invalid");
275     keys.set(0, key);
276     json.add("key", keys);
277 
278     IOException e =
279         assertThrows(IOException.class, () -> JsonKeysetReader.withJsonObject(json).read());
280     assertThat(e.toString()).contains("unknown output prefix type");
281   }
282 
283   @Test
testRead_jsonKeysetWriter_shouldWork()284   public void testRead_jsonKeysetWriter_shouldWork() throws Exception {
285     KeysetHandle handle1 = KeysetHandle.generateNew(PredefinedMacParameters.HMAC_SHA256_128BITTAG);
286     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
287     CleartextKeysetHandle.write(handle1, JsonKeysetWriter.withOutputStream(outputStream));
288     KeysetHandle handle2 =
289         CleartextKeysetHandle.read(JsonKeysetReader.withBytes(outputStream.toByteArray()));
290 
291     assertKeysetHandle(handle1, handle2);
292   }
293 
294   @Test
testRead_staticMethods_validKeyset_shouldWork()295   public void testRead_staticMethods_validKeyset_shouldWork() throws Exception {
296     KeysetHandle handle1 = CleartextKeysetHandle.read(JsonKeysetReader.withString(JSON_KEYSET));
297     KeysetHandle handle2 =
298         CleartextKeysetHandle.read(
299             JsonKeysetReader.withInputStream(
300                 new ByteArrayInputStream(JSON_KEYSET.getBytes(UTF_8))));
301     KeysetHandle handle3 =
302         CleartextKeysetHandle.read(JsonKeysetReader.withBytes(JSON_KEYSET.getBytes(UTF_8)));
303     KeysetHandle handle4 =
304         CleartextKeysetHandle.read(
305             JsonKeysetReader.withJsonObject(JsonParser.parseString(JSON_KEYSET).getAsJsonObject()));
306 
307     assertKeysetHandle(handle1, handle2);
308     assertKeysetHandle(handle1, handle3);
309     assertKeysetHandle(handle1, handle4);
310   }
311 
312   @Test
testReadEncrypted_singleKey_shouldWork()313   public void testReadEncrypted_singleKey_shouldWork() throws Exception {
314     Aead masterKey =
315         KeysetHandle.generateNew(PredefinedAeadParameters.AES128_EAX)
316             .getPrimitive(RegistryConfiguration.get(), Aead.class);
317     KeysetHandle handle1 = KeysetHandle.generateNew(PredefinedMacParameters.HMAC_SHA256_128BITTAG);
318     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
319     handle1.write(JsonKeysetWriter.withOutputStream(outputStream), masterKey);
320     KeysetHandle handle2 =
321         KeysetHandle.read(
322             JsonKeysetReader.withInputStream(new ByteArrayInputStream(outputStream.toByteArray())),
323             masterKey);
324 
325     assertKeysetHandle(handle1, handle2);
326   }
327 
328   @Test
testReadEncrypted_multipleKeys_shouldWork()329   public void testReadEncrypted_multipleKeys_shouldWork() throws Exception {
330     Aead keysetEncryptionAead =
331         KeysetHandle.generateNew(KeyTemplates.get("AES128_EAX"))
332             .getPrimitive(RegistryConfiguration.get(), Aead.class);
333     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
334     KeysetHandle handle1 =
335         KeysetHandle.newBuilder()
336             .addEntry(
337                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG")
338                     .withRandomId())
339             .addEntry(
340                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_128BITTAG_RAW")
341                     .withRandomId()
342                     .makePrimary())
343             .addEntry(
344                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_256BITTAG")
345                     .withRandomId())
346             .addEntry(
347                 KeysetHandle.generateEntryFromParametersName("HMAC_SHA256_256BITTAG_RAW")
348                     .withRandomId()
349                     .setStatus(KeyStatus.DESTROYED))
350             .addEntry(
351                 KeysetHandle.generateEntryFromParametersName("AES256_CMAC")
352                     .withRandomId()
353                     .setStatus(KeyStatus.DISABLED))
354             .build();
355     handle1.write(JsonKeysetWriter.withOutputStream(outputStream), keysetEncryptionAead);
356     KeysetHandle handle2 =
357         KeysetHandle.read(
358             JsonKeysetReader.withInputStream(new ByteArrayInputStream(outputStream.toByteArray())),
359             keysetEncryptionAead);
360 
361     assertKeysetHandle(handle1, handle2);
362   }
363 
364   @Test
testReadEncrypted_missingKeysetInfo_shouldSucceed()365   public void testReadEncrypted_missingKeysetInfo_shouldSucceed() throws Exception {
366     Aead keysetEncryptionAead =
367         KeysetHandle.generateNew(KeyTemplates.get("AES128_EAX"))
368             .getPrimitive(RegistryConfiguration.get(), Aead.class);
369     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
370     KeysetHandle handle1 = KeysetHandle.generateNew(KeyTemplates.get("HMAC_SHA256_128BITTAG"));
371 
372     // Generate a valid encrypted keyset in JSON format, and delete "keysetInfo".
373     handle1.write(JsonKeysetWriter.withOutputStream(outputStream), keysetEncryptionAead);
374     JsonObject jsonEncryptedKeyset =
375         JsonParser.parseString(new String(outputStream.toByteArray(), UTF_8)).getAsJsonObject();
376     jsonEncryptedKeyset.remove("keysetInfo");
377     String jsonEncryptedKeysetWithoutKeysetInfo = jsonEncryptedKeyset.toString();
378 
379     KeysetHandle handle2 =
380         KeysetHandle.read(
381             JsonKeysetReader.withString(jsonEncryptedKeysetWithoutKeysetInfo),
382             keysetEncryptionAead);
383 
384     assertKeysetHandle(handle1, handle2);
385   }
386 
387   @Test
testReadEncrypted_missingEncryptedKeyset_shouldThrowException()388   public void testReadEncrypted_missingEncryptedKeyset_shouldThrowException() throws Exception {
389     Aead masterKey =
390         KeysetHandle.generateNew(PredefinedAeadParameters.AES128_EAX)
391             .getPrimitive(RegistryConfiguration.get(), Aead.class);
392     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
393     KeysetHandle handle = KeysetHandle.generateNew(PredefinedMacParameters.HMAC_SHA256_128BITTAG);
394     handle.write(JsonKeysetWriter.withOutputStream(outputStream), masterKey);
395     JsonObject json =
396         JsonParser.parseString(new String(outputStream.toByteArray(), UTF_8)).getAsJsonObject();
397     json.remove("encryptedKeyset"); // remove key
398 
399     IOException e =
400         assertThrows(
401             IOException.class, () -> JsonKeysetReader.withJsonObject(json).readEncrypted());
402     assertThat(e.toString()).contains("invalid encrypted keyset");
403   }
404 
405   @Test
testReadEncrypted_jsonKeysetWriter_shouldWork()406   public void testReadEncrypted_jsonKeysetWriter_shouldWork() throws Exception {
407     Aead masterKey =
408         KeysetHandle.generateNew(PredefinedAeadParameters.AES128_EAX)
409             .getPrimitive(RegistryConfiguration.get(), Aead.class);
410     KeysetHandle handle1 = KeysetHandle.generateNew(PredefinedMacParameters.HMAC_SHA256_128BITTAG);
411     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
412     handle1.write(JsonKeysetWriter.withOutputStream(outputStream), masterKey);
413     KeysetHandle handle2 =
414         KeysetHandle.read(JsonKeysetReader.withBytes(outputStream.toByteArray()), masterKey);
415 
416     assertKeysetHandle(handle1, handle2);
417   }
418 
419   @Test
readKeyset_negativeKeyId_works()420   public void readKeyset_negativeKeyId_works() throws Exception {
421     String jsonKeysetString = createJsonKeysetWithId("-21");
422     Keyset keyset = JsonKeysetReader.withString(jsonKeysetString).read();
423     assertThat(keyset.getPrimaryKeyId()).isEqualTo(-21);
424   }
425 
426   @Test
readKeyset_convertsUnsignedUint32IntoSignedInt32()427   public void readKeyset_convertsUnsignedUint32IntoSignedInt32() throws Exception {
428     String jsonKeysetString = createJsonKeysetWithId("4294967275"); // 2^32 - 21
429     Keyset keyset = JsonKeysetReader.withString(jsonKeysetString).read();
430     assertThat(keyset.getPrimaryKeyId()).isEqualTo(-21);
431   }
432 
433   @Test
readKeyset_acceptsMaxUint32()434   public void readKeyset_acceptsMaxUint32() throws Exception {
435     String jsonKeysetString = createJsonKeysetWithId("4294967295"); // 2^32 - 1 = 0xffffffff
436     Keyset keyset = JsonKeysetReader.withString(jsonKeysetString).read();
437     assertThat(keyset.getPrimaryKeyId()).isEqualTo(-1);
438   }
439 
440   @Test
readKeyset_acceptsMinInt32()441   public void readKeyset_acceptsMinInt32() throws Exception {
442     String jsonKeysetString = createJsonKeysetWithId("-2147483648"); // - 2^31
443     Keyset keyset = JsonKeysetReader.withString(jsonKeysetString).read();
444     assertThat(keyset.getPrimaryKeyId()).isEqualTo(-2147483648);
445   }
446 
447   @Test
readKeyset_rejectsKeyIdLargerThanUint32()448   public void readKeyset_rejectsKeyIdLargerThanUint32() throws Exception {
449     String jsonKeysetString = createJsonKeysetWithId("4294967296"); // 2^32
450     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
451   }
452 
453   @Test
readKeyset_rejectsKeyIdLargerThanUint64()454   public void readKeyset_rejectsKeyIdLargerThanUint64() throws Exception {
455     String jsonKeysetString = createJsonKeysetWithId("18446744073709551658"); // 2^64 + 42
456     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
457   }
458 
459   @Test
readKeyset_rejectsKeyIdSmallerThanInt32()460   public void readKeyset_rejectsKeyIdSmallerThanInt32() throws Exception {
461     String jsonKeysetString = createJsonKeysetWithId("-2147483649"); // - 2^31 - 1
462     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
463   }
464 
465   @Test
testReadKeyset_keyIdWithComment_throws()466   public void testReadKeyset_keyIdWithComment_throws() throws Exception {
467     String jsonKeysetString = createJsonKeysetWithId("123 /* comment on key ID */");
468     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
469   }
470 
471   @Test
testReadKeyset_withDuplicatedMapKey_throws()472   public void testReadKeyset_withDuplicatedMapKey_throws() throws Exception {
473     String jsonKeysetString = "{"
474         + "\"primaryKeyId\": 123,"
475         + "\"key\": [{"
476         + "\"keyData\": {"
477         + "\"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
478         + "\"keyMaterialType\": \"SYMMETRIC\","
479         + "\"keyMaterialType\": \"SYMMETRIC\","
480         + "\"value\": \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac+bwFOGSkENGmU+1EYgocg==\""
481         + "},"
482         + "\"outputPrefixType\": \"TINK\","
483         + "\"keyId\": 123,"
484         + "\"status\": \"ENABLED\""
485         + "}]}";
486     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
487   }
488 
489   @Test
testReadKeyset_withInvalidCharacterInTypeUrl_throws()490   public void testReadKeyset_withInvalidCharacterInTypeUrl_throws() throws Exception {
491     String jsonKeysetString =
492         "{"
493             + "\"primaryKeyId\": 123,"
494             + "\"key\": [{"
495             + "\"keyData\": {"
496             + "\"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\\uD834\","
497             + "\"keyMaterialType\": \"SYMMETRIC\","
498             + "\"value\": \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac+bwFOGSkENGmU+1EYgocg==\""
499             + "},"
500             + "\"outputPrefixType\": \"TINK\","
501             + "\"keyId\": 123,"
502             + "\"status\": \"ENABLED\""
503             + "}]}";
504     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
505   }
506 
507   @Test
testReadKeyset_withoutQuotes_throws()508   public void testReadKeyset_withoutQuotes_throws() throws Exception {
509     String jsonKeysetString = "{"
510         + "primaryKeyId: 123,"
511         + "key:[{"
512         + "keyData:{"
513         + "typeUrl:\"type.googleapis.com/google.crypto.tink.HmacKey\","
514         + "keyMaterialType: SYMMETRIC,"
515         + "value: \"EgQIAxAQGiBYhMkitTWFVefTIBg6kpvac+bwFOGSkENGmU+1EYgocg==\""
516         + "},"
517         + "outputPrefixType:TINK,"
518         + "keyId:123,"
519         + "status:ENABLED"
520         + "}]}";
521     assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeysetString).read());
522   }
523 
524   @Test
testRead_validJsonKeyset_doesNotThrow()525   public void testRead_validJsonKeyset_doesNotThrow() throws Exception {
526     String jsonKeyset =
527         "{"
528             + "  \"primaryKeyId\": 42818733,"
529             + "  \"key\": ["
530             + "    {"
531             + "      \"keyData\": {"
532             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
533             + "        \"keyMaterialType\": \"SYMMETRIC\","
534             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
535             + "      },"
536             + "      \"outputPrefixType\": \"TINK\","
537             + "      \"keyId\": 42818733,"
538             + "      \"status\": \"ENABLED\""
539             + "    }]"
540             + "}";
541     assertThat(JsonKeysetReader.withString(jsonKeyset).read()).isNotNull();
542   }
543 
544   @Test
testRead_primaryKeyIdIsAString_throws()545   public void testRead_primaryKeyIdIsAString_throws() throws Exception {
546     String jsonKeyset =
547         "{"
548             + "  \"primaryKeyId\": \"42818733\","
549             + "  \"key\": ["
550             + "    {"
551             + "      \"keyData\": {"
552             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
553             + "        \"keyMaterialType\": \"SYMMETRIC\","
554             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
555             + "      },"
556             + "      \"outputPrefixType\": \"TINK\","
557             + "      \"keyId\": 42818733,"
558             + "      \"status\": \"ENABLED\""
559             + "    }]"
560             + "}";
561     IOException e =
562         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
563     assertThat(e).hasMessageThat().contains("invalid key id");
564   }
565 
566   @Test
testRead_primaryKeyIdIsNotAnIntegerOrAString_throws()567   public void testRead_primaryKeyIdIsNotAnIntegerOrAString_throws() throws Exception {
568     String jsonKeyset =
569         "{"
570             + "  \"primaryKeyId\": true,"
571             + "  \"key\": ["
572             + "    {"
573             + "      \"keyData\": {"
574             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
575             + "        \"keyMaterialType\": \"SYMMETRIC\","
576             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
577             + "      },"
578             + "      \"outputPrefixType\": \"TINK\","
579             + "      \"keyId\": 42818733,"
580             + "      \"status\": \"ENABLED\""
581             + "    }]"
582             + "}";
583     IOException e =
584         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
585     assertThat(e).hasMessageThat().contains("invalid key id");
586   }
587 
588   @Test
testRead_keyIsNotAnArray_throws()589   public void testRead_keyIsNotAnArray_throws() throws Exception {
590     String jsonKeyset =
591         "{"
592             + "  \"primaryKeyId\": 42818733,"
593             + "  \"key\": "
594             + "    {"
595             + "      \"keyData\": {"
596             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
597             + "        \"keyMaterialType\": \"SYMMETRIC\","
598             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
599             + "      },"
600             + "      \"outputPrefixType\": \"TINK\","
601             + "      \"keyId\": 42818733,"
602             + "      \"status\": \"ENABLED\""
603             + "    }"
604             + "}";
605     IOException e =
606         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
607     assertThat(e).hasMessageThat().contains("key must be an array");
608   }
609 
610   @Test
testRead_keyEntryIsNotAnObject_throws()611   public void testRead_keyEntryIsNotAnObject_throws() throws Exception {
612     String jsonKeyset = "{\"primaryKeyId\":42818733,\"key\":[true]}";
613     IOException e =
614         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
615     assertThat(e)
616         .hasMessageThat()
617         .contains("java.lang.IllegalStateException: Not a JSON Object: true");
618   }
619 
620   @Test
testRead_keyDataIsNotAnObject_throws()621   public void testRead_keyDataIsNotAnObject_throws() throws Exception {
622     String jsonKeyset =
623         "{"
624             + "  \"primaryKeyId\": 42818733,"
625             + "  \"key\": ["
626             + "    {"
627             + "      \"keyData\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\","
628             + "      \"outputPrefixType\": \"TINK\","
629             + "      \"keyId\": 42818733,"
630             + "      \"status\": \"ENABLED\""
631             + "    }]"
632             + "}";
633     IOException e =
634         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
635     assertThat(e).hasMessageThat().contains("keyData must be an object");
636   }
637 
638   @Test
testRead_outputPrefixTypeIsNotAString_throws()639   public void testRead_outputPrefixTypeIsNotAString_throws() throws Exception {
640     String jsonKeyset =
641         "{"
642             + "  \"primaryKeyId\": 42818733,"
643             + "  \"key\": ["
644             + "    {"
645             + "      \"keyData\": {"
646             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
647             + "        \"keyMaterialType\": \"SYMMETRIC\","
648             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
649             + "      },"
650             + "      \"outputPrefixType\": 1,"
651             + "      \"keyId\": 42818733,"
652             + "      \"status\": \"ENABLED\""
653             + "    }]"
654             + "}";
655     IOException e =
656         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
657     assertThat(e).hasMessageThat().contains("unknown output prefix type: 1");
658   }
659 
660   @Test
testRead_keyIdIsAString_throws()661   public void testRead_keyIdIsAString_throws() throws Exception {
662     String jsonKeyset =
663         "{"
664             + "  \"primaryKeyId\": 42818733,"
665             + "  \"key\": ["
666             + "    {"
667             + "      \"keyData\": {"
668             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
669             + "        \"keyMaterialType\": \"SYMMETRIC\","
670             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
671             + "      },"
672             + "      \"outputPrefixType\": \"TINK\","
673             + "      \"keyId\": \"42818733\","
674             + "      \"status\": \"ENABLED\""
675             + "    }]"
676             + "}";
677     IOException e =
678         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
679     assertThat(e).hasMessageThat().contains("invalid key id");
680   }
681 
682   @Test
testRead_keyIdIsNotAnIntegerOrAString_throws()683   public void testRead_keyIdIsNotAnIntegerOrAString_throws() throws Exception {
684     String jsonKeyset =
685         "{"
686             + "  \"primaryKeyId\": 42818733,"
687             + "  \"key\": ["
688             + "    {"
689             + "      \"keyData\": {"
690             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
691             + "        \"keyMaterialType\": \"SYMMETRIC\","
692             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
693             + "      },"
694             + "      \"outputPrefixType\": \"TINK\","
695             + "      \"keyId\": true,"
696             + "      \"status\": \"ENABLED\""
697             + "    }]"
698             + "}";
699     IOException e =
700         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
701     assertThat(e).hasMessageThat().contains("invalid key id");
702   }
703 
704   @Test
testRead_statusIsNotAString_throws()705   public void testRead_statusIsNotAString_throws() throws Exception {
706     String jsonKeyset =
707         "{"
708             + "  \"primaryKeyId\": 42818733,"
709             + "  \"key\": ["
710             + "    {"
711             + "      \"keyData\": {"
712             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
713             + "        \"keyMaterialType\": \"SYMMETRIC\","
714             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
715             + "      },"
716             + "      \"outputPrefixType\": \"TINK\","
717             + "      \"keyId\": 42818733,"
718             + "      \"status\": true"
719             + "    }]"
720             + "}";
721 
722     IOException e =
723         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
724     assertThat(e).hasMessageThat().contains("unknown status: true");
725   }
726 
727   @Test
testRead_typeUrlIsNotAString_works()728   public void testRead_typeUrlIsNotAString_works() throws Exception {
729     String jsonKeyset =
730         "{"
731             + "  \"primaryKeyId\": 42818733,"
732             + "  \"key\": ["
733             + "    {"
734             + "      \"keyData\": {"
735             + "        \"typeUrl\": 123,"
736             + "        \"keyMaterialType\": \"SYMMETRIC\","
737             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
738             + "      },"
739             + "      \"outputPrefixType\": \"TINK\","
740             + "      \"keyId\": 42818733,"
741             + "      \"status\": \"ENABLED\""
742             + "    }]"
743             + "}";
744     Keyset keyset = JsonKeysetReader.withString(jsonKeyset).read();
745     assertThat(keyset.getKey(0).getKeyData().getTypeUrl()).isEqualTo("123");
746   }
747 
748   @Test
testRead_valueIsNotAString_throws()749   public void testRead_valueIsNotAString_throws() throws Exception {
750     String jsonKeyset =
751         "{"
752             + "  \"primaryKeyId\": 42818733,"
753             + "  \"key\": ["
754             + "    {"
755             + "      \"keyData\": {"
756             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
757             + "        \"keyMaterialType\": \"SYMMETRIC\","
758             + "        \"value\": 123"
759             + "      },"
760             + "      \"outputPrefixType\": \"TINK\","
761             + "      \"keyId\": 42818733,"
762             + "      \"status\": \"ENABLED\""
763             + "    }]"
764             + "}";
765     Keyset keyset = JsonKeysetReader.withString(jsonKeyset).read();
766     assertThat(keyset.getKey(0).getKeyData().getValue().size()).isEqualTo(2);
767   }
768 
769   @Test
testRead_keyMaterialTypeIsNotAString_throws()770   public void testRead_keyMaterialTypeIsNotAString_throws() throws Exception {
771     String jsonKeyset =
772         "{"
773             + "  \"primaryKeyId\": 42818733,"
774             + "  \"key\": ["
775             + "    {"
776             + "      \"keyData\": {"
777             + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
778             + "        \"keyMaterialType\": 123,"
779             + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
780             + "      },"
781             + "      \"outputPrefixType\": \"TINK\","
782             + "      \"keyId\": 42818733,"
783             + "      \"status\": \"ENABLED\""
784             + "    }]"
785             + "}";
786     IOException e =
787         assertThrows(IOException.class, () -> JsonKeysetReader.withString(jsonKeyset).read());
788     assertThat(e).hasMessageThat().contains("unknown key material type: 123");
789   }
790 }
791