1 // Copyright 2020 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.prf; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug; 21 import static org.junit.Assert.assertThrows; 22 import static org.junit.Assert.assertTrue; 23 24 import com.google.crypto.tink.InsecureSecretKeyAccess; 25 import com.google.crypto.tink.KeyTemplate; 26 import com.google.crypto.tink.KeyTemplates; 27 import com.google.crypto.tink.KeysetHandle; 28 import com.google.crypto.tink.Parameters; 29 import com.google.crypto.tink.RegistryConfiguration; 30 import com.google.crypto.tink.TinkProtoKeysetFormat; 31 import com.google.crypto.tink.aead.AeadConfig; 32 import com.google.crypto.tink.aead.PredefinedAeadParameters; 33 import com.google.crypto.tink.internal.KeyManagerRegistry; 34 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 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.subtle.Hex; 40 import com.google.crypto.tink.subtle.Random; 41 import com.google.crypto.tink.subtle.prf.HkdfStreamingPrf; 42 import com.google.crypto.tink.subtle.prf.PrfImpl; 43 import com.google.crypto.tink.subtle.prf.StreamingPrf; 44 import com.google.crypto.tink.util.Bytes; 45 import com.google.crypto.tink.util.SecretBytes; 46 import java.security.GeneralSecurityException; 47 import java.util.Set; 48 import java.util.TreeSet; 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 HkdfPrfKeyManager. */ 58 @RunWith(Theories.class) 59 public class HkdfPrfKeyManagerTest { 60 @Before register()61 public void register() throws Exception { 62 PrfConfig.register(); 63 KeyDerivationConfig.register(); 64 AeadConfig.register(); 65 } 66 67 @Test testHkdfSha256Template()68 public void testHkdfSha256Template() throws Exception { 69 KeyTemplate kt = HkdfPrfKeyManager.hkdfSha256Template(); 70 assertThat(kt.toParameters()) 71 .isEqualTo( 72 HkdfPrfParameters.builder() 73 .setKeySizeBytes(32) 74 .setHashType(HkdfPrfParameters.HashType.SHA256) 75 .setSalt(Bytes.copyFrom(new byte[] {})) 76 .build()); 77 } 78 79 @Test testKeyTemplateAndManagerCompatibility()80 public void testKeyTemplateAndManagerCompatibility() throws Exception { 81 Parameters p = HkdfPrfKeyManager.hkdfSha256Template().toParameters(); 82 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 83 } 84 85 @DataPoints("templateNames") 86 public static final String[] KEY_TEMPLATES = 87 new String[] { 88 "HKDF_SHA256", 89 }; 90 91 @Theory testTemplates(@romDataPoints"templateNames") String templateName)92 public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception { 93 KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName)); 94 assertThat(h.size()).isEqualTo(1); 95 assertThat(h.getAt(0).getKey().getParameters()) 96 .isEqualTo(KeyTemplates.get(templateName).toParameters()); 97 } 98 99 @Test register_registersPrfPrimitiveConstructor()100 public void register_registersPrfPrimitiveConstructor() throws Exception { 101 HkdfPrfParameters hkdfPrfParameters = 102 HkdfPrfParameters.builder() 103 .setHashType(HkdfPrfParameters.HashType.SHA256) 104 .setKeySizeBytes(32) 105 .setSalt(Bytes.copyFrom(Random.randBytes(5))) 106 .build(); 107 com.google.crypto.tink.prf.HkdfPrfKey hkdfPrfKey = 108 com.google.crypto.tink.prf.HkdfPrfKey.builder() 109 .setParameters(hkdfPrfParameters) 110 .setKeyBytes(SecretBytes.copyFrom(Random.randBytes(32), InsecureSecretKeyAccess.get())) 111 .build(); 112 113 Prf prf = MutablePrimitiveRegistry.globalInstance().getPrimitive(hkdfPrfKey, Prf.class); 114 115 assertThat(prf).isNotNull(); 116 } 117 118 @Test register_registersStreamingPrfPrimitiveConstructor()119 public void register_registersStreamingPrfPrimitiveConstructor() throws Exception { 120 HkdfPrfParameters hkdfPrfParameters = 121 HkdfPrfParameters.builder() 122 .setHashType(HkdfPrfParameters.HashType.SHA256) 123 .setKeySizeBytes(32) 124 .setSalt(Bytes.copyFrom(Random.randBytes(5))) 125 .build(); 126 com.google.crypto.tink.prf.HkdfPrfKey hkdfPrfKey = 127 com.google.crypto.tink.prf.HkdfPrfKey.builder() 128 .setParameters(hkdfPrfParameters) 129 .setKeyBytes(SecretBytes.copyFrom(Random.randBytes(32), InsecureSecretKeyAccess.get())) 130 .build(); 131 132 StreamingPrf streamingPrf = 133 MutablePrimitiveRegistry.globalInstance().getPrimitive(hkdfPrfKey, StreamingPrf.class); 134 135 assertThat(streamingPrf).isNotNull(); 136 } 137 138 @Test testKeyManagerRegistered()139 public void testKeyManagerRegistered() throws Exception { 140 assertThat( 141 KeyManagerRegistry.globalInstance() 142 .getKeyManager("type.googleapis.com/google.crypto.tink.HkdfPrfKey", Prf.class)) 143 .isNotNull(); 144 } 145 146 @Test createKey_works()147 public void createKey_works() throws Exception { 148 HkdfPrfParameters params = 149 HkdfPrfParameters.builder() 150 .setHashType(HkdfPrfParameters.HashType.SHA256) 151 .setKeySizeBytes(32) 152 .build(); 153 KeysetHandle handle = KeysetHandle.generateNew(params); 154 assertThat(handle.size()).isEqualTo(1); 155 com.google.crypto.tink.prf.HkdfPrfKey key = 156 (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey(); 157 assertThat(key.getParameters()).isEqualTo(params); 158 } 159 160 @Test createKey_otherParams_works()161 public void createKey_otherParams_works() throws Exception { 162 HkdfPrfParameters params = 163 HkdfPrfParameters.builder() 164 .setHashType(HkdfPrfParameters.HashType.SHA512) 165 .setKeySizeBytes(32) 166 .build(); 167 KeysetHandle handle = KeysetHandle.generateNew(params); 168 assertThat(handle.size()).isEqualTo(1); 169 com.google.crypto.tink.prf.HkdfPrfKey key = 170 (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey(); 171 assertThat(key.getParameters()).isEqualTo(params); 172 } 173 174 @Test createKey_differentKeyValues_alwaysDifferent()175 public void createKey_differentKeyValues_alwaysDifferent() throws Exception { 176 HkdfPrfParameters params = 177 HkdfPrfParameters.builder() 178 .setHashType(HkdfPrfParameters.HashType.SHA512) 179 .setKeySizeBytes(32) 180 .build(); 181 182 int numKeys = 100; 183 Set<String> keys = new TreeSet<>(); 184 for (int i = 0; i < numKeys; i++) { 185 KeysetHandle handle = KeysetHandle.generateNew(params); 186 assertThat(handle.size()).isEqualTo(1); 187 com.google.crypto.tink.prf.HkdfPrfKey key = 188 (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey(); 189 keys.add(Hex.encode(key.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get()))); 190 } 191 assertThat(keys).hasSize(numKeys); 192 } 193 194 @Test createPrimitiveAndUseIt_works()195 public void createPrimitiveAndUseIt_works() throws Exception { 196 HkdfPrfParameters params = 197 HkdfPrfParameters.builder() 198 .setHashType(HkdfPrfParameters.HashType.SHA512) 199 .setKeySizeBytes(32) 200 .build(); 201 KeysetHandle handle = KeysetHandle.generateNew(params); 202 assertThat(handle.size()).isEqualTo(1); 203 PrfSet prfSet = handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 204 Prf directPrf = 205 PrfImpl.wrap( 206 HkdfStreamingPrf.create( 207 (com.google.crypto.tink.prf.HkdfPrfKey) handle.getAt(0).getKey())); 208 assertThat(prfSet.computePrimary(new byte[0], 16)) 209 .isEqualTo(directPrf.compute(new byte[0], 16)); 210 } 211 212 @Test serializeAndDeserializeKeysets()213 public void serializeAndDeserializeKeysets() throws Exception { 214 HkdfPrfParameters params = 215 HkdfPrfParameters.builder() 216 .setHashType(HkdfPrfParameters.HashType.SHA512) 217 .setKeySizeBytes(32) 218 .build(); 219 KeysetHandle handle = KeysetHandle.generateNew(params); 220 221 byte[] serializedKeyset = 222 TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()); 223 KeysetHandle parsed = 224 TinkProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get()); 225 assertTrue(parsed.equalsKeyset(handle)); 226 } 227 228 @Theory createKeyWithRejectedParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)229 public void createKeyWithRejectedParameters_throws( 230 @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception { 231 assertThrows(GeneralSecurityException.class, () -> KeysetHandle.generateNew(params)); 232 } 233 234 @Theory createPrimitiveWithRejectedParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)235 public void createPrimitiveWithRejectedParameters_throws( 236 @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception { 237 com.google.crypto.tink.prf.HkdfPrfKey key = 238 com.google.crypto.tink.prf.HkdfPrfKey.builder() 239 .setParameters(params) 240 .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes())) 241 .build(); 242 KeysetHandle handle = 243 KeysetHandle.newBuilder() 244 .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary()) 245 .build(); 246 assertThrows( 247 GeneralSecurityException.class, 248 () -> handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class)); 249 } 250 251 /** We allow serialization and deserialization with parameters which are otherwise rejected */ 252 @Theory serializeDeserializeKeysetsWithRejectedParams_works( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)253 public void serializeDeserializeKeysetsWithRejectedParams_works( 254 @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception { 255 com.google.crypto.tink.prf.HkdfPrfKey key = 256 com.google.crypto.tink.prf.HkdfPrfKey.builder() 257 .setParameters(params) 258 .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes())) 259 .build(); 260 KeysetHandle handle = 261 KeysetHandle.newBuilder() 262 .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary()) 263 .build(); 264 byte[] serializedKeyset = 265 TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()); 266 KeysetHandle parsed = 267 TinkProtoKeysetFormat.parseKeyset(serializedKeyset, InsecureSecretKeyAccess.get()); 268 assertTrue(parsed.equalsKeyset(handle)); 269 } 270 271 @Theory deriveAesGcmKey_withInvalidPrfParameters_throws( @romDataPoints"KeyManager rejected") HkdfPrfParameters params)272 public void deriveAesGcmKey_withInvalidPrfParameters_throws( 273 @FromDataPoints("KeyManager rejected") HkdfPrfParameters params) throws Exception { 274 PrfKey prfKeyForDeriver = 275 HkdfPrfKey.builder() 276 .setParameters(params) 277 .setKeyBytes(SecretBytes.randomBytes(params.getKeySizeBytes())) 278 .build(); 279 PrfBasedKeyDerivationParameters derivationParameters = 280 PrfBasedKeyDerivationParameters.builder() 281 .setDerivedKeyParameters(PredefinedAeadParameters.AES128_GCM) 282 .setPrfParameters(prfKeyForDeriver.getParameters()) 283 .build(); 284 PrfBasedKeyDerivationKey key = 285 PrfBasedKeyDerivationKey.create( 286 derivationParameters, prfKeyForDeriver, /* idRequirement= */ 112233); 287 288 KeysetHandle keyset = 289 KeysetHandle.newBuilder() 290 .addEntry(KeysetHandle.importKey(key).withFixedId(112233).makePrimary()) 291 .build(); 292 assertThrows( 293 GeneralSecurityException.class, 294 () -> keyset.getPrimitive(RegistryConfiguration.get(), KeysetDeriver.class)); 295 } 296 createRejectedParameters()297 private static HkdfPrfParameters[] createRejectedParameters() { 298 return exceptionIsBug( 299 () -> 300 new HkdfPrfParameters[] { 301 // Key Size 16 is rejected 302 HkdfPrfParameters.builder() 303 .setHashType(HkdfPrfParameters.HashType.SHA512) 304 .setKeySizeBytes(16) 305 .build(), 306 // Only SHA256 and SHA512 are accepted 307 HkdfPrfParameters.builder() 308 .setHashType(HkdfPrfParameters.HashType.SHA1) 309 .setKeySizeBytes(32) 310 .build(), 311 HkdfPrfParameters.builder() 312 .setHashType(HkdfPrfParameters.HashType.SHA224) 313 .setKeySizeBytes(32) 314 .build(), 315 HkdfPrfParameters.builder() 316 .setHashType(HkdfPrfParameters.HashType.SHA384) 317 .setKeySizeBytes(32) 318 .build() 319 }); 320 } 321 322 @DataPoints("KeyManager rejected") 323 public static final HkdfPrfParameters[] RECJECTED_PARAMETERS = createRejectedParameters(); 324 } 325