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.aead; 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; 24 import com.google.crypto.tink.InsecureSecretKeyAccess; 25 import com.google.crypto.tink.KeysetHandle; 26 import com.google.crypto.tink.RegistryConfiguration; 27 import com.google.crypto.tink.TinkProtoKeysetFormat; 28 import com.google.crypto.tink.aead.AesCtrHmacAeadParameters.HashType; 29 import com.google.crypto.tink.aead.AesCtrHmacAeadParameters.Variant; 30 import com.google.crypto.tink.internal.MonitoringAnnotations; 31 import com.google.crypto.tink.internal.MutableMonitoringRegistry; 32 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 33 import com.google.crypto.tink.internal.PrimitiveConstructor; 34 import com.google.crypto.tink.internal.PrimitiveRegistry; 35 import com.google.crypto.tink.internal.testing.FakeMonitoringClient; 36 import com.google.crypto.tink.proto.AesCtrHmacAeadKey; 37 import com.google.crypto.tink.proto.KeyData.KeyMaterialType; 38 import com.google.crypto.tink.proto.KeyStatusType; 39 import com.google.crypto.tink.proto.Keyset; 40 import com.google.crypto.tink.proto.Keyset.Key; 41 import com.google.crypto.tink.proto.OutputPrefixType; 42 import com.google.crypto.tink.subtle.Bytes; 43 import com.google.crypto.tink.subtle.Hex; 44 import com.google.crypto.tink.subtle.Random; 45 import com.google.crypto.tink.testing.TestUtil; 46 import com.google.crypto.tink.util.SecretBytes; 47 import java.security.GeneralSecurityException; 48 import java.util.Arrays; 49 import java.util.List; 50 import org.junit.BeforeClass; 51 import org.junit.Test; 52 import org.junit.experimental.theories.DataPoints; 53 import org.junit.experimental.theories.FromDataPoints; 54 import org.junit.experimental.theories.Theories; 55 import org.junit.experimental.theories.Theory; 56 import org.junit.runner.RunWith; 57 58 /** Unit tests for {@link AeadWrapper}. */ 59 @RunWith(Theories.class) 60 public class AeadWrapperTest { 61 62 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkKey; 63 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkFixedKey; 64 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey tinkFixedKeyDifferentId; 65 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey crunchyFixedKey; 66 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawKey0; 67 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawKey1; 68 private static com.google.crypto.tink.aead.AesCtrHmacAeadKey rawFixedKey; 69 70 @DataPoints("keys") 71 public static com.google.crypto.tink.Key[] keys; 72 73 @BeforeClass setUpClass()74 public static void setUpClass() throws Exception { 75 AesCtrHmacAeadKeyManager.register(true); 76 77 AesCtrHmacAeadParameters tinkParameters = 78 AesCtrHmacAeadParameters.builder() 79 .setAesKeySizeBytes(32) 80 .setHmacKeySizeBytes(32) 81 .setHashType(HashType.SHA512) 82 .setIvSizeBytes(16) 83 .setTagSizeBytes(16) 84 .setVariant(Variant.TINK) 85 .build(); 86 tinkKey = 87 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 88 .setParameters(tinkParameters) 89 .setAesKeyBytes(SecretBytes.randomBytes(32)) 90 .setHmacKeyBytes(SecretBytes.randomBytes(32)) 91 .setIdRequirement(42) 92 .build(); 93 tinkFixedKey = 94 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 95 .setParameters(tinkParameters) 96 .setAesKeyBytes( 97 SecretBytes.copyFrom( 98 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 99 InsecureSecretKeyAccess.get())) 100 .setHmacKeyBytes( 101 SecretBytes.copyFrom( 102 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 103 InsecureSecretKeyAccess.get())) 104 .setIdRequirement(42) 105 .build(); 106 tinkFixedKeyDifferentId = 107 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 108 .setParameters(tinkParameters) 109 .setAesKeyBytes( 110 SecretBytes.copyFrom( 111 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 112 InsecureSecretKeyAccess.get())) 113 .setHmacKeyBytes( 114 SecretBytes.copyFrom( 115 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 116 InsecureSecretKeyAccess.get())) 117 .setIdRequirement(43) 118 .build(); 119 AesCtrHmacAeadParameters crunchyParameters = 120 AesCtrHmacAeadParameters.builder() 121 .setAesKeySizeBytes(32) 122 .setHmacKeySizeBytes(32) 123 .setHashType(HashType.SHA512) 124 .setIvSizeBytes(16) 125 .setTagSizeBytes(16) 126 .setVariant(Variant.CRUNCHY) 127 .build(); 128 crunchyFixedKey = 129 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 130 .setParameters(crunchyParameters) 131 .setAesKeyBytes( 132 SecretBytes.copyFrom( 133 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 134 InsecureSecretKeyAccess.get())) 135 .setHmacKeyBytes( 136 SecretBytes.copyFrom( 137 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 138 InsecureSecretKeyAccess.get())) 139 .setIdRequirement(42) 140 .build(); 141 AesCtrHmacAeadParameters rawParameters = 142 AesCtrHmacAeadParameters.builder() 143 .setAesKeySizeBytes(32) 144 .setHmacKeySizeBytes(32) 145 .setHashType(HashType.SHA512) 146 .setIvSizeBytes(16) 147 .setTagSizeBytes(16) 148 .setVariant(Variant.NO_PREFIX) 149 .build(); 150 rawKey0 = 151 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 152 .setParameters(rawParameters) 153 .setAesKeyBytes(SecretBytes.randomBytes(32)) 154 .setHmacKeyBytes(SecretBytes.randomBytes(32)) 155 .build(); 156 rawKey1 = 157 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 158 .setParameters(rawParameters) 159 .setAesKeyBytes(SecretBytes.randomBytes(32)) 160 .setHmacKeyBytes(SecretBytes.randomBytes(32)) 161 .build(); 162 rawFixedKey = 163 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 164 .setParameters(rawParameters) 165 .setAesKeyBytes( 166 SecretBytes.copyFrom( 167 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 168 InsecureSecretKeyAccess.get())) 169 .setHmacKeyBytes( 170 SecretBytes.copyFrom( 171 Hex.decode("0011223344556677889910111213141516171819202122232425262728293031"), 172 InsecureSecretKeyAccess.get())) 173 .build(); 174 175 keys = new com.google.crypto.tink.Key[] {tinkKey, crunchyFixedKey, rawKey0}; 176 } 177 178 @Test wrappedNonRawEncrypt_addsPrefixToRawCiphertext()179 public void wrappedNonRawEncrypt_addsPrefixToRawCiphertext() throws Exception { 180 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 181 AeadConfig.register(); 182 183 byte[] plaintext = "plaintext".getBytes(UTF_8); 184 byte[] associatedData = "associatedData".getBytes(UTF_8); 185 byte[] outputPrefix = Hex.decode("000000002a"); 186 187 KeysetHandle rawKeysetHandle = 188 KeysetHandle.newBuilder() 189 .addEntry(KeysetHandle.importKey(rawFixedKey).withFixedId(0x0000002a).makePrimary()) 190 .build(); 191 Aead rawAead = rawKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 192 KeysetHandle tinkKeysetHandle = 193 KeysetHandle.newBuilder() 194 .addEntry(KeysetHandle.importKey(crunchyFixedKey).makePrimary()) 195 .build(); 196 Aead crunchyAead = tinkKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 197 198 byte[] ciphertext = crunchyAead.encrypt(plaintext, associatedData); 199 byte[] ciphertextPrefix = Arrays.copyOf(ciphertext, 5); 200 byte[] ciphertextWithoutPrefix = Arrays.copyOfRange(ciphertext, 5, ciphertext.length); 201 202 assertThat(ciphertextPrefix).isEqualTo(outputPrefix); 203 assertThat(rawAead.decrypt(ciphertextWithoutPrefix, associatedData)).isEqualTo(plaintext); 204 } 205 206 @Test wrappedNonRawDecrypt_decryptsRawCiphertextWithPrefix()207 public void wrappedNonRawDecrypt_decryptsRawCiphertextWithPrefix() throws Exception { 208 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 209 AeadConfig.register(); 210 211 byte[] plaintext = "plaintext".getBytes(UTF_8); 212 byte[] associatedData = "associatedData".getBytes(UTF_8); 213 byte[] outputPrefix = Hex.decode("010000002a"); 214 byte[] invalid = "invalid".getBytes(UTF_8); 215 216 KeysetHandle rawKeysetHandle = 217 KeysetHandle.newBuilder() 218 .addEntry(KeysetHandle.importKey(rawFixedKey).withFixedId(0x0000002a).makePrimary()) 219 .build(); 220 Aead rawAead = rawKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 221 KeysetHandle tinkKeysetHandle = 222 KeysetHandle.newBuilder() 223 .addEntry(KeysetHandle.importKey(tinkFixedKey).makePrimary()) 224 .build(); 225 Aead tinkAead = tinkKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 226 227 byte[] rawCiphertext = rawAead.encrypt(plaintext, associatedData); 228 byte[] rawCiphertextWithTinkPrefix = Bytes.concat(outputPrefix, rawCiphertext); 229 230 assertThat(tinkAead.decrypt(rawCiphertextWithTinkPrefix, associatedData)).isEqualTo(plaintext); 231 assertThrows( 232 GeneralSecurityException.class, () -> tinkAead.decrypt(rawCiphertext, associatedData)); 233 assertThrows( 234 GeneralSecurityException.class, 235 () -> tinkAead.decrypt(rawCiphertextWithTinkPrefix, invalid)); 236 assertThrows(GeneralSecurityException.class, () -> tinkAead.decrypt(invalid, associatedData)); 237 assertThrows( 238 GeneralSecurityException.class, () -> tinkAead.decrypt("".getBytes(UTF_8), associatedData)); 239 } 240 241 @Theory encryptAndDecrypt_success(@romDataPoints"keys") com.google.crypto.tink.Key key)242 public void encryptAndDecrypt_success(@FromDataPoints("keys") com.google.crypto.tink.Key key) 243 throws Exception { 244 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 245 AeadConfig.register(); 246 247 byte[] plaintext = "plaintext".getBytes(UTF_8); 248 byte[] associatedData = "associatedData".getBytes(UTF_8); 249 250 KeysetHandle keysetHandle = 251 KeysetHandle.newBuilder() 252 .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary()) 253 .build(); 254 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 255 byte[] ciphertext = aead.encrypt(plaintext, associatedData); 256 257 assertThat(aead.decrypt(ciphertext, associatedData)).isEqualTo(plaintext); 258 } 259 260 @Theory encryptAndDecrypt_incorrectInputsFail( @romDataPoints"keys") com.google.crypto.tink.Key key)261 public void encryptAndDecrypt_incorrectInputsFail( 262 @FromDataPoints("keys") com.google.crypto.tink.Key key) throws Exception { 263 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 264 AeadConfig.register(); 265 266 byte[] plaintext = "plaintext".getBytes(UTF_8); 267 byte[] associatedData = "associatedData".getBytes(UTF_8); 268 byte[] invalid = "invalid".getBytes(UTF_8); 269 270 KeysetHandle keysetHandle = 271 KeysetHandle.newBuilder() 272 .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary()) 273 .build(); 274 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 275 KeysetHandle incorrectKeyKeysetHandle = 276 KeysetHandle.newBuilder() 277 .addEntry(KeysetHandle.importKey(rawKey1).withFixedId(42).makePrimary()) 278 .build(); 279 Aead incorrectKeyAead = 280 incorrectKeyKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 281 byte[] ciphertext = aead.encrypt(plaintext, associatedData); 282 283 assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext, invalid)); 284 assertThrows(GeneralSecurityException.class, () -> aead.decrypt(invalid, associatedData)); 285 assertThrows( 286 GeneralSecurityException.class, () -> aead.decrypt("".getBytes(UTF_8), associatedData)); 287 assertThrows( 288 GeneralSecurityException.class, () -> incorrectKeyAead.decrypt(ciphertext, associatedData)); 289 } 290 291 @Test decryptWorksIfCiphertextIsValidForAnyPrimitiveInThePrimitiveSet()292 public void decryptWorksIfCiphertextIsValidForAnyPrimitiveInThePrimitiveSet() throws Exception { 293 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 294 AeadConfig.register(); 295 296 byte[] plaintext = "plaintext".getBytes(UTF_8); 297 byte[] associatedData = "associatedData".getBytes(UTF_8); 298 299 KeysetHandle keysetHandle0 = 300 KeysetHandle.newBuilder() 301 .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary()) 302 .build(); 303 Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class); 304 KeysetHandle keysetHandle1 = 305 KeysetHandle.newBuilder() 306 .addEntry(KeysetHandle.importKey(rawKey1).withRandomId().makePrimary()) 307 .build(); 308 Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class); 309 KeysetHandle keysetHandle01 = 310 KeysetHandle.newBuilder() 311 .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary()) 312 .addEntry(KeysetHandle.importKey(rawKey1).withRandomId()) 313 .build(); 314 Aead aead01 = keysetHandle01.getPrimitive(RegistryConfiguration.get(), Aead.class); 315 byte[] ciphertext0 = aead0.encrypt(plaintext, associatedData); 316 byte[] ciphertext1 = aead1.encrypt(plaintext, associatedData); 317 318 assertThat(aead01.decrypt(ciphertext0, associatedData)).isEqualTo(plaintext); 319 assertThat(aead01.decrypt(ciphertext1, associatedData)).isEqualTo(plaintext); 320 } 321 322 @Test encryptUsesPrimaryPrimitive()323 public void encryptUsesPrimaryPrimitive() throws Exception { 324 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 325 AeadConfig.register(); 326 327 byte[] plaintext = "plaintext".getBytes(UTF_8); 328 byte[] associatedData = "associatedData".getBytes(UTF_8); 329 330 KeysetHandle keysetHandle0 = 331 KeysetHandle.newBuilder() 332 .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary()) 333 .build(); 334 Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class); 335 KeysetHandle keysetHandle1 = 336 KeysetHandle.newBuilder() 337 .addEntry(KeysetHandle.importKey(rawKey1).withRandomId().makePrimary()) 338 .build(); 339 Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class); 340 KeysetHandle keysetHandle01 = 341 KeysetHandle.newBuilder() 342 .addEntry(KeysetHandle.importKey(rawKey0).withRandomId().makePrimary()) 343 .addEntry(KeysetHandle.importKey(rawKey1).withRandomId()) 344 .build(); 345 Aead aead01 = keysetHandle01.getPrimitive(RegistryConfiguration.get(), Aead.class); 346 byte[] ciphertext = aead01.encrypt(plaintext, associatedData); 347 348 // rawKey0 is the primary key of aead01. Therefore, aead0 should be able to decrypt, and aead1 349 // not. 350 assertThat(aead0.decrypt(ciphertext, associatedData)).isEqualTo(plaintext); 351 assertThrows(GeneralSecurityException.class, () -> aead1.decrypt(ciphertext, associatedData)); 352 } 353 354 @Theory decryptFailsIfEncryptedWithOtherKeyEvenIfKeyIdsAreEqual( @romDataPoints"keys") com.google.crypto.tink.Key key)355 public void decryptFailsIfEncryptedWithOtherKeyEvenIfKeyIdsAreEqual( 356 @FromDataPoints("keys") com.google.crypto.tink.Key key) throws Exception { 357 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 358 AeadConfig.register(); 359 360 byte[] plaintext = "plaintext".getBytes(UTF_8); 361 byte[] associatedData = "associatedData".getBytes(UTF_8); 362 363 KeysetHandle keysetHandle0 = 364 KeysetHandle.newBuilder() 365 .addEntry(KeysetHandle.importKey(key).withFixedId(42).makePrimary()) 366 .build(); 367 Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class); 368 KeysetHandle keysetHandle1 = 369 KeysetHandle.newBuilder() 370 .addEntry(KeysetHandle.importKey(rawKey1).withFixedId(42).makePrimary()) 371 .build(); 372 Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class); 373 byte[] ciphertext = aead0.encrypt(plaintext, associatedData); 374 375 assertThrows(GeneralSecurityException.class, () -> aead1.decrypt(ciphertext, associatedData)); 376 } 377 378 @Test nonRawKeysWithSameKeyMaterialButDifferentKeyIds_decryptFails()379 public void nonRawKeysWithSameKeyMaterialButDifferentKeyIds_decryptFails() throws Exception { 380 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 381 AeadConfig.register(); 382 383 byte[] plaintext = "plaintext".getBytes(UTF_8); 384 byte[] associatedData = "associatedData".getBytes(UTF_8); 385 386 KeysetHandle tinkKeysetHandle0 = 387 KeysetHandle.newBuilder() 388 .addEntry(KeysetHandle.importKey(tinkFixedKey).makePrimary()) 389 .build(); 390 Aead tinkAead0 = tinkKeysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class); 391 KeysetHandle tinkKeysetHandle1 = 392 KeysetHandle.newBuilder() 393 .addEntry(KeysetHandle.importKey(tinkFixedKeyDifferentId).makePrimary()) 394 .build(); 395 Aead tinkAead1 = tinkKeysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class); 396 397 byte[] ciphertext = tinkAead0.encrypt(plaintext, associatedData); 398 399 assertThrows( 400 GeneralSecurityException.class, () -> tinkAead1.decrypt(ciphertext, associatedData)); 401 } 402 403 @Test rawKeysWithSameKeyMaterialButDifferentKeyIds_decryptWorks()404 public void rawKeysWithSameKeyMaterialButDifferentKeyIds_decryptWorks() throws Exception { 405 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 406 AeadConfig.register(); 407 408 byte[] plaintext = "plaintext".getBytes(UTF_8); 409 byte[] associatedData = "associatedData".getBytes(UTF_8); 410 411 KeysetHandle keysetHandle0 = 412 KeysetHandle.newBuilder() 413 .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(123).makePrimary()) 414 .build(); 415 Aead aead0 = keysetHandle0.getPrimitive(RegistryConfiguration.get(), Aead.class); 416 KeysetHandle keysetHandle1 = 417 KeysetHandle.newBuilder() 418 .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(42).makePrimary()) 419 .build(); 420 Aead aead1 = keysetHandle1.getPrimitive(RegistryConfiguration.get(), Aead.class); 421 byte[] ciphertext = aead0.encrypt(plaintext, associatedData); 422 423 assertThat(aead1.decrypt(ciphertext, associatedData)).isEqualTo(plaintext); 424 } 425 426 @Test getPrimitiveFromKeysetHandleWithoutPrimary_throws()427 public void getPrimitiveFromKeysetHandleWithoutPrimary_throws() throws Exception { 428 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 429 AeadConfig.register(); 430 431 AesCtrHmacAeadKey aesCtrHmacAeadProtoKey = 432 AesCtrHmacAeadKey.newBuilder() 433 .setAesCtrKey(TestUtil.createAesCtrKey(Random.randBytes(16), 12)) 434 .setHmacKey(TestUtil.createHmacKey(Random.randBytes(20), 16)) 435 .build(); 436 Key key = 437 TestUtil.createKey( 438 TestUtil.createKeyData( 439 aesCtrHmacAeadProtoKey, 440 "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", 441 KeyMaterialType.SYMMETRIC), 442 123, 443 KeyStatusType.ENABLED, 444 OutputPrefixType.TINK); 445 Keyset keysetWithoutPrimary = Keyset.newBuilder().addKey(key).build(); 446 assertThrows( 447 GeneralSecurityException.class, 448 () -> 449 TinkProtoKeysetFormat.parseKeyset( 450 keysetWithoutPrimary.toByteArray(), InsecureSecretKeyAccess.get()) 451 .getPrimitive(RegistryConfiguration.get(), Aead.class)); 452 } 453 454 @Test testAeadWithoutAnnotations_hasNoMonitoring()455 public void testAeadWithoutAnnotations_hasNoMonitoring() throws Exception { 456 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 457 AeadConfig.register(); 458 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 459 MutableMonitoringRegistry.globalInstance().clear(); 460 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 461 462 KeysetHandle keysetHandle = 463 KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(tinkKey).makePrimary()).build(); 464 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 465 466 byte[] plaintext = "plaintext".getBytes(UTF_8); 467 byte[] associatedData = "associatedData".getBytes(UTF_8); 468 byte[] ciphertext = aead.encrypt(plaintext, associatedData); 469 assertThat(aead.decrypt(ciphertext, associatedData)).isEqualTo(plaintext); 470 assertThrows( 471 GeneralSecurityException.class, () -> aead.decrypt(ciphertext, "invalid".getBytes(UTF_8))); 472 473 // Without annotations, nothing gets logged. 474 assertThat(fakeMonitoringClient.getLogEntries()).isEmpty(); 475 assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty(); 476 } 477 478 @Test testAeadWithAnnotations_hasMonitoring()479 public void testAeadWithAnnotations_hasMonitoring() throws Exception { 480 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 481 AeadConfig.register(); 482 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 483 MutableMonitoringRegistry.globalInstance().clear(); 484 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 485 486 byte[] plaintext = Random.randBytes(20); 487 byte[] plaintext2 = Random.randBytes(30); 488 byte[] associatedData = Random.randBytes(40); 489 490 // generate ciphertext2 using key2 491 KeysetHandle singleKeyKeysetHandle = 492 KeysetHandle.newBuilder() 493 .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(43).makePrimary()) 494 .build(); 495 Aead aead2 = singleKeyKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 496 byte[] ciphertext2 = aead2.encrypt(plaintext2, associatedData); 497 498 MonitoringAnnotations annotations = 499 MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build(); 500 KeysetHandle twoKeysKeysetHandle = 501 KeysetHandle.newBuilder() 502 .addEntry(KeysetHandle.importKey(tinkKey).makePrimary()) 503 .addEntry(KeysetHandle.importKey(rawKey0).withFixedId(43)) 504 .setMonitoringAnnotations(annotations) 505 .build(); 506 Aead aead = twoKeysKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 507 508 byte[] ciphertext = aead.encrypt(plaintext, associatedData); // uses key1 to encrypt 509 byte[] decrypted = aead.decrypt(ciphertext, associatedData); 510 assertThat(decrypted).isEqualTo(plaintext); 511 byte[] decrypted2 = aead.decrypt(ciphertext2, associatedData); 512 assertThat(decrypted2).isEqualTo(plaintext2); 513 514 assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext, new byte[0])); 515 516 List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries(); 517 assertThat(logEntries).hasSize(3); 518 FakeMonitoringClient.LogEntry encEntry = logEntries.get(0); 519 assertThat(encEntry.getKeyId()).isEqualTo(42); 520 assertThat(encEntry.getPrimitive()).isEqualTo("aead"); 521 assertThat(encEntry.getApi()).isEqualTo("encrypt"); 522 assertThat(encEntry.getNumBytesAsInput()).isEqualTo(plaintext.length); 523 assertThat(encEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 524 525 FakeMonitoringClient.LogEntry decEntry = logEntries.get(1); 526 assertThat(decEntry.getKeyId()).isEqualTo(42); 527 assertThat(decEntry.getPrimitive()).isEqualTo("aead"); 528 assertThat(decEntry.getApi()).isEqualTo("decrypt"); 529 assertThat(decEntry.getNumBytesAsInput()).isEqualTo(ciphertext.length); 530 assertThat(decEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 531 532 FakeMonitoringClient.LogEntry dec2Entry = logEntries.get(2); 533 assertThat(dec2Entry.getKeyId()).isEqualTo(43); 534 assertThat(dec2Entry.getPrimitive()).isEqualTo("aead"); 535 assertThat(dec2Entry.getApi()).isEqualTo("decrypt"); 536 // ciphertext2 was encrypted with key2, which has a RAW ouput prefix. 537 assertThat(dec2Entry.getNumBytesAsInput()).isEqualTo(ciphertext2.length); 538 assertThat(dec2Entry.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 539 540 List<FakeMonitoringClient.LogFailureEntry> failures = 541 fakeMonitoringClient.getLogFailureEntries(); 542 assertThat(failures).hasSize(1); 543 FakeMonitoringClient.LogFailureEntry decFailure = failures.get(0); 544 assertThat(decFailure.getPrimitive()).isEqualTo("aead"); 545 assertThat(decFailure.getApi()).isEqualTo("decrypt"); 546 assertThat(decFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42); 547 assertThat(decFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 548 } 549 550 private static class AlwaysFailingAead implements Aead { AlwaysFailingAead(com.google.crypto.tink.aead.AesCtrHmacAeadKey key)551 public AlwaysFailingAead(com.google.crypto.tink.aead.AesCtrHmacAeadKey key) {} 552 553 @Override encrypt(byte[] plaintext, byte[] aad)554 public byte[] encrypt(byte[] plaintext, byte[] aad) throws GeneralSecurityException { 555 throw new GeneralSecurityException("fail"); 556 } 557 558 @Override decrypt(byte[] ciphertext, byte[] aad)559 public byte[] decrypt(byte[] ciphertext, byte[] aad) throws GeneralSecurityException { 560 throw new GeneralSecurityException("fail"); 561 } 562 } 563 564 @Test testFailingAeadWithAnnotations_hasMonitoring()565 public void testFailingAeadWithAnnotations_hasMonitoring() throws Exception { 566 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 567 MutablePrimitiveRegistry.globalInstance() 568 .registerPrimitiveConstructor( 569 PrimitiveConstructor.create( 570 AlwaysFailingAead::new, 571 com.google.crypto.tink.aead.AesCtrHmacAeadKey.class, 572 Aead.class)); 573 AeadWrapper.register(); 574 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 575 MutableMonitoringRegistry.globalInstance().clear(); 576 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 577 578 MonitoringAnnotations annotations = 579 MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build(); 580 KeysetHandle aesCtrHmacAeadKeysetHandle = 581 KeysetHandle.newBuilder() 582 .addEntry(KeysetHandle.importKey(tinkKey).makePrimary()) 583 .setMonitoringAnnotations(annotations) 584 .build(); 585 Aead aead = aesCtrHmacAeadKeysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 586 587 byte[] randomBytes = Random.randBytes(20); 588 byte[] associatedData = Random.randBytes(20); 589 assertThrows(GeneralSecurityException.class, () -> aead.encrypt(randomBytes, associatedData)); 590 assertThrows(GeneralSecurityException.class, () -> aead.decrypt(randomBytes, associatedData)); 591 592 assertThat(fakeMonitoringClient.getLogEntries()).isEmpty(); 593 594 List<FakeMonitoringClient.LogFailureEntry> failures = 595 fakeMonitoringClient.getLogFailureEntries(); 596 assertThat(failures).hasSize(2); 597 FakeMonitoringClient.LogFailureEntry encFailure = failures.get(0); 598 assertThat(encFailure.getPrimitive()).isEqualTo("aead"); 599 assertThat(encFailure.getApi()).isEqualTo("encrypt"); 600 assertThat(encFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42); 601 assertThat(encFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 602 603 FakeMonitoringClient.LogFailureEntry decFailure = failures.get(1); 604 assertThat(decFailure.getPrimitive()).isEqualTo("aead"); 605 assertThat(decFailure.getApi()).isEqualTo("decrypt"); 606 assertThat(decFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42); 607 assertThat(decFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 608 } 609 610 @Test registerToInternalPrimitiveRegistry_works()611 public void registerToInternalPrimitiveRegistry_works() throws Exception { 612 PrimitiveRegistry.Builder initialBuilder = PrimitiveRegistry.builder(); 613 PrimitiveRegistry initialRegistry = initialBuilder.build(); 614 PrimitiveRegistry.Builder processedBuilder = PrimitiveRegistry.builder(initialRegistry); 615 616 AeadWrapper.registerToInternalPrimitiveRegistry(processedBuilder); 617 PrimitiveRegistry processedRegistry = processedBuilder.build(); 618 619 assertThrows( 620 GeneralSecurityException.class, 621 () -> initialRegistry.getInputPrimitiveClass(Aead.class)); 622 assertThat(processedRegistry.getInputPrimitiveClass(Aead.class)) 623 .isEqualTo(Aead.class); 624 } 625 } 626