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