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