• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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.internal;
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.assertEquals;
22 import static org.junit.Assert.assertThrows;
23 
24 import com.google.crypto.tink.Aead;
25 import com.google.crypto.tink.CryptoFormat;
26 import com.google.crypto.tink.InsecureSecretKeyAccess;
27 import com.google.crypto.tink.Mac;
28 import com.google.crypto.tink.aead.AesGcmKey;
29 import com.google.crypto.tink.aead.PredefinedAeadParameters;
30 import com.google.crypto.tink.mac.HmacKey;
31 import com.google.crypto.tink.mac.HmacKeyManager;
32 import com.google.crypto.tink.proto.HashType;
33 import com.google.crypto.tink.proto.HmacParams;
34 import com.google.crypto.tink.proto.KeyData;
35 import com.google.crypto.tink.proto.KeyStatusType;
36 import com.google.crypto.tink.proto.Keyset;
37 import com.google.crypto.tink.proto.Keyset.Key;
38 import com.google.crypto.tink.proto.OutputPrefixType;
39 import com.google.crypto.tink.subtle.AesGcmJce;
40 import com.google.crypto.tink.subtle.Hex;
41 import com.google.crypto.tink.testing.TestUtil;
42 import com.google.crypto.tink.util.SecretBytes;
43 import com.google.protobuf.ByteString;
44 import java.security.GeneralSecurityException;
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.List;
48 import javax.annotation.Nullable;
49 import org.junit.BeforeClass;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.junit.runners.JUnit4;
53 
54 /** Tests for PrimitiveSet. */
55 @RunWith(JUnit4.class)
56 public class PrimitiveSetTest {
57 
58   private static class DummyMac1 implements Mac {
DummyMac1()59     public DummyMac1() {}
60 
61     @Override
computeMac(byte[] data)62     public byte[] computeMac(byte[] data) throws GeneralSecurityException {
63       return this.getClass().getSimpleName().getBytes(UTF_8);
64     }
65 
66     @Override
verifyMac(byte[] mac, byte[] data)67     public void verifyMac(byte[] mac, byte[] data) throws GeneralSecurityException {
68       return;
69     }
70   }
71 
72   private static class DummyMac2 implements Mac {
DummyMac2()73     public DummyMac2() {}
74 
75     @Override
computeMac(byte[] data)76     public byte[] computeMac(byte[] data) throws GeneralSecurityException {
77       return this.getClass().getSimpleName().getBytes(UTF_8);
78     }
79 
80     @Override
verifyMac(byte[] mac, byte[] data)81     public void verifyMac(byte[] mac, byte[] data) throws GeneralSecurityException {
82       return;
83     }
84   }
85 
86   @BeforeClass
setUp()87   public static void setUp() throws GeneralSecurityException {
88     HmacKeyManager.register(true);
89   }
90 
getKeyFromProtoKey(Key key)91   com.google.crypto.tink.Key getKeyFromProtoKey(Key key) throws GeneralSecurityException {
92     @Nullable Integer idRequirement = key.getKeyId();
93     if (key.getOutputPrefixType() == OutputPrefixType.RAW) {
94       idRequirement = null;
95     }
96     return MutableSerializationRegistry.globalInstance()
97         .parseKeyWithLegacyFallback(
98             ProtoKeySerialization.create(
99                 key.getKeyData().getTypeUrl(),
100                 key.getKeyData().getValue(),
101                 key.getKeyData().getKeyMaterialType(),
102                 key.getOutputPrefixType(),
103                 idRequirement),
104             InsecureSecretKeyAccess.get());
105   }
106 
107   @Test
primitiveSetWithOneEntry_works()108   public void primitiveSetWithOneEntry_works() throws Exception {
109     byte[] keyMaterial = Hex.decode("000102030405060708090a0b0c0d0e0f");
110     AesGcmKey key =
111         AesGcmKey.builder()
112             .setParameters(PredefinedAeadParameters.AES128_GCM)
113             .setKeyBytes(SecretBytes.copyFrom(keyMaterial, InsecureSecretKeyAccess.get()))
114             .setIdRequirement(42)
115             .build();
116     Aead fullPrimitive = AesGcmJce.create(key);
117     Keyset.Key protoKey =
118         TestUtil.createKey(
119             TestUtil.createAesGcmKeyData(keyMaterial),
120             42,
121             KeyStatusType.ENABLED,
122             OutputPrefixType.TINK);
123     PrimitiveSet<Aead> pset =
124         PrimitiveSet.newBuilder(Aead.class)
125             .addPrimaryFullPrimitive(fullPrimitive, key, protoKey)
126             .build();
127     assertThat(pset.getAll()).hasSize(1);
128     List<PrimitiveSet.Entry<Aead>> entries =
129         pset.getPrimitive(CryptoFormat.getOutputPrefix(protoKey));
130     assertThat(entries).hasSize(1);
131     PrimitiveSet.Entry<Aead> entry = entries.get(0);
132     assertThat(entry.getFullPrimitive()).isEqualTo(fullPrimitive);
133     assertThat(entry.getStatus()).isEqualTo(KeyStatusType.ENABLED);
134     assertThat(entry.getOutputPrefixType()).isEqualTo(OutputPrefixType.TINK);
135     assertThat(entry.getKeyId()).isEqualTo(42);
136     assertThat(entry.getKeyTypeUrl()).isEqualTo("type.googleapis.com/google.crypto.tink.AesGcmKey");
137     assertThat(entry.getKey()).isEqualTo(key);
138   }
139 
140   @Test
testBasicFunctionality()141   public void testBasicFunctionality() throws Exception {
142     Key key1 =
143         Key.newBuilder()
144             .setKeyId(1)
145             .setStatus(KeyStatusType.ENABLED)
146             .setOutputPrefixType(OutputPrefixType.TINK)
147             .build();
148     Key key2 =
149         Key.newBuilder()
150             .setKeyId(2)
151             .setStatus(KeyStatusType.ENABLED)
152             .setOutputPrefixType(OutputPrefixType.RAW)
153             .build();
154     Key key3 =
155         Key.newBuilder()
156             .setKeyId(3)
157             .setStatus(KeyStatusType.ENABLED)
158             .setOutputPrefixType(OutputPrefixType.LEGACY)
159             .build();
160     PrimitiveSet<Mac> pset =
161         PrimitiveSet.newBuilder(Mac.class)
162             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
163             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
164             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
165             .build();
166 
167     assertThat(pset.getAll()).hasSize(3);
168 
169     List<PrimitiveSet.Entry<Mac>> entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key1));
170     assertThat(entries).hasSize(1);
171     PrimitiveSet.Entry<Mac> entry = entries.get(0);
172     assertEquals(
173         DummyMac1.class.getSimpleName(),
174         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
175     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
176     assertEquals(1, entry.getKeyId());
177 
178     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key2));
179     assertThat(entries).hasSize(1);
180     entry = entries.get(0);
181     assertEquals(
182         DummyMac2.class.getSimpleName(),
183         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
184     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
185     assertEquals(2, entry.getKeyId());
186 
187     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key3));
188     assertThat(entries).hasSize(1);
189     entry = entries.get(0);
190     assertEquals(
191         DummyMac1.class.getSimpleName(),
192         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
193     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
194     assertEquals(3, entry.getKeyId());
195 
196     entry = pset.getPrimary();
197     assertEquals(
198         DummyMac2.class.getSimpleName(),
199         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
200     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
201     assertEquals(2, entry.getKeyId());
202   }
203 
204   @Test
testAddFullPrimitive_works()205   public void testAddFullPrimitive_works() throws Exception {
206     Key key1 =
207         Key.newBuilder()
208             .setKeyId(1)
209             .setStatus(KeyStatusType.ENABLED)
210             .setOutputPrefixType(OutputPrefixType.TINK)
211             .build();
212     Key key2 =
213         Key.newBuilder()
214             .setKeyId(2)
215             .setStatus(KeyStatusType.ENABLED)
216             .setOutputPrefixType(OutputPrefixType.RAW)
217             .build();
218     Key key3 =
219         Key.newBuilder()
220             .setKeyId(3)
221             .setStatus(KeyStatusType.ENABLED)
222             .setOutputPrefixType(OutputPrefixType.LEGACY)
223             .build();
224     PrimitiveSet<Mac> pset =
225         PrimitiveSet.newBuilder(Mac.class)
226             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
227             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
228             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
229             .build();
230 
231     assertThat(pset.getAll()).hasSize(3);
232 
233     List<PrimitiveSet.Entry<Mac>> entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key1));
234     assertThat(entries).hasSize(1);
235 
236     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key2));
237     assertThat(entries).hasSize(1);
238 
239     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key3));
240     assertThat(entries).hasSize(1);
241 
242     PrimitiveSet.Entry<Mac> entry = pset.getPrimary();
243     assertThat(entry).isNotNull();
244   }
245 
246   @Test
testAddFullPrimitive_fullPrimitiveHandledCorrectly()247   public void testAddFullPrimitive_fullPrimitiveHandledCorrectly() throws Exception {
248     Key key1 =
249         Key.newBuilder()
250             .setKeyId(1)
251             .setStatus(KeyStatusType.ENABLED)
252             .setOutputPrefixType(OutputPrefixType.TINK)
253             .build();
254     Key key2 =
255         Key.newBuilder()
256             .setKeyId(2)
257             .setStatus(KeyStatusType.ENABLED)
258             .setOutputPrefixType(OutputPrefixType.RAW)
259             .build();
260     Key key3 =
261         Key.newBuilder()
262             .setKeyId(3)
263             .setStatus(KeyStatusType.ENABLED)
264             .setOutputPrefixType(OutputPrefixType.LEGACY)
265             .build();
266     PrimitiveSet<Mac> pset =
267         PrimitiveSet.newBuilder(Mac.class)
268             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
269             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
270             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
271             .build();
272 
273     PrimitiveSet.Entry<Mac> entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key1)).get(0);
274     assertEquals(
275         DummyMac1.class.getSimpleName(),
276         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
277 
278     entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key2)).get(0);
279     assertEquals(
280         DummyMac2.class.getSimpleName(),
281         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
282 
283     entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key3)).get(0);
284     assertEquals(
285         DummyMac1.class.getSimpleName(),
286         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
287 
288     entry = pset.getPrimary();
289     assertEquals(
290         DummyMac2.class.getSimpleName(),
291         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
292   }
293 
294   @Test
testAddFullPrimitive_keysHandledCorrectly()295   public void testAddFullPrimitive_keysHandledCorrectly() throws Exception {
296     Key key1 =
297         Key.newBuilder()
298             .setKeyId(1)
299             .setStatus(KeyStatusType.ENABLED)
300             .setOutputPrefixType(OutputPrefixType.TINK)
301             .build();
302     Key key2 =
303         Key.newBuilder()
304             .setKeyId(2)
305             .setStatus(KeyStatusType.ENABLED)
306             .setOutputPrefixType(OutputPrefixType.RAW)
307             .build();
308     Key key3 =
309         Key.newBuilder()
310             .setKeyId(3)
311             .setStatus(KeyStatusType.ENABLED)
312             .setOutputPrefixType(OutputPrefixType.LEGACY)
313             .build();
314     PrimitiveSet<Mac> pset =
315         PrimitiveSet.newBuilder(Mac.class)
316             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
317             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
318             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
319             .build();
320 
321     PrimitiveSet.Entry<Mac> entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key1)).get(0);
322     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
323     assertEquals(1, entry.getKeyId());
324 
325     entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key2)).get(0);
326     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
327     assertEquals(2, entry.getKeyId());
328 
329     entry = pset.getPrimitive(CryptoFormat.getOutputPrefix(key3)).get(0);
330     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
331     assertEquals(3, entry.getKeyId());
332 
333     entry = pset.getPrimary();
334     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
335     assertEquals(2, entry.getKeyId());
336   }
337 
338   @Test
testAddFullPrimitive_throwsOnDoublePrimaryAdd()339   public void testAddFullPrimitive_throwsOnDoublePrimaryAdd() throws Exception {
340     Key key1 =
341         Key.newBuilder()
342             .setKeyId(1)
343             .setStatus(KeyStatusType.ENABLED)
344             .setOutputPrefixType(OutputPrefixType.TINK)
345             .build();
346     Key key2 =
347         Key.newBuilder()
348             .setKeyId(2)
349             .setStatus(KeyStatusType.ENABLED)
350             .setOutputPrefixType(OutputPrefixType.RAW)
351             .build();
352     assertThrows(
353         IllegalStateException.class,
354         () ->
355             PrimitiveSet.newBuilder(Mac.class)
356                 .addPrimaryFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
357                 .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
358                 .build());
359   }
360 
361   @Test
testNoPrimary_getPrimaryReturnsNull()362   public void testNoPrimary_getPrimaryReturnsNull() throws Exception {
363     Key key =
364         Key.newBuilder()
365             .setKeyId(1)
366             .setStatus(KeyStatusType.ENABLED)
367             .setOutputPrefixType(OutputPrefixType.TINK)
368             .build();
369     PrimitiveSet<Mac> pset =
370         PrimitiveSet.newBuilder(Mac.class)
371             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key), key)
372             .build();
373     assertThat(pset.getPrimary()).isNull();
374   }
375 
376   @Test
testEntryGetParametersToString()377   public void testEntryGetParametersToString() throws Exception {
378     Key key1 =
379         Key.newBuilder()
380             .setKeyId(1)
381             .setStatus(KeyStatusType.ENABLED)
382             .setOutputPrefixType(OutputPrefixType.TINK)
383             .setKeyData(KeyData.newBuilder().setTypeUrl("typeUrl1").build())
384             .build();
385 
386     PrimitiveSet<Mac> pset =
387         PrimitiveSet.newBuilder(Mac.class)
388             .addPrimaryFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
389             .build();
390     assertThat(
391             pset.getPrimitive(CryptoFormat.getOutputPrefix(key1)).get(0).getParameters().toString())
392         .isEqualTo("(typeUrl=typeUrl1, outputPrefixType=TINK)");
393   }
394 
395   @Test
getKeyWithoutParser_givesLegacyProtoKey()396   public void getKeyWithoutParser_givesLegacyProtoKey() throws Exception {
397     PrimitiveSet.Builder<Mac> builder = PrimitiveSet.newBuilder(Mac.class);
398     Key key1 =
399         Key.newBuilder()
400             .setKeyId(1)
401             .setStatus(KeyStatusType.ENABLED)
402             .setOutputPrefixType(OutputPrefixType.TINK)
403             .setKeyData(KeyData.newBuilder().setTypeUrl("typeUrl1").build())
404             .build();
405     builder.addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1);
406     PrimitiveSet<Mac> pset = builder.build();
407     com.google.crypto.tink.Key key =
408         pset.getPrimitive(CryptoFormat.getOutputPrefix(key1)).get(0).getKey();
409 
410     assertThat(key).isInstanceOf(LegacyProtoKey.class);
411     LegacyProtoKey legacyProtoKey = (LegacyProtoKey) key;
412     assertThat(legacyProtoKey.getSerialization(InsecureSecretKeyAccess.get()).getTypeUrl())
413         .isEqualTo("typeUrl1");
414   }
415 
416   @Test
getKeyWithParser_works()417   public void getKeyWithParser_works() throws Exception {
418     // HmacKey's proto serialization HmacProtoSerialization is registed in HmacKeyManager.
419     Key protoKey =
420         TestUtil.createKey(
421             TestUtil.createHmacKeyData("01234567890123456".getBytes(UTF_8), 16),
422             /* keyId= */ 42,
423             KeyStatusType.ENABLED,
424             OutputPrefixType.TINK);
425     byte[] prefix = CryptoFormat.getOutputPrefix(protoKey);
426     PrimitiveSet.Builder<Mac> builder = PrimitiveSet.newBuilder(Mac.class);
427     builder.addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(protoKey), protoKey);
428     PrimitiveSet<Mac> pset = builder.build();
429 
430     com.google.crypto.tink.Key key = pset.getPrimitive(prefix).get(0).getKey();
431     assertThat(key).isInstanceOf(HmacKey.class);
432     HmacKey hmacKey = (HmacKey) key;
433     assertThat(hmacKey.getIdRequirementOrNull()).isEqualTo(42);
434   }
435 
436   @Test
addPrimitiveWithInvalidKeyThatHasAParser_throws()437   public void addPrimitiveWithInvalidKeyThatHasAParser_throws() throws Exception {
438     // HmacKey's proto serialization HmacProtoSerialization is registed in HmacKeyManager.
439     com.google.crypto.tink.proto.HmacKey invalidProtoHmacKey =
440         com.google.crypto.tink.proto.HmacKey.newBuilder()
441             .setVersion(999)
442             .setKeyValue(ByteString.copyFromUtf8("01234567890123456"))
443             .setParams(HmacParams.newBuilder().setHash(HashType.UNKNOWN_HASH).setTagSize(0))
444             .build();
445     Key protoKey =
446         TestUtil.createKey(
447             TestUtil.createKeyData(
448                 invalidProtoHmacKey,
449                 "type.googleapis.com/google.crypto.tink.HmacKey",
450                 KeyData.KeyMaterialType.SYMMETRIC),
451             /* keyId= */ 42,
452             KeyStatusType.ENABLED,
453             OutputPrefixType.TINK);
454 
455     PrimitiveSet.Builder<Mac> builder = PrimitiveSet.newBuilder(Mac.class);
456     assertThrows(
457         GeneralSecurityException.class,
458         () -> builder.addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(protoKey), protoKey));
459   }
460 
461   @Test
testWithAnnotations()462   public void testWithAnnotations() throws Exception {
463     MonitoringAnnotations annotations =
464         MonitoringAnnotations.newBuilder().add("name", "value").build();
465     PrimitiveSet<Mac> pset = PrimitiveSet.newBuilder(Mac.class).setAnnotations(annotations).build();
466 
467     HashMap<String, String> expected = new HashMap<>();
468     expected.put("name", "value");
469     assertThat(pset.getAnnotations().toMap()).containsExactlyEntriesIn(expected);
470   }
471 
472   @Test
testGetEmptyAnnotations()473   public void testGetEmptyAnnotations() throws Exception {
474     PrimitiveSet<Mac> pset = PrimitiveSet.newBuilder(Mac.class).build();
475     assertThat(pset.getAnnotations()).isEqualTo(MonitoringAnnotations.EMPTY);
476   }
477 
478   @Test
testDuplicateKeys()479   public void testDuplicateKeys() throws Exception {
480     Key key1 =
481         Key.newBuilder()
482             .setKeyId(1)
483             .setStatus(KeyStatusType.ENABLED)
484             .setOutputPrefixType(OutputPrefixType.TINK)
485             .build();
486     Key key2 =
487         Key.newBuilder()
488             .setKeyId(1)
489             .setStatus(KeyStatusType.ENABLED)
490             .setOutputPrefixType(OutputPrefixType.RAW)
491             .build();
492     Key key3 =
493         Key.newBuilder()
494             .setKeyId(2)
495             .setStatus(KeyStatusType.ENABLED)
496             .setOutputPrefixType(OutputPrefixType.LEGACY)
497             .build();
498     Key key4 =
499         Key.newBuilder()
500             .setKeyId(2)
501             .setStatus(KeyStatusType.ENABLED)
502             .setOutputPrefixType(OutputPrefixType.LEGACY)
503             .build();
504     Key key5 =
505         Key.newBuilder()
506             .setKeyId(3)
507             .setStatus(KeyStatusType.ENABLED)
508             .setOutputPrefixType(OutputPrefixType.RAW)
509             .build();
510     Key key6 =
511         Key.newBuilder()
512             .setKeyId(3)
513             .setStatus(KeyStatusType.ENABLED)
514             .setOutputPrefixType(OutputPrefixType.RAW)
515             .build();
516 
517     PrimitiveSet<Mac> pset =
518         PrimitiveSet.newBuilder(Mac.class)
519             .addFullPrimitive(new DummyMac1(), null, key1)
520             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
521             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
522             .addFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key4), key4)
523             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key5), key5)
524             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key6), key6)
525             .build();
526 
527     assertThat(pset.getAll()).hasSize(3); // 3 instead of 6 because of duplicated key ids
528 
529     // tink keys
530     List<PrimitiveSet.Entry<Mac>> entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key1));
531     assertThat(entries).hasSize(1);
532     PrimitiveSet.Entry<Mac> entry = entries.get(0);
533     assertEquals(
534         DummyMac1.class.getSimpleName(),
535         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
536     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
537     assertEquals(1, entry.getKeyId());
538 
539     // raw keys
540     List<Integer> ids = new ArrayList<>(); // The order of the keys is an implementation detail.
541     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key2));
542     assertThat(entries).hasSize(3);
543     entry = entries.get(0);
544     assertEquals(
545         DummyMac2.class.getSimpleName(),
546         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
547     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
548     ids.add(entry.getKeyId());
549     entry = entries.get(1);
550     assertEquals(
551         DummyMac1.class.getSimpleName(),
552         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
553     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
554     ids.add(entry.getKeyId());
555     entry = entries.get(2);
556     assertEquals(
557         DummyMac1.class.getSimpleName(),
558         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
559     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
560     ids.add(entry.getKeyId());
561 
562     assertThat(ids).containsExactly(1, 3, 3);
563     // legacy keys
564     entries = pset.getPrimitive(CryptoFormat.getOutputPrefix(key3));
565     assertThat(entries).hasSize(2);
566     entry = entries.get(0);
567     assertEquals(
568         DummyMac1.class.getSimpleName(),
569         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
570     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
571     assertEquals(2, entry.getKeyId());
572     entry = entries.get(1);
573     assertEquals(
574         DummyMac2.class.getSimpleName(),
575         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
576     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
577     assertEquals(2, entry.getKeyId());
578 
579     entry = pset.getPrimary();
580     assertEquals(
581         DummyMac2.class.getSimpleName(),
582         new String(entry.getFullPrimitive().computeMac(null), UTF_8));
583     assertEquals(KeyStatusType.ENABLED, entry.getStatus());
584     assertEquals(1, entry.getKeyId());
585   }
586 
587   @Test
testAddFullPrimive_withUnknownPrefixType_shouldFail()588   public void testAddFullPrimive_withUnknownPrefixType_shouldFail() throws Exception {
589     Key key1 = Key.newBuilder().setKeyId(1).setStatus(KeyStatusType.ENABLED).build();
590 
591     assertThrows(
592         GeneralSecurityException.class,
593         () ->
594             PrimitiveSet.newBuilder(Mac.class)
595                 .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
596                 .build());
597     assertThrows(
598         GeneralSecurityException.class,
599         () ->
600             PrimitiveSet.newBuilder(Mac.class)
601                 .addPrimaryFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
602                 .build());
603   }
604 
605   @Test
testAddFullPrimive_withDisabledKey_shouldFail()606   public void testAddFullPrimive_withDisabledKey_shouldFail() throws Exception {
607     Key key1 =
608         Key.newBuilder()
609             .setKeyId(1)
610             .setStatus(KeyStatusType.DISABLED)
611             .setOutputPrefixType(OutputPrefixType.TINK)
612             .build();
613 
614     assertThrows(
615         GeneralSecurityException.class,
616         () ->
617             PrimitiveSet.newBuilder(Mac.class)
618                 .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
619                 .build());
620     assertThrows(
621         GeneralSecurityException.class,
622         () ->
623             PrimitiveSet.newBuilder(Mac.class)
624                 .addPrimaryFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
625                 .build());
626   }
627 
628   @Test
testAddFullPrimive_withNullPrimitive_throwsNullPointerException()629   public void testAddFullPrimive_withNullPrimitive_throwsNullPointerException() throws Exception {
630     Key key =
631         Key.newBuilder()
632             .setKeyId(1)
633             .setStatus(KeyStatusType.ENABLED)
634             .setOutputPrefixType(OutputPrefixType.TINK)
635             .build();
636 
637     assertThrows(
638         NullPointerException.class,
639         () ->
640             PrimitiveSet.newBuilder(Mac.class)
641                 .addFullPrimitive(null, getKeyFromProtoKey(key), key));
642 
643     assertThrows(
644         NullPointerException.class,
645         () ->
646             PrimitiveSet.newBuilder(Mac.class)
647                 .addPrimaryFullPrimitive(null, getKeyFromProtoKey(key), key));
648   }
649 
650   @Test
testPrefixIsUnique()651   public void testPrefixIsUnique() throws Exception {
652     Key key1 =
653         Key.newBuilder()
654             .setKeyId(0xffffffff)
655             .setStatus(KeyStatusType.ENABLED)
656             .setOutputPrefixType(OutputPrefixType.TINK)
657             .build();
658     Key key2 =
659         Key.newBuilder()
660             .setKeyId(0xffffffdf)
661             .setStatus(KeyStatusType.ENABLED)
662             .setOutputPrefixType(OutputPrefixType.RAW)
663             .build();
664     Key key3 =
665         Key.newBuilder()
666             .setKeyId(0xffffffef)
667             .setStatus(KeyStatusType.ENABLED)
668             .setOutputPrefixType(OutputPrefixType.LEGACY)
669             .build();
670 
671     PrimitiveSet<Mac> pset =
672         PrimitiveSet.newBuilder(Mac.class)
673             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key1), key1)
674             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key2), key2)
675             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key3), key3)
676             .build();
677 
678     assertThat(pset.getAll()).hasSize(3);
679     assertThat(pset.getPrimitive(Hex.decode("01ffffffff"))).hasSize(1);
680     assertThat(pset.getPrimitive(Hex.decode("01ffffffef"))).isEmpty();
681     assertThat(pset.getPrimitive(Hex.decode("00ffffffff"))).isEmpty();
682     assertThat(pset.getPrimitive(Hex.decode("00ffffffef"))).hasSize(1);
683   }
684 
685   @Test
getAllInKeysetOrder_works()686   public void getAllInKeysetOrder_works() throws Exception {
687     Key key0 =
688         Key.newBuilder()
689             .setKeyId(0xffffffff)
690             .setStatus(KeyStatusType.ENABLED)
691             .setOutputPrefixType(OutputPrefixType.TINK)
692             .build();
693     Key key1 =
694         Key.newBuilder()
695             .setKeyId(0xffffffdf)
696             .setStatus(KeyStatusType.ENABLED)
697             .setOutputPrefixType(OutputPrefixType.RAW)
698             .build();
699     Key key2 =
700         Key.newBuilder()
701             .setKeyId(0xffffffef)
702             .setStatus(KeyStatusType.ENABLED)
703             .setOutputPrefixType(OutputPrefixType.LEGACY)
704             .build();
705     PrimitiveSet<Mac> pset =
706         PrimitiveSet.newBuilder(Mac.class)
707             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key0), key0)
708             .addPrimaryFullPrimitive(new DummyMac2(), getKeyFromProtoKey(key1), key1)
709             .addFullPrimitive(new DummyMac1(), getKeyFromProtoKey(key2), key2)
710             .build();
711 
712     List<PrimitiveSet.Entry<Mac>> entries = pset.getAllInKeysetOrder();
713     assertThat(entries).hasSize(3);
714     assertThat(entries.get(0).getOutputPrefixType()).isEqualTo(OutputPrefixType.TINK);
715     assertThat(entries.get(1).getOutputPrefixType()).isEqualTo(OutputPrefixType.RAW);
716     assertThat(entries.get(2).getOutputPrefixType()).isEqualTo(OutputPrefixType.LEGACY);
717   }
718 }
719