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.mac; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertTrue; 21 22 import com.google.crypto.tink.InsecureSecretKeyAccess; 23 import com.google.crypto.tink.Key; 24 import com.google.crypto.tink.KeyTemplate; 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.Parameters; 29 import com.google.crypto.tink.RegistryConfiguration; 30 import com.google.crypto.tink.TinkProtoKeysetFormat; 31 import com.google.crypto.tink.internal.KeyManagerRegistry; 32 import com.google.crypto.tink.internal.SlowInputStream; 33 import com.google.crypto.tink.mac.internal.HmacTestUtil; 34 import com.google.crypto.tink.mac.internal.HmacTestUtil.HmacTestVector; 35 import com.google.crypto.tink.subtle.Hex; 36 import com.google.crypto.tink.util.SecretBytes; 37 import java.io.ByteArrayInputStream; 38 import java.nio.ByteBuffer; 39 import java.util.Arrays; 40 import java.util.Set; 41 import java.util.TreeSet; 42 import javax.annotation.Nullable; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.experimental.theories.DataPoints; 46 import org.junit.experimental.theories.FromDataPoints; 47 import org.junit.experimental.theories.Theories; 48 import org.junit.experimental.theories.Theory; 49 import org.junit.runner.RunWith; 50 51 /** Unit tests for {@link HmacKeyManager}. */ 52 @RunWith(Theories.class) 53 public class HmacKeyManagerTest { 54 @Before register()55 public void register() throws Exception { 56 MacConfig.register(); 57 } 58 59 @Test testKeyManagerRegistered()60 public void testKeyManagerRegistered() throws Exception { 61 assertThat( 62 KeyManagerRegistry.globalInstance() 63 .getKeyManager("type.googleapis.com/google.crypto.tink.HmacKey", Mac.class)) 64 .isNotNull(); 65 } 66 67 @Test createKey_multipleTimes()68 public void createKey_multipleTimes() throws Exception { 69 HmacParameters parameters = 70 HmacParameters.builder() 71 .setKeySizeBytes(32) 72 .setTagSizeBytes(16) 73 .setHashType(HmacParameters.HashType.SHA256) 74 .setVariant(HmacParameters.Variant.TINK) 75 .build(); 76 int numKeys = 100; 77 Set<String> keys = new TreeSet<>(); 78 for (int i = 0; i < numKeys; ++i) { 79 KeysetHandle handle = KeysetHandle.generateNew(parameters); 80 com.google.crypto.tink.mac.HmacKey macKey = 81 (com.google.crypto.tink.mac.HmacKey) handle.getAt(0).getKey(); 82 keys.add(Hex.encode(macKey.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get()))); 83 } 84 assertThat(keys).hasSize(numKeys); 85 } 86 87 @Test testHmacSha256HalfDigestTemplate()88 public void testHmacSha256HalfDigestTemplate() throws Exception { 89 KeyTemplate template = HmacKeyManager.hmacSha256HalfDigestTemplate(); 90 assertThat(template.toParameters()) 91 .isEqualTo( 92 HmacParameters.builder() 93 .setKeySizeBytes(32) 94 .setTagSizeBytes(16) 95 .setHashType(HmacParameters.HashType.SHA256) 96 .setVariant(HmacParameters.Variant.TINK) 97 .build()); 98 } 99 100 @Test testHmacSha256Template()101 public void testHmacSha256Template() throws Exception { 102 KeyTemplate template = HmacKeyManager.hmacSha256Template(); 103 assertThat(template.toParameters()) 104 .isEqualTo( 105 HmacParameters.builder() 106 .setKeySizeBytes(32) 107 .setTagSizeBytes(32) 108 .setHashType(HmacParameters.HashType.SHA256) 109 .setVariant(HmacParameters.Variant.TINK) 110 .build()); 111 } 112 113 @Test testHmacSha512HalfDigestTemplate()114 public void testHmacSha512HalfDigestTemplate() throws Exception { 115 KeyTemplate template = HmacKeyManager.hmacSha512HalfDigestTemplate(); 116 assertThat(template.toParameters()) 117 .isEqualTo( 118 HmacParameters.builder() 119 .setKeySizeBytes(64) 120 .setTagSizeBytes(32) 121 .setHashType(HmacParameters.HashType.SHA512) 122 .setVariant(HmacParameters.Variant.TINK) 123 .build()); 124 } 125 126 @Test testHmacSha512Template()127 public void testHmacSha512Template() throws Exception { 128 KeyTemplate template = HmacKeyManager.hmacSha512Template(); 129 assertThat(template.toParameters()) 130 .isEqualTo( 131 HmacParameters.builder() 132 .setKeySizeBytes(64) 133 .setTagSizeBytes(64) 134 .setHashType(HmacParameters.HashType.SHA512) 135 .setVariant(HmacParameters.Variant.TINK) 136 .build()); 137 } 138 139 @Test testKeyTemplatesWork()140 public void testKeyTemplatesWork() throws Exception { 141 Parameters p = HmacKeyManager.hmacSha256Template().toParameters(); 142 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 143 144 p = HmacKeyManager.hmacSha256HalfDigestTemplate().toParameters(); 145 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 146 147 p = HmacKeyManager.hmacSha512Template().toParameters(); 148 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 149 150 p = HmacKeyManager.hmacSha512HalfDigestTemplate().toParameters(); 151 assertThat(KeysetHandle.generateNew(p).getAt(0).getKey().getParameters()).isEqualTo(p); 152 } 153 154 @DataPoints("templateNames") 155 public static final String[] KEY_TEMPLATES = 156 new String[] { 157 "HMAC_SHA256_128BITTAG", 158 "HMAC_SHA256_128BITTAG_RAW", 159 "HMAC_SHA256_256BITTAG", 160 "HMAC_SHA256_256BITTAG_RAW", 161 "HMAC_SHA512_128BITTAG", 162 "HMAC_SHA512_128BITTAG_RAW", 163 "HMAC_SHA512_256BITTAG", 164 "HMAC_SHA512_256BITTAG_RAW", 165 "HMAC_SHA512_512BITTAG", 166 "HMAC_SHA512_512BITTAG_RAW" 167 }; 168 169 @Theory testTemplates(@romDataPoints"templateNames") String templateName)170 public void testTemplates(@FromDataPoints("templateNames") String templateName) throws Exception { 171 KeysetHandle h = KeysetHandle.generateNew(KeyTemplates.get(templateName)); 172 assertThat(h.size()).isEqualTo(1); 173 assertThat(h.getAt(0).getKey().getParameters()) 174 .isEqualTo(KeyTemplates.get(templateName).toParameters()); 175 } 176 177 @Theory testCreateKeyFromRandomness(@romDataPoints"templateNames") String templateName)178 public void testCreateKeyFromRandomness(@FromDataPoints("templateNames") String templateName) 179 throws Exception { 180 byte[] keyMaterial = 181 new byte[] { 182 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 183 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 184 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 185 }; 186 HmacParameters parameters = (HmacParameters) KeyTemplates.get(templateName).toParameters(); 187 com.google.crypto.tink.mac.HmacKey key = 188 HmacKeyManager.createHmacKeyFromRandomness( 189 parameters, 190 new ByteArrayInputStream(keyMaterial), 191 parameters.hasIdRequirement() ? 123 : null, 192 InsecureSecretKeyAccess.get()); 193 byte[] expectedKeyBytes = Arrays.copyOf(keyMaterial, parameters.getKeySizeBytes()); 194 Key expectedKey = 195 com.google.crypto.tink.mac.HmacKey.builder() 196 .setParameters(parameters) 197 .setIdRequirement(parameters.hasIdRequirement() ? 123 : null) 198 .setKeyBytes(SecretBytes.copyFrom(expectedKeyBytes, InsecureSecretKeyAccess.get())) 199 .build(); 200 assertTrue(key.equalsKey(expectedKey)); 201 } 202 203 @Test testCreateKeyFromRandomness_slowInputStream_works()204 public void testCreateKeyFromRandomness_slowInputStream_works() throws Exception { 205 byte[] keyMaterial = 206 new byte[] { 207 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 208 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 209 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 210 }; 211 HmacParameters parameters = 212 HmacParameters.builder() 213 .setKeySizeBytes(64) 214 .setTagSizeBytes(64) 215 .setHashType(HmacParameters.HashType.SHA512) 216 .setVariant(HmacParameters.Variant.TINK) 217 .build(); 218 com.google.crypto.tink.mac.HmacKey key = 219 HmacKeyManager.createHmacKeyFromRandomness( 220 parameters, SlowInputStream.copyFrom(keyMaterial), 7975, InsecureSecretKeyAccess.get()); 221 byte[] expectedKeyBytes = Arrays.copyOf(keyMaterial, parameters.getKeySizeBytes()); 222 Key expectedKey = 223 com.google.crypto.tink.mac.HmacKey.builder() 224 .setParameters(parameters) 225 .setIdRequirement(7975) 226 .setKeyBytes(SecretBytes.copyFrom(expectedKeyBytes, InsecureSecretKeyAccess.get())) 227 .build(); 228 assertTrue(key.equalsKey(expectedKey)); 229 } 230 231 @DataPoints("hmacTestVectors") 232 public static final HmacTestVector[] HMAC_TEST_VECTORS = HmacTestUtil.HMAC_TEST_VECTORS; 233 234 @Theory testGetPrimitive_chunkedMac_works(@romDataPoints"hmacTestVectors") HmacTestVector t)235 public void testGetPrimitive_chunkedMac_works(@FromDataPoints("hmacTestVectors") HmacTestVector t) 236 throws Exception { 237 KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(t.key).makePrimary(); 238 @Nullable Integer id = t.key.getIdRequirementOrNull(); 239 if (id == null) { 240 entry.withRandomId(); 241 } else { 242 entry.withFixedId(id); 243 } 244 KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build(); 245 ChunkedMac chunkedMac = handle.getPrimitive(RegistryConfiguration.get(), ChunkedMac.class); 246 247 ChunkedMacComputation chunkedMacComputation = chunkedMac.createComputation(); 248 chunkedMacComputation.update(ByteBuffer.wrap(t.message).asReadOnlyBuffer()); 249 assertThat(t.tag).isEqualTo(chunkedMacComputation.computeMac()); 250 251 ChunkedMacVerification chunkedHmacVerification = chunkedMac.createVerification(t.tag); 252 chunkedHmacVerification.update(ByteBuffer.wrap(t.message)); 253 chunkedHmacVerification.verifyMac(); 254 } 255 256 @Theory testGetPrimitive_mac_works(@romDataPoints"hmacTestVectors") HmacTestVector t)257 public void testGetPrimitive_mac_works(@FromDataPoints("hmacTestVectors") HmacTestVector t) 258 throws Exception { 259 KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(t.key).makePrimary(); 260 @Nullable Integer id = t.key.getIdRequirementOrNull(); 261 if (id == null) { 262 entry.withRandomId(); 263 } else { 264 entry.withFixedId(id); 265 } 266 KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build(); 267 Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class); 268 269 assertThat(t.tag).isEqualTo(mac.computeMac(t.message)); 270 mac.verifyMac(t.tag, t.message); 271 } 272 273 @Test testSerializeAndParse_works()274 public void testSerializeAndParse_works() throws Exception { 275 HmacParameters parameters = 276 HmacParameters.builder() 277 .setKeySizeBytes(64) 278 .setTagSizeBytes(64) 279 .setHashType(HmacParameters.HashType.SHA512) 280 .setVariant(HmacParameters.Variant.TINK) 281 .build(); 282 KeysetHandle handle = KeysetHandle.generateNew(parameters); 283 byte[] serialized = 284 TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()); 285 KeysetHandle parsed = 286 TinkProtoKeysetFormat.parseKeyset(serialized, InsecureSecretKeyAccess.get()); 287 288 assertTrue(handle.equalsKeyset(parsed)); 289 } 290 } 291