• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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