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