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 package com.google.crypto.tink.tinkkey; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.crypto.tink.internal.KeyTemplateProtoConverter.getOutputPrefixType; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.common.truth.Expect; 23 import com.google.crypto.tink.KeyManager; 24 import com.google.crypto.tink.KeyTemplate; 25 import com.google.crypto.tink.KeyTemplates; 26 import com.google.crypto.tink.PrivateKeyManager; 27 import com.google.crypto.tink.TinkProtoParametersFormat; 28 import com.google.crypto.tink.aead.AesEaxKeyManager; 29 import com.google.crypto.tink.aead.AesEaxParameters; 30 import com.google.crypto.tink.internal.KeyManagerRegistry; 31 import com.google.crypto.tink.proto.AesEaxKey; 32 import com.google.crypto.tink.proto.KeyData; 33 import com.google.crypto.tink.signature.Ed25519PrivateKeyManager; 34 import com.google.crypto.tink.tinkkey.internal.ProtoKey; 35 import com.google.errorprone.annotations.Immutable; 36 import com.google.protobuf.ByteString; 37 import com.google.protobuf.ExtensionRegistryLite; 38 import com.google.protobuf.InvalidProtocolBufferException; 39 import java.security.GeneralSecurityException; 40 import java.util.Set; 41 import java.util.TreeSet; 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.JUnit4; 47 48 /** Tests for KeyHandle * */ 49 @RunWith(JUnit4.class) 50 public final class KeyHandleTest { newKeyData(com.google.crypto.tink.KeyTemplate keyTemplate)51 private static KeyData newKeyData(com.google.crypto.tink.KeyTemplate keyTemplate) 52 throws GeneralSecurityException { 53 try { 54 byte[] serializedKeyTemplate = 55 TinkProtoParametersFormat.serialize(keyTemplate.toParameters()); 56 com.google.crypto.tink.proto.KeyTemplate protoTemplate = 57 com.google.crypto.tink.proto.KeyTemplate.parseFrom( 58 serializedKeyTemplate, ExtensionRegistryLite.getEmptyRegistry()); 59 KeyManager<?> manager = 60 KeyManagerRegistry.globalInstance().getUntypedKeyManager(protoTemplate.getTypeUrl()); 61 if (KeyManagerRegistry.globalInstance().isNewKeyAllowed(protoTemplate.getTypeUrl())) { 62 return manager.newKeyData(protoTemplate.getValue()); 63 } else { 64 throw new GeneralSecurityException( 65 "newKey-operation not permitted for key type " + protoTemplate.getTypeUrl()); 66 } 67 } catch (InvalidProtocolBufferException e) { 68 throw new GeneralSecurityException("Failed to parse serialized parameters", e); 69 } 70 } 71 getPublicKeyData(String typeUrl, ByteString serializedPrivateKey)72 private static KeyData getPublicKeyData(String typeUrl, ByteString serializedPrivateKey) 73 throws GeneralSecurityException { 74 KeyManager<?> manager = KeyManagerRegistry.globalInstance().getUntypedKeyManager(typeUrl); 75 76 if (!(manager instanceof PrivateKeyManager)) { 77 throw new GeneralSecurityException( 78 "manager for key type " + typeUrl + " is not a PrivateKeyManager"); 79 } 80 return ((PrivateKeyManager) manager).getPublicKeyData(serializedPrivateKey); 81 } 82 83 @Rule public final Expect expect = Expect.create(); 84 85 @Immutable 86 static final class DummyTinkKey implements TinkKey { 87 private final boolean hasSecret; 88 private final KeyTemplate template; 89 DummyTinkKey(boolean hasSecret)90 public DummyTinkKey(boolean hasSecret) { 91 this.hasSecret = hasSecret; 92 this.template = null; 93 } 94 DummyTinkKey(boolean hasSecret, KeyTemplate template)95 public DummyTinkKey(boolean hasSecret, KeyTemplate template) { 96 this.hasSecret = hasSecret; 97 this.template = template; 98 } 99 100 @Override hasSecret()101 public boolean hasSecret() { 102 return hasSecret; 103 } 104 105 @Override getKeyTemplate()106 public KeyTemplate getKeyTemplate() { 107 if (template == null) { 108 throw new UnsupportedOperationException(); 109 } 110 return template; 111 } 112 } 113 114 @Before setUp()115 public void setUp() throws Exception { 116 AesEaxKeyManager.register(/* newKeyAllowed= */ true); 117 Ed25519PrivateKeyManager.registerPair(/* newKeyAllowed= */ true); 118 } 119 120 @Test createFromKey_tinkKeyWithSecret_noSecretKeyAccess_shouldThrowException()121 public void createFromKey_tinkKeyWithSecret_noSecretKeyAccess_shouldThrowException() 122 throws Exception { 123 TinkKey key = new DummyTinkKey(/* hasSecret= */ true); 124 KeyAccess access = KeyAccess.publicAccess(); 125 126 assertThrows(GeneralSecurityException.class, () -> KeyHandle.createFromKey(key, access)); 127 } 128 129 @Test createFromKey_keyDataSymmetric_shouldHaveSecret()130 public void createFromKey_keyDataSymmetric_shouldHaveSecret() throws Exception { 131 KeyTemplate kt = KeyTemplates.get("AES128_EAX"); 132 KeyData kd = newKeyData(kt); 133 134 KeyHandle kh = KeyHandle.createFromKey(kd, getOutputPrefixType(kt)); 135 136 assertThat(kh.hasSecret()).isTrue(); 137 } 138 139 @Test createFromKey_keyDataAsymmetricPrivate_shouldHaveSecret()140 public void createFromKey_keyDataAsymmetricPrivate_shouldHaveSecret() throws Exception { 141 KeyTemplate kt = KeyTemplates.get("ED25519"); 142 KeyData kd = newKeyData(kt); 143 144 KeyHandle kh = KeyHandle.createFromKey(kd, getOutputPrefixType(kt)); 145 146 assertThat(kh.hasSecret()).isTrue(); 147 } 148 149 @Test createFromKey_keyDataUnknown_shouldHaveSecret()150 public void createFromKey_keyDataUnknown_shouldHaveSecret() throws Exception { 151 KeyTemplate kt = KeyTemplates.get("ED25519"); 152 KeyData kd = 153 newKeyData(kt).toBuilder() 154 .setKeyMaterialType(KeyData.KeyMaterialType.UNKNOWN_KEYMATERIAL) 155 .build(); 156 157 KeyHandle kh = KeyHandle.createFromKey(kd, getOutputPrefixType(kt)); 158 159 assertThat(kh.hasSecret()).isTrue(); 160 } 161 162 @Test createFromKey_keyDataAsymmetricPublic_shouldNotHaveSecret()163 public void createFromKey_keyDataAsymmetricPublic_shouldNotHaveSecret() throws Exception { 164 KeyTemplate kt = KeyTemplates.get("ED25519"); 165 KeyData privateKeyData = newKeyData(kt); 166 167 KeyData kd = getPublicKeyData(privateKeyData.getTypeUrl(), privateKeyData.getValue()); 168 169 KeyHandle kh = KeyHandle.createFromKey(kd, getOutputPrefixType(kt)); 170 171 assertThat(kh.hasSecret()).isFalse(); 172 } 173 174 @Test createFromKey_keyDataRemote_shouldNotHaveSecret()175 public void createFromKey_keyDataRemote_shouldNotHaveSecret() throws Exception { 176 KeyTemplate kt = KeyTemplates.get("ED25519"); 177 KeyData kd = 178 newKeyData(kt).toBuilder().setKeyMaterialType(KeyData.KeyMaterialType.REMOTE).build(); 179 180 KeyHandle kh = KeyHandle.createFromKey(kd, getOutputPrefixType(kt)); 181 182 assertThat(kh.hasSecret()).isFalse(); 183 } 184 185 @Test generateNew_shouldWork()186 public void generateNew_shouldWork() throws Exception { 187 KeyTemplate template = KeyTemplates.get("AES128_EAX"); 188 189 KeyHandle handle = KeyHandle.generateNew(template); 190 191 ProtoKey protoKey = (ProtoKey) handle.getKey(SecretKeyAccess.insecureSecretAccess()); 192 expect.that(protoKey.getOutputPrefixType()).isEqualTo(KeyTemplate.OutputPrefixType.TINK); 193 expect.that(protoKey.hasSecret()).isTrue(); 194 KeyData keyData = protoKey.getProtoKey(); 195 expect.that(keyData.getTypeUrl()).isEqualTo("type.googleapis.com/google.crypto.tink.AesEaxKey"); 196 197 AesEaxParameters parameters = (AesEaxParameters) template.toParameters(); 198 199 AesEaxKey aesEaxKey = 200 AesEaxKey.parseFrom(keyData.getValue(), ExtensionRegistryLite.getEmptyRegistry()); 201 expect.that(aesEaxKey.getKeyValue().size()).isEqualTo(parameters.getKeySizeBytes()); 202 } 203 204 @Test generateNew_compareWith_createFromKeyViaProtoKey_shouldBeEqual()205 public void generateNew_compareWith_createFromKeyViaProtoKey_shouldBeEqual() throws Exception { 206 KeyTemplate template = KeyTemplates.get("AES128_EAX"); 207 KeyData keyData = newKeyData(template); 208 ProtoKey protoKey = new ProtoKey(keyData, KeyTemplate.OutputPrefixType.TINK); 209 210 KeyHandle handle1 = KeyHandle.generateNew(template); 211 KeyHandle handle2 = KeyHandle.createFromKey(protoKey, SecretKeyAccess.insecureSecretAccess()); 212 213 expect.that(handle1.getStatus()).isEqualTo(handle2.getStatus()); 214 ProtoKey outputProtoKey1 = (ProtoKey) handle1.getKey(SecretKeyAccess.insecureSecretAccess()); 215 ProtoKey outputProtoKey2 = (ProtoKey) handle2.getKey(SecretKeyAccess.insecureSecretAccess()); 216 expect 217 .that(outputProtoKey1.getOutputPrefixType()) 218 .isEqualTo(outputProtoKey2.getOutputPrefixType()); 219 expect.that(handle1.hasSecret()).isEqualTo(handle2.hasSecret()); 220 } 221 222 @Test generateNew_generatesDifferentKeys()223 public void generateNew_generatesDifferentKeys() throws Exception { 224 KeyTemplate template = KeyTemplates.get("AES128_EAX"); 225 Set<String> keys = new TreeSet<>(); 226 227 int numKeys = 2; 228 for (int j = 0; j < numKeys; j++) { 229 KeyHandle handle = KeyHandle.generateNew(template); 230 ProtoKey protoKey = (ProtoKey) handle.getKey(SecretKeyAccess.insecureSecretAccess()); 231 KeyData keyData = protoKey.getProtoKey(); 232 AesEaxKey aesEaxKey = 233 AesEaxKey.parseFrom(keyData.getValue(), ExtensionRegistryLite.getEmptyRegistry()); 234 keys.add(aesEaxKey.getKeyValue().toStringUtf8()); 235 } 236 237 assertThat(keys).hasSize(numKeys); 238 } 239 240 @Test hasSecret_tinkKeyWithSecret_shouldReturnTrue()241 public void hasSecret_tinkKeyWithSecret_shouldReturnTrue() throws Exception { 242 TinkKey key = new DummyTinkKey(/* hasSecret= */ true); 243 KeyHandle kh = KeyHandle.createFromKey(key, SecretKeyAccess.insecureSecretAccess()); 244 245 assertThat(kh.hasSecret()).isTrue(); 246 } 247 248 @Test hasSecret_tinkKeyWithoutSecret_shouldReturnFalse()249 public void hasSecret_tinkKeyWithoutSecret_shouldReturnFalse() throws Exception { 250 TinkKey key = new DummyTinkKey(/* hasSecret= */ false); 251 KeyAccess access = KeyAccess.publicAccess(); 252 KeyHandle kh = KeyHandle.createFromKey(key, access); 253 254 assertThat(kh.hasSecret()).isFalse(); 255 } 256 257 @Test getKey_tinkKeyWithoutSecret_noSecretKeyAccess_shouldWork()258 public void getKey_tinkKeyWithoutSecret_noSecretKeyAccess_shouldWork() throws Exception { 259 TinkKey key = new DummyTinkKey(/* hasSecret= */ false); 260 KeyAccess access = KeyAccess.publicAccess(); 261 KeyHandle kh = KeyHandle.createFromKey(key, access); 262 263 assertThat(kh.getKey(access)).isEqualTo(key); 264 } 265 266 @Test getKey_tinkKeyWithoutSecret_secretKeyAccess_shouldWork()267 public void getKey_tinkKeyWithoutSecret_secretKeyAccess_shouldWork() throws Exception { 268 TinkKey key = new DummyTinkKey(/* hasSecret= */ false); 269 KeyAccess access = SecretKeyAccess.insecureSecretAccess(); 270 KeyHandle kh = KeyHandle.createFromKey(key, access); 271 272 assertThat(kh.getKey(access)).isEqualTo(key); 273 } 274 275 @Test getKey_tinkKeyWithSecret_noSecretKeyAccess_shouldThrowException()276 public void getKey_tinkKeyWithSecret_noSecretKeyAccess_shouldThrowException() throws Exception { 277 TinkKey key = new DummyTinkKey(/* hasSecret= */ true); 278 KeyHandle kh = KeyHandle.createFromKey(key, SecretKeyAccess.insecureSecretAccess()); 279 KeyAccess pubAccess = KeyAccess.publicAccess(); 280 281 assertThrows(GeneralSecurityException.class, () -> kh.getKey(pubAccess)); 282 } 283 284 @Test getKey_tinkKeyWithSecret_secretKeyAccess_shouldWork()285 public void getKey_tinkKeyWithSecret_secretKeyAccess_shouldWork() throws Exception { 286 TinkKey key = new DummyTinkKey(/* hasSecret= */ true); 287 KeyAccess access = SecretKeyAccess.insecureSecretAccess(); 288 KeyHandle kh = KeyHandle.createFromKey(key, access); 289 290 assertThat(kh.getKey(access)).isEqualTo(key); 291 } 292 293 @Test getKeyTemplate()294 public void getKeyTemplate() throws Exception { 295 KeyTemplate keyTemplate = KeyTemplates.get("ED25519_RAW"); 296 TinkKey key = new DummyTinkKey(/* hasSecret= */ false, keyTemplate); 297 KeyHandle keyHandle = KeyHandle.createFromKey(key, KeyAccess.publicAccess()); 298 299 KeyTemplate returnedKeyTemplate = keyHandle.getKeyTemplate(); 300 301 assertThat(returnedKeyTemplate.toParameters()).isEqualTo(keyTemplate.toParameters()); 302 } 303 304 @Test getKeyTemplate_tinkKeyWithoutKeyTemplateSupport_shouldThrow()305 public void getKeyTemplate_tinkKeyWithoutKeyTemplateSupport_shouldThrow() throws Exception { 306 TinkKey key = new DummyTinkKey(/* hasSecret= */ false); 307 KeyHandle keyHandle = KeyHandle.createFromKey(key, KeyAccess.publicAccess()); 308 309 assertThrows(UnsupportedOperationException.class, keyHandle::getKeyTemplate); 310 } 311 } 312