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 import static org.junit.Assert.assertTrue; 23 24 import com.google.crypto.tink.Aead; 25 import com.google.crypto.tink.InsecureSecretKeyAccess; 26 import com.google.crypto.tink.Key; 27 import com.google.crypto.tink.KeyTemplate; 28 import com.google.crypto.tink.KeyTemplates; 29 import com.google.crypto.tink.KeysetHandle; 30 import com.google.crypto.tink.Parameters; 31 import com.google.crypto.tink.RegistryConfiguration; 32 import com.google.crypto.tink.TinkProtoKeysetFormat; 33 import com.google.crypto.tink.internal.KeyManagerRegistry; 34 import com.google.crypto.tink.internal.SlowInputStream; 35 import com.google.crypto.tink.keyderivation.KeyDerivationConfig; 36 import com.google.crypto.tink.keyderivation.KeysetDeriver; 37 import com.google.crypto.tink.keyderivation.PrfBasedKeyDerivationKey; 38 import com.google.crypto.tink.keyderivation.PrfBasedKeyDerivationParameters; 39 import com.google.crypto.tink.prf.HkdfPrfKey; 40 import com.google.crypto.tink.prf.HkdfPrfParameters; 41 import com.google.crypto.tink.prf.PrfKey; 42 import com.google.crypto.tink.subtle.EncryptThenAuthenticate; 43 import com.google.crypto.tink.subtle.Hex; 44 import com.google.crypto.tink.subtle.Random; 45 import com.google.crypto.tink.util.SecretBytes; 46 import java.io.ByteArrayInputStream; 47 import java.security.GeneralSecurityException; 48 import java.util.Arrays; 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.experimental.theories.DataPoints; 52 import org.junit.experimental.theories.FromDataPoints; 53 import org.junit.experimental.theories.Theories; 54 import org.junit.experimental.theories.Theory; 55 import org.junit.runner.RunWith; 56 57 /** Tests for AesCtrHmacAeadKeyManager. */ 58 @RunWith(Theories.class) 59 public class AesCtrHmacAeadKeyManagerTest { 60 @Before register()61 public void register() throws Exception { 62 AeadConfig.register(); 63 KeyDerivationConfig.register(); 64 } 65 66 @Test testKeyManagerRegistered()67 public void testKeyManagerRegistered() throws Exception { 68 assertThat( 69 KeyManagerRegistry.globalInstance() 70 .getKeyManager( 71 "type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey", Aead.class)) 72 .isNotNull(); 73 } 74 75 @Test getPrimtive_encryptDecryptTink_worksAsDirectlyCreated()76 public void getPrimtive_encryptDecryptTink_worksAsDirectlyCreated() throws Exception { 77 AesCtrHmacAeadParameters parameters = 78 AesCtrHmacAeadParameters.builder() 79 .setAesKeySizeBytes(32) 80 .setHmacKeySizeBytes(32) 81 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 82 .setTagSizeBytes(17) 83 .setIvSizeBytes(14) 84 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 85 .build(); 86 com.google.crypto.tink.aead.AesCtrHmacAeadKey key = 87 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 88 .setAesKeyBytes(SecretBytes.randomBytes(32)) 89 .setHmacKeyBytes(SecretBytes.randomBytes(32)) 90 .setParameters(parameters) 91 .setIdRequirement(42) 92 .build(); 93 KeysetHandle keysetHandle = 94 KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build(); 95 byte[] plaintext = "plaintext".getBytes(UTF_8); 96 byte[] aad = "aad".getBytes(UTF_8); 97 98 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 99 Aead directAead = EncryptThenAuthenticate.create(key); 100 101 assertThat(directAead.decrypt(aead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext); 102 assertThat(aead.decrypt(directAead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext); 103 } 104 105 @Test getPrimitive_encryptDecryptCrunchy_works()106 public void getPrimitive_encryptDecryptCrunchy_works() throws Exception { 107 AesCtrHmacAeadParameters parameters = 108 AesCtrHmacAeadParameters.builder() 109 .setAesKeySizeBytes(16) 110 .setHmacKeySizeBytes(32) 111 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 112 .setTagSizeBytes(18) 113 .setIvSizeBytes(13) 114 .setVariant(AesCtrHmacAeadParameters.Variant.CRUNCHY) 115 .build(); 116 com.google.crypto.tink.aead.AesCtrHmacAeadKey key = 117 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 118 .setAesKeyBytes(SecretBytes.randomBytes(16)) 119 .setHmacKeyBytes(SecretBytes.randomBytes(32)) 120 .setParameters(parameters) 121 .setIdRequirement(42) 122 .build(); 123 KeysetHandle keysetHandle = 124 KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build(); 125 byte[] plaintext = "plaintext".getBytes(UTF_8); 126 byte[] aad = "aad".getBytes(UTF_8); 127 128 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 129 130 assertThat(aead.decrypt(aead.encrypt(plaintext, aad), aad)).isEqualTo(plaintext); 131 } 132 133 @Test getPrimitive_bitFlipCiphertext_throws()134 public void getPrimitive_bitFlipCiphertext_throws() throws Exception { 135 AesCtrHmacAeadParameters parameters = 136 AesCtrHmacAeadParameters.builder() 137 .setAesKeySizeBytes(32) 138 .setHmacKeySizeBytes(16) 139 .setHashType(AesCtrHmacAeadParameters.HashType.SHA512) 140 .setTagSizeBytes(16) 141 .setIvSizeBytes(12) 142 .setVariant(AesCtrHmacAeadParameters.Variant.CRUNCHY) 143 .build(); 144 com.google.crypto.tink.aead.AesCtrHmacAeadKey key = 145 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 146 .setAesKeyBytes(SecretBytes.randomBytes(32)) 147 .setHmacKeyBytes(SecretBytes.randomBytes(16)) 148 .setParameters(parameters) 149 .setIdRequirement(42) 150 .build(); 151 KeysetHandle keysetHandle = 152 KeysetHandle.newBuilder().addEntry(KeysetHandle.importKey(key).makePrimary()).build(); 153 byte[] plaintext = Random.randBytes(1001); 154 byte[] aad = Random.randBytes(13); 155 156 Aead aead = keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead.class); 157 byte[] ciphertext = aead.encrypt(plaintext, aad); 158 159 for (int i = 0; i < ciphertext.length; i++) { 160 for (int j = 0; j < 8; j++) { 161 byte[] c1 = Arrays.copyOf(ciphertext, ciphertext.length); 162 c1[i] = (byte) (c1[i] ^ (1 << j)); 163 assertThrows(GeneralSecurityException.class, () -> aead.decrypt(c1, aad)); 164 } 165 } 166 } 167 168 @Test testAes128CtrHmacSha256Template()169 public void testAes128CtrHmacSha256Template() throws Exception { 170 KeyTemplate template = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template(); 171 assertThat(template.toParameters()) 172 .isEqualTo( 173 AesCtrHmacAeadParameters.builder() 174 .setAesKeySizeBytes(16) 175 .setHmacKeySizeBytes(32) 176 .setIvSizeBytes(16) 177 .setTagSizeBytes(16) 178 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 179 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 180 .build()); 181 } 182 183 @Test testAes256CtrHmacSha256Template()184 public void testAes256CtrHmacSha256Template() throws Exception { 185 KeyTemplate template = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template(); 186 assertThat(template.toParameters()) 187 .isEqualTo( 188 AesCtrHmacAeadParameters.builder() 189 .setAesKeySizeBytes(32) 190 .setHmacKeySizeBytes(32) 191 .setIvSizeBytes(16) 192 .setTagSizeBytes(32) 193 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 194 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 195 .build()); 196 } 197 198 @Test testKeyTemplatesWork()199 public void testKeyTemplatesWork() throws Exception { 200 Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters(); 201 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 202 203 p = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template().toParameters(); 204 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 205 } 206 207 @DataPoints("templateNames") 208 public static final String[] KEY_TEMPLATES = 209 new String[] { 210 "AES128_CTR_HMAC_SHA256", 211 "AES128_CTR_HMAC_SHA256_RAW", 212 "AES256_CTR_HMAC_SHA256", 213 "AES256_CTR_HMAC_SHA256_RAW", 214 }; 215 216 @Theory testTemplates(@romDataPoints"templateNames") String templateName)217 public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception { 218 KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName)); 219 assertThat(h.size()).isEqualTo(1); 220 assertThat(h.getAt(0).getKey().getParameters()) 221 .isEqualTo(KeyTemplates.get(templateName).toParameters()); 222 } 223 224 @Test callingCreateTwiceGivesDifferentKeys()225 public void callingCreateTwiceGivesDifferentKeys() throws Exception { 226 Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters(); 227 Key key = KeysetHandle.generateNew(p).getAt(0).getKey(); 228 for (int i = 0; i < 1000; ++i) { 229 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().equalsKey(key)).isFalse(); 230 } 231 } 232 233 @Theory testCreateKeyFromRandomness(@romDataPoints"templateNames") String templateName)234 public void testCreateKeyFromRandomness(@FromDataPoints("templateNames") String templateName) 235 throws Exception { 236 byte[] keyMaterial = 237 new byte[] { 238 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 239 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 240 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 241 }; 242 AesCtrHmacAeadParameters parameters = 243 (AesCtrHmacAeadParameters) KeyTemplates.get(templateName).toParameters(); 244 com.google.crypto.tink.aead.AesCtrHmacAeadKey key = 245 AesCtrHmacAeadKeyManager.createAesCtrHmacAeadKeyFromRandomness( 246 parameters, 247 new ByteArrayInputStream(keyMaterial), 248 parameters.hasIdRequirement() ? 123 : null, 249 InsecureSecretKeyAccess.get()); 250 byte[] expectedAesKey = Arrays.copyOf(keyMaterial, parameters.getAesKeySizeBytes()); 251 byte[] expectedHmacKey = 252 Arrays.copyOfRange( 253 keyMaterial, 254 parameters.getAesKeySizeBytes(), 255 parameters.getAesKeySizeBytes() + parameters.getHmacKeySizeBytes()); 256 Key expectedKey = 257 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 258 .setParameters(parameters) 259 .setIdRequirement(parameters.hasIdRequirement() ? 123 : null) 260 .setAesKeyBytes(SecretBytes.copyFrom(expectedAesKey, InsecureSecretKeyAccess.get())) 261 .setHmacKeyBytes(SecretBytes.copyFrom(expectedHmacKey, InsecureSecretKeyAccess.get())) 262 .build(); 263 assertTrue(key.equalsKey(expectedKey)); 264 } 265 266 @Test testCreateKeyFromRandomness_slowInputStream_works()267 public void testCreateKeyFromRandomness_slowInputStream_works() throws Exception { 268 byte[] keyMaterial = 269 new byte[] { 270 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 271 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 272 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 273 }; 274 AesCtrHmacAeadParameters parameters = 275 AesCtrHmacAeadParameters.builder() 276 .setAesKeySizeBytes(32) 277 .setHmacKeySizeBytes(32) 278 .setIvSizeBytes(16) 279 .setTagSizeBytes(32) 280 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 281 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 282 .build(); 283 com.google.crypto.tink.aead.AesCtrHmacAeadKey key = 284 AesCtrHmacAeadKeyManager.createAesCtrHmacAeadKeyFromRandomness( 285 parameters, 286 SlowInputStream.copyFrom(keyMaterial), 287 12347, 288 InsecureSecretKeyAccess.get()); 289 byte[] expectedAesKey = Arrays.copyOf(keyMaterial, parameters.getAesKeySizeBytes()); 290 byte[] expectedHmacKey = 291 Arrays.copyOfRange( 292 keyMaterial, 293 parameters.getAesKeySizeBytes(), 294 parameters.getAesKeySizeBytes() + parameters.getHmacKeySizeBytes()); 295 Key expectedKey = 296 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 297 .setParameters(parameters) 298 .setIdRequirement(12347) 299 .setAesKeyBytes(SecretBytes.copyFrom(expectedAesKey, InsecureSecretKeyAccess.get())) 300 .setHmacKeyBytes(SecretBytes.copyFrom(expectedHmacKey, InsecureSecretKeyAccess.get())) 301 .build(); 302 assertTrue(key.equalsKey(expectedKey)); 303 } 304 createDerivationKey(Parameters derivedParameters, int id)305 private static PrfBasedKeyDerivationKey createDerivationKey(Parameters derivedParameters, int id) 306 throws Exception { 307 PrfKey prfKey = 308 HkdfPrfKey.builder() 309 .setParameters( 310 HkdfPrfParameters.builder() 311 .setKeySizeBytes(32) 312 .setHashType(HkdfPrfParameters.HashType.SHA256) 313 .build()) 314 .setKeyBytes( 315 SecretBytes.copyFrom( 316 Hex.decode("0102030405060708091011121314151617181920212123242526272829303132"), 317 InsecureSecretKeyAccess.get())) 318 .build(); 319 PrfBasedKeyDerivationParameters derivationParameters = 320 PrfBasedKeyDerivationParameters.builder() 321 .setDerivedKeyParameters(derivedParameters) 322 .setPrfParameters(prfKey.getParameters()) 323 .build(); 324 return PrfBasedKeyDerivationKey.create(derivationParameters, prfKey, /* idRequirement= */ id); 325 } 326 secretBytesFromHex(String hex)327 private static final SecretBytes secretBytesFromHex(String hex) { 328 return SecretBytes.copyFrom(Hex.decode(hex), InsecureSecretKeyAccess.get()); 329 } 330 331 @Test testDeriveKey_predefinedKey_works()332 public void testDeriveKey_predefinedKey_works() throws Exception { 333 // Same test vector as in PrfBasedKeyDeriverTest 334 KeysetDeriver deriver = 335 KeysetHandle.newBuilder() 336 .addEntry( 337 KeysetHandle.importKey( 338 createDerivationKey(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256, 24680)) 339 .makePrimary()) 340 .build() 341 .getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class); 342 KeysetHandle derivedKeyset = deriver.deriveKeyset(Hex.decode("000102")); 343 assertThat(derivedKeyset.size()).isEqualTo(1); 344 assertThat(derivedKeyset.getAt(0).getKey().getParameters()) 345 .isEqualTo(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256); 346 Key expectedKey = 347 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 348 .setParameters(PredefinedAeadParameters.AES128_CTR_HMAC_SHA256) 349 .setIdRequirement(24680) 350 .setAesKeyBytes(secretBytesFromHex("94e397d674deda6e965295698491a3fe")) 351 .setHmacKeyBytes( 352 secretBytesFromHex( 353 "b69838a35f1d48143f3c4cbad90eeb249c8ddea6d09adc5f89a9a190122b095d")) 354 .build(); 355 356 KeysetHandle expectedKeyset = 357 KeysetHandle.newBuilder() 358 .addEntry(KeysetHandle.importKey(expectedKey).makePrimary()) 359 .build(); 360 assertTrue(derivedKeyset.equalsKeyset(expectedKeyset)); 361 } 362 363 @Test testDeriveKey_24byteAes_throws()364 public void testDeriveKey_24byteAes_throws() throws Exception { 365 KeysetHandle derivationHandle = 366 KeysetHandle.newBuilder() 367 .addEntry( 368 KeysetHandle.importKey( 369 createDerivationKey( 370 AesCtrHmacAeadParameters.builder() 371 .setAesKeySizeBytes(24) 372 .setHmacKeySizeBytes(32) 373 .setTagSizeBytes(16) 374 .setIvSizeBytes(16) 375 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 376 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 377 .build(), 378 24680)) 379 .makePrimary()) 380 .build(); 381 // TODO(tholenst): This should throw. 382 Object unused = 383 derivationHandle 384 .getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class) 385 .deriveKeyset(Hex.decode("000102")); 386 } 387 388 @Test testNewKey_validationHappens_throws()389 public void testNewKey_validationHappens_throws() throws Exception { 390 AesCtrHmacAeadParameters rejectedParameters = 391 AesCtrHmacAeadParameters.builder() 392 .setAesKeySizeBytes(24) 393 .setHmacKeySizeBytes(32) 394 .setIvSizeBytes(16) 395 .setTagSizeBytes(32) 396 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 397 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 398 .build(); 399 assertThrows( 400 GeneralSecurityException.class, () -> KeysetHandle.generateNew(rejectedParameters)); 401 } 402 403 @Test testGetPrimitive_validationHappens_throws()404 public void testGetPrimitive_validationHappens_throws() throws Exception { 405 AesCtrHmacAeadParameters rejectedParameters = 406 AesCtrHmacAeadParameters.builder() 407 .setAesKeySizeBytes(24) 408 .setHmacKeySizeBytes(32) 409 .setIvSizeBytes(16) 410 .setTagSizeBytes(32) 411 .setHashType(AesCtrHmacAeadParameters.HashType.SHA256) 412 .setVariant(AesCtrHmacAeadParameters.Variant.TINK) 413 .build(); 414 com.google.crypto.tink.aead.AesCtrHmacAeadKey rejectedKey = 415 com.google.crypto.tink.aead.AesCtrHmacAeadKey.builder() 416 .setParameters(rejectedParameters) 417 .setIdRequirement(123456) 418 .setAesKeyBytes(SecretBytes.randomBytes(rejectedParameters.getAesKeySizeBytes())) 419 .setHmacKeyBytes(SecretBytes.randomBytes(rejectedParameters.getHmacKeySizeBytes())) 420 .build(); 421 assertThrows( 422 GeneralSecurityException.class, 423 () -> 424 KeysetHandle.newBuilder() 425 .addEntry(KeysetHandle.importKey(rejectedKey).makePrimary()) 426 .build() 427 .getPrimitive(RegistryConfiguration.get(), Aead.class)); 428 } 429 430 @Test serializeAndParse_works()431 public void serializeAndParse_works() throws Exception { 432 Parameters p = AesCtrHmacAeadKeyManager.aes256CtrHmacSha256Template().toParameters(); 433 KeysetHandle handle = KeysetHandle.generateNew(p); 434 435 byte[] serialized = 436 TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()); 437 KeysetHandle parsed = 438 TinkProtoKeysetFormat.parseKeyset(serialized, InsecureSecretKeyAccess.get()); 439 assertThat(parsed.equalsKeyset(handle)).isTrue(); 440 } 441 } 442