• 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.mac;
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.DeterministicAead;
24 import com.google.crypto.tink.InsecureSecretKeyAccess;
25 import com.google.crypto.tink.KeyTemplates;
26 import com.google.crypto.tink.KeysetHandle;
27 import com.google.crypto.tink.Mac;
28 import com.google.crypto.tink.RegistryConfiguration;
29 import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
30 import com.google.crypto.tink.daead.DeterministicAeadConfig;
31 import com.google.crypto.tink.util.SecretBytes;
32 import java.security.GeneralSecurityException;
33 import org.junit.BeforeClass;
34 import org.junit.experimental.theories.DataPoints;
35 import org.junit.experimental.theories.FromDataPoints;
36 import org.junit.experimental.theories.Theories;
37 import org.junit.experimental.theories.Theory;
38 import org.junit.runner.RunWith;
39 
40 /** Unit tests for the Mac package. Uses only the public API. */
41 @RunWith(Theories.class)
42 public final class MacTest {
43 
44   @BeforeClass
setUp()45   public static void setUp() throws Exception {
46     MacConfig.register();
47     DeterministicAeadConfig.register(); // Needed for getPrimitiveFromNonMacKeyset_throws.
48   }
49 
50   @DataPoints("templates")
51   public static final String[] TEMPLATES =
52       new String[] {
53         "AES256_CMAC",
54         "AES256_CMAC_RAW",
55         "HMAC_SHA256_128BITTAG",
56         "HMAC_SHA256_128BITTAG_RAW",
57         "HMAC_SHA256_256BITTAG",
58         "HMAC_SHA256_256BITTAG_RAW",
59         "HMAC_SHA512_128BITTAG",
60         "HMAC_SHA512_128BITTAG_RAW",
61         "HMAC_SHA512_256BITTAG",
62         "HMAC_SHA512_256BITTAG_RAW",
63         "HMAC_SHA512_512BITTAG",
64         "HMAC_SHA512_512BITTAG_RAW",
65       };
66 
67   @Theory
create_computeVerify(@romDataPoints"templates") String templateName)68   public void create_computeVerify(@FromDataPoints("templates") String templateName)
69       throws Exception {
70     KeysetHandle handle = KeysetHandle.generateNew(KeyTemplates.get(templateName));
71     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
72 
73     byte[] data = "data".getBytes(UTF_8);
74     byte[] tag = mac.computeMac(data);
75     mac.verifyMac(tag, data);
76 
77     KeysetHandle otherHandle = KeysetHandle.generateNew(KeyTemplates.get(templateName));
78     Mac otherMac = otherHandle.getPrimitive(RegistryConfiguration.get(), Mac.class);
79     assertThrows(GeneralSecurityException.class, () -> otherMac.verifyMac(tag, data));
80 
81     byte[] invalid = "invalid".getBytes(UTF_8);
82     byte[] empty = "".getBytes(UTF_8);
83     assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(invalid, data));
84     assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(tag, invalid));
85     assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(empty, data));
86     assertThrows(GeneralSecurityException.class, () -> mac.verifyMac(tag, empty));
87     mac.verifyMac(mac.computeMac(empty), empty);
88   }
89 
90   @Theory
useAesCmacParametersAndAesCmacKey()91   public void useAesCmacParametersAndAesCmacKey() throws Exception {
92     AesCmacParameters parameters =
93         AesCmacParameters.builder()
94             .setKeySizeBytes(32)
95             .setTagSizeBytes(13)
96             .setVariant(AesCmacParameters.Variant.LEGACY)
97             .build();
98     KeysetHandle handle =
99         KeysetHandle.newBuilder()
100             .addEntry(
101                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(123).makePrimary())
102             .build();
103 
104     AesCmacKey aesCmacKey = (AesCmacKey) handle.getAt(0).getKey();
105     assertThat(aesCmacKey.getParameters()).isEqualTo(parameters);
106     assertThat(aesCmacKey.getIdRequirementOrNull()).isEqualTo(123);
107     SecretBytes secretBytes = aesCmacKey.getAesKey();
108     assertThat(secretBytes.size()).isEqualTo(32);
109 
110     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
111     byte[] data = "data".getBytes(UTF_8);
112     byte[] tag = mac.computeMac(data);
113     mac.verifyMac(tag, data);
114   }
115 
116   @Theory
useHmacParametersAndHmacKey()117   public void useHmacParametersAndHmacKey() throws Exception {
118     HmacParameters parameters =
119         HmacParameters.builder()
120             .setKeySizeBytes(42)
121             .setTagSizeBytes(13)
122             .setHashType(HmacParameters.HashType.SHA1)
123             .setVariant(HmacParameters.Variant.CRUNCHY)
124             .build();
125     KeysetHandle handle =
126         KeysetHandle.newBuilder()
127             .addEntry(
128                 KeysetHandle.generateEntryFromParameters(parameters).withFixedId(123).makePrimary())
129             .build();
130 
131     HmacKey hmacKey = (HmacKey) handle.getAt(0).getKey();
132     assertThat(hmacKey.getParameters()).isEqualTo(parameters);
133     assertThat(hmacKey.getIdRequirementOrNull()).isEqualTo(123);
134     SecretBytes secretBytes = hmacKey.getKeyBytes();
135     assertThat(secretBytes.size()).isEqualTo(42);
136 
137     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
138     byte[] data = "data".getBytes(UTF_8);
139     byte[] tag = mac.computeMac(data);
140     mac.verifyMac(tag, data);
141   }
142 
143   // A keyset with one MAC key, serialized in Tink's JSON format.
144   private static final String JSON_MAC_KEYSET =
145       ""
146           + "{"
147           + "  \"primaryKeyId\": 207420876,"
148           + "  \"key\": ["
149           + "    {"
150           + "      \"keyData\": {"
151           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
152           + "        \"value\": \"GiAPii+kxtLpvCARQpftFLt4R+O6ARsyhTR7SkCCGt0bHRIEEBAIAw==\","
153           + "        \"keyMaterialType\": \"SYMMETRIC\""
154           + "      },"
155           + "      \"status\": \"ENABLED\","
156           + "      \"keyId\": 207420876,"
157           + "      \"outputPrefixType\": \"TINK\""
158           + "    }"
159           + "  ]"
160           + "}";
161 
162   @Theory
readKeysetEncryptDecrypt()163   public void readKeysetEncryptDecrypt()
164       throws Exception {
165     KeysetHandle handle =
166         TinkJsonProtoKeysetFormat.parseKeyset(JSON_MAC_KEYSET, InsecureSecretKeyAccess.get());
167 
168     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
169 
170     byte[] data = "data".getBytes(UTF_8);
171     byte[] tag = mac.computeMac(data);
172     mac.verifyMac(tag, data);
173   }
174 
175   // A keyset with multiple keys. The first key is the same as in JSON_AEAD_KEYSET.
176   private static final String JSON_MAC_KEYSET_WITH_MULTIPLE_KEYS =
177       ""
178           + "{"
179           + "  \"primaryKeyId\": 2054715504,"
180           + "  \"key\": ["
181           + "    {"
182           + "      \"keyData\": {"
183           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
184           + "        \"value\": \"GiAPii+kxtLpvCARQpftFLt4R+O6ARsyhTR7SkCCGt0bHRIEEBAIAw==\","
185           + "        \"keyMaterialType\": \"SYMMETRIC\""
186           + "      },"
187           + "      \"status\": \"ENABLED\","
188           + "      \"keyId\": 207420876,"
189           + "      \"outputPrefixType\": \"TINK\""
190           + "    }, {"
191           + "      \"keyData\": {"
192           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesCmacKey\","
193           + "        \"value\": \"GgIIEBIgLaZ/6QXYeqZB8F4zHTRJU5k6TF5xvlSX9ZVLVA09UY0=\","
194           + "        \"keyMaterialType\": \"SYMMETRIC\""
195           + "      },"
196           + "      \"status\": \"ENABLED\","
197           + "      \"keyId\": 2054715504,"
198           + "      \"outputPrefixType\": \"RAW\""
199           + "    }, {"
200           + "      \"keyData\": {"
201           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
202           + "        \"value\": \"GkCCIGYpFz3mj8wnTH3Ca81F1sQ7JEMxoE8B2nKiND7LrKfbaUx+/qqDXUP"
203           + "VjkzC9XdbjsaEqc9yI+RKyITef+eUEgQQQAgE\","
204           + "        \"keyMaterialType\": \"SYMMETRIC\""
205           + "      },"
206           + "      \"status\": \"ENABLED\","
207           + "      \"keyId\": 1540103625,"
208           + "      \"outputPrefixType\": \"LEGACY\""
209           + "    }, {"
210           + "      \"keyData\": {"
211           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacKey\","
212           + "        \"value\": \"GkA8u6JKtInsySJDZO4j6TLoIvLuGAeAZHDZoTlST0aZZ8gZZViHogzWTqt"
213           + "i2Vlp3ccy+OdN6lhMxSiphcPaR5OiEgQQIAgE\","
214           + "        \"keyMaterialType\": \"SYMMETRIC\""
215           + "      },"
216           + "      \"status\": \"ENABLED\","
217           + "      \"keyId\": 570162478,"
218           + "      \"outputPrefixType\": \"CRUNCHY\""
219           + "    }"
220           + "  ]"
221           + "}";
222 
223   @Theory
multipleKeysReadKeysetWithEncryptDecrypt()224   public void multipleKeysReadKeysetWithEncryptDecrypt()
225       throws Exception {
226     KeysetHandle handle =
227         TinkJsonProtoKeysetFormat.parseKeyset(
228             JSON_MAC_KEYSET_WITH_MULTIPLE_KEYS, InsecureSecretKeyAccess.get());
229 
230     Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class);
231 
232     byte[] data = "data".getBytes(UTF_8);
233     byte[] tag = mac.computeMac(data);
234     mac.verifyMac(tag, data);
235 
236     // Also test that mac can verify tags computed with a non-primary key. We use
237     // JSON_MAC_KEYSET to compute a tag with the first key.
238     KeysetHandle handle1 =
239         TinkJsonProtoKeysetFormat.parseKeyset(JSON_MAC_KEYSET, InsecureSecretKeyAccess.get());
240     Mac mac1 = handle1.getPrimitive(RegistryConfiguration.get(), Mac.class);
241     byte[] tag1 = mac1.computeMac(data);
242     mac.verifyMac(tag1, data);
243   }
244 
245   // A keyset with a valid DeterministicAead key. This keyset can't be used with the Mac primitive.
246   private static final String JSON_DAEAD_KEYSET =
247       ""
248           + "{"
249           + "  \"primaryKeyId\": 961932622,"
250           + "  \"key\": ["
251           + "    {"
252           + "      \"keyData\": {"
253           + "        \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesSivKey\","
254           + "        \"keyMaterialType\": \"SYMMETRIC\","
255           + "        \"value\": \"EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCS"
256           + "kvV2+7u6F2DN+kqUjAfkf2W\""
257           + "      },"
258           + "      \"outputPrefixType\": \"TINK\","
259           + "      \"keyId\": 961932622,"
260           + "      \"status\": \"ENABLED\""
261           + "    }"
262           + "  ]"
263           + "}";
264 
265   @Theory
getPrimitiveFromNonMacKeyset_throws()266   public void getPrimitiveFromNonMacKeyset_throws() throws Exception {
267     KeysetHandle handle =
268         TinkJsonProtoKeysetFormat.parseKeyset(JSON_DAEAD_KEYSET, InsecureSecretKeyAccess.get());
269     // Test that the keyset can create a DeterministicAead primitive, but not a Mac.
270     Object unused = handle.getPrimitive(RegistryConfiguration.get(), DeterministicAead.class);
271     assertThrows(
272         GeneralSecurityException.class,
273         () -> handle.getPrimitive(RegistryConfiguration.get(), Mac.class));
274   }
275 }
276