• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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.aead;
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.assertThrows;
22 
23 import com.google.crypto.tink.Aead;
24 import com.google.crypto.tink.DeterministicAead;
25 import com.google.crypto.tink.InsecureSecretKeyAccess;
26 import com.google.crypto.tink.KeyTemplates;
27 import com.google.crypto.tink.KeysetHandle;
28 import com.google.crypto.tink.RegistryConfiguration;
29 import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
30 import com.google.crypto.tink.daead.DeterministicAeadConfig;
31 import java.security.GeneralSecurityException;
32 import org.junit.BeforeClass;
33 import org.junit.experimental.theories.DataPoints;
34 import org.junit.experimental.theories.FromDataPoints;
35 import org.junit.experimental.theories.Theories;
36 import org.junit.experimental.theories.Theory;
37 import org.junit.runner.RunWith;
38 
39 /** Unit tests for the Aead package. Uses only the public API. */
40 @RunWith(Theories.class)
41 public final class AeadTest {
42 
43   @BeforeClass
setUp()44   public static void setUp() throws Exception {
45     AeadConfig.register();
46     DeterministicAeadConfig.register(); // Needed for getPrimitiveFromNonAeadKeyset_throws.
47   }
48 
49   @DataPoints("templates")
50   public static final String[] TEMPLATES =
51       new String[] {
52         "AES128_EAX",
53         "AES128_EAX_RAW",
54         "AES256_EAX",
55         "AES256_EAX_RAW",
56         "AES128_GCM",
57         "AES128_GCM_RAW",
58         "AES256_GCM",
59         "AES256_GCM_RAW",
60         "AES128_CTR_HMAC_SHA256",
61         "AES128_CTR_HMAC_SHA256_RAW",
62         "AES256_CTR_HMAC_SHA256",
63         "AES256_CTR_HMAC_SHA256_RAW",
64         "CHACHA20_POLY1305",
65         "CHACHA20_POLY1305_RAW",
66         "XCHACHA20_POLY1305",
67         "XCHACHA20_POLY1305_RAW"
68       };
69 
70   @Theory
createEncryptDecrypt(@romDataPoints"templates") String templateName)71   public void createEncryptDecrypt(@FromDataPoints("templates") String templateName)
72       throws Exception {
73     KeysetHandle handle = KeysetHandle.generateNew(KeyTemplates.get(templateName));
74     Aead aead = handle.getPrimitive(RegistryConfiguration.get(), Aead.class);
75     byte[] plaintext = "plaintext".getBytes(UTF_8);
76     byte[] associatedData = "associatedData".getBytes(UTF_8);
77     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
78     byte[] decrypted = aead.decrypt(ciphertext, associatedData);
79     assertThat(decrypted).isEqualTo(plaintext);
80 
81     KeysetHandle otherHandle = KeysetHandle.generateNew(KeyTemplates.get(templateName));
82     Aead otherAead = otherHandle.getPrimitive(RegistryConfiguration.get(), Aead.class);
83     assertThrows(
84         GeneralSecurityException.class, () -> otherAead.decrypt(ciphertext, associatedData));
85 
86     byte[] invalid = "invalid".getBytes(UTF_8);
87     byte[] empty = "".getBytes(UTF_8);
88     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext, invalid));
89     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(invalid, associatedData));
90     assertThrows(GeneralSecurityException.class, () -> aead.decrypt(empty, associatedData));
91     assertThat(aead.decrypt(aead.encrypt(empty, associatedData), associatedData)).isEqualTo(empty);
92     assertThat(aead.decrypt(aead.encrypt(plaintext, empty), empty)).isEqualTo(plaintext);
93   }
94 
95   // A keyset with one AEAD key, serialized in Tink's JSON format.
96   private static final String JSON_AEAD_KEYSET =
97       ""
98           + "{"
99           + "  \"primaryKeyId\": 42818733,"
100           + "  \"key\": ["
101           + "    {"
102           + "      \"keyData\": {"
103           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
104           + "        \"keyMaterialType\": \"SYMMETRIC\","
105           + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
106           + "      },"
107           + "      \"outputPrefixType\": \"TINK\","
108           + "      \"keyId\": 42818733,"
109           + "      \"status\": \"ENABLED\""
110           + "    }"
111           + "  ]"
112           + "}";
113 
114   @Theory
readKeysetEncryptDecrypt()115   public void readKeysetEncryptDecrypt()
116       throws Exception {
117     KeysetHandle handle =
118         TinkJsonProtoKeysetFormat.parseKeyset(JSON_AEAD_KEYSET, InsecureSecretKeyAccess.get());
119 
120     Aead aead = handle.getPrimitive(RegistryConfiguration.get(), Aead.class);
121 
122     byte[] plaintext = "plaintext".getBytes(UTF_8);
123     byte[] associatedData = "associatedData".getBytes(UTF_8);
124     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
125     byte[] decrypted = aead.decrypt(ciphertext, associatedData);
126     assertThat(decrypted).isEqualTo(plaintext);
127   }
128 
129   // A keyset with multiple keys. The first key is the same as in JSON_AEAD_KEYSET.
130   private static final String JSON_AEAD_KEYSET_WITH_MULTIPLE_KEYS =
131       ""
132           + "{"
133           + "  \"primaryKeyId\": 365202604,"
134           + "  \"key\": ["
135           + "    {"
136           + "      \"keyData\": {"
137           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesGcmKey\","
138           + "        \"keyMaterialType\": \"SYMMETRIC\","
139           + "        \"value\": \"GhCC74uJ+2f4qlpaHwR4ylNQ\""
140           + "      },"
141           + "      \"outputPrefixType\": \"TINK\","
142           + "      \"keyId\": 42818733,"
143           + "      \"status\": \"ENABLED\""
144           + "    }, {"
145           + "      \"keyData\": {"
146           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesEaxKey\","
147           + "        \"keyMaterialType\": \"SYMMETRIC\","
148           + "        \"value\": \"EgIIEBogU4nieBfIeJHBrhC+TjezFgxkkuhQHbyWkUMH+7atLxI=\""
149           + "      },"
150           + "      \"outputPrefixType\": \"RAW\","
151           + "      \"keyId\": 365202604,"
152           + "      \"status\": \"ENABLED\""
153           + "    }, {"
154           + "      \"keyData\": {"
155           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey\","
156           + "        \"keyMaterialType\": \"SYMMETRIC\","
157           + "        \"value\": \"GigaIMttlipP/JvQOpIB0NYhDPoLgWBiIxmtaWbSPa2TeQOmEgQQEAgDEhYaEPcCM"
158           + "mPLgRGhmMmSC4AJ1CESAggQ\""
159           + "      },"
160           + "      \"outputPrefixType\": \"LEGACY\","
161           + "      \"keyId\": 277095770,"
162           + "      \"status\": \"ENABLED\""
163           + "    }"
164           + "  ]"
165           + "}";
166 
167   @Theory
multipleKeysReadKeysetWithEncryptDecrypt()168   public void multipleKeysReadKeysetWithEncryptDecrypt()
169       throws Exception {
170     KeysetHandle handle =
171         TinkJsonProtoKeysetFormat.parseKeyset(
172             JSON_AEAD_KEYSET_WITH_MULTIPLE_KEYS, InsecureSecretKeyAccess.get());
173 
174     Aead aead = handle.getPrimitive(RegistryConfiguration.get(), Aead.class);
175 
176     byte[] plaintext = "plaintext".getBytes(UTF_8);
177     byte[] associatedData = "associatedData".getBytes(UTF_8);
178     byte[] ciphertext = aead.encrypt(plaintext, associatedData);
179     assertThat(aead.decrypt(ciphertext, associatedData)).isEqualTo(plaintext);
180 
181     // Also test that aead can decrypt ciphertexts encrypted with a non-primary key. We use
182     // JSON_AEAD_KEYSET to encrypt with the first key.
183     KeysetHandle handle1 =
184         TinkJsonProtoKeysetFormat.parseKeyset(JSON_AEAD_KEYSET, InsecureSecretKeyAccess.get());
185 
186     Aead aead1 = handle1.getPrimitive(RegistryConfiguration.get(), Aead.class);
187     byte[] ciphertext1 = aead1.encrypt(plaintext, associatedData);
188     assertThat(aead.decrypt(ciphertext1, associatedData)).isEqualTo(plaintext);
189   }
190 
191   // A keyset with a valid DeterministicAead key. This keyset can't be used with the Aead primitive.
192   private static final String JSON_DAEAD_KEYSET =
193       ""
194           + "{"
195           + "  \"primaryKeyId\": 961932622,"
196           + "  \"key\": ["
197           + "    {"
198           + "      \"keyData\": {"
199           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesSivKey\","
200           + "        \"keyMaterialType\": \"SYMMETRIC\","
201           + "        \"value\": \"EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCS"
202           + "kvV2+7u6F2DN+kqUjAfkf2W\""
203           + "      },"
204           + "      \"outputPrefixType\": \"TINK\","
205           + "      \"keyId\": 961932622,"
206           + "      \"status\": \"ENABLED\""
207           + "    }"
208           + "  ]"
209           + "}";
210 
211   @Theory
getPrimitiveFromNonAeadKeyset_throws()212   public void getPrimitiveFromNonAeadKeyset_throws()
213       throws Exception {
214     KeysetHandle handle =
215         TinkJsonProtoKeysetFormat.parseKeyset(JSON_DAEAD_KEYSET, InsecureSecretKeyAccess.get());
216     // Test that the keyset can create a DeterministicAead primitive, but not a Aead.
217     Object unused = handle.getPrimitive(RegistryConfiguration.get(), DeterministicAead.class);
218     assertThrows(
219         GeneralSecurityException.class,
220         () -> handle.getPrimitive(RegistryConfiguration.get(), Aead.class));
221   }
222 }
223