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