1 // Copyright 2023 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 java.nio.charset.StandardCharsets.UTF_8; 20 21 import com.google.crypto.tink.Key; 22 import com.google.crypto.tink.KeysetHandle; 23 import com.google.crypto.tink.Mac; 24 import com.google.crypto.tink.RegistryConfiguration; 25 import com.google.crypto.tink.mac.AesCmacParameters.Variant; 26 import com.google.crypto.tink.mac.HmacParameters.HashType; 27 import com.google.crypto.tink.mac.internal.AesCmacProtoSerialization; 28 import com.google.crypto.tink.mac.internal.HmacProtoSerialization; 29 import com.google.crypto.tink.util.SecretBytes; 30 import java.nio.ByteBuffer; 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 /** 40 * These tests ensure interoperability between the new ChunkedMac implementations and the old Mac 41 * implementations. 42 */ 43 @RunWith(Theories.class) 44 public class ChunkedMacTest { 45 private static final int HMAC_KEY_SIZE = 20; 46 private static final int HMAC_TAG_SIZE = 10; 47 private static final int AES_CMAC_KEY_SIZE = 32; 48 private static final int AES_CMAC_TAG_SIZE = 10; 49 50 @DataPoints("keys") 51 public static Key[] keys; 52 53 @BeforeClass setUp()54 public static void setUp() throws Exception { 55 MacConfig.register(); 56 AesCmacProtoSerialization.register(); 57 HmacProtoSerialization.register(); 58 ChunkedMacWrapper.register(); 59 createTestKeys(); 60 } 61 createTestKeys()62 private static void createTestKeys() { 63 HmacParameters noPrefixHmacParameters = 64 createDefaultHmacParameters(HmacParameters.Variant.NO_PREFIX); 65 HmacParameters legacyHmacParameters = 66 createDefaultHmacParameters(HmacParameters.Variant.LEGACY); 67 HmacParameters crunchyHmacParameters = 68 createDefaultHmacParameters(HmacParameters.Variant.CRUNCHY); 69 HmacParameters tinkHmacParameters = 70 createDefaultHmacParameters(HmacParameters.Variant.TINK); 71 AesCmacParameters noPrefixAesCmacParameters = 72 createDefaultAesCmacParameters(AesCmacParameters.Variant.NO_PREFIX); 73 AesCmacParameters legacyAesCmacParameters = 74 createDefaultAesCmacParameters(AesCmacParameters.Variant.LEGACY); 75 AesCmacParameters crunchyAesCmacParameters = 76 createDefaultAesCmacParameters(AesCmacParameters.Variant.CRUNCHY); 77 AesCmacParameters tinkAesCmacParameters = 78 createDefaultAesCmacParameters(AesCmacParameters.Variant.TINK); 79 80 try { 81 keys = 82 new Key[] { 83 HmacKey.builder() 84 .setParameters(noPrefixHmacParameters) 85 .setKeyBytes(SecretBytes.randomBytes(HMAC_KEY_SIZE)) 86 .setIdRequirement(null) 87 .build(), 88 AesCmacKey.builder() 89 .setParameters(noPrefixAesCmacParameters) 90 .setAesKeyBytes(SecretBytes.randomBytes(AES_CMAC_KEY_SIZE)) 91 .setIdRequirement(null) 92 .build(), 93 HmacKey.builder() 94 .setParameters(tinkHmacParameters) 95 .setKeyBytes(SecretBytes.randomBytes(HMAC_KEY_SIZE)) 96 .setIdRequirement(4) 97 .build(), 98 AesCmacKey.builder() 99 .setParameters(tinkAesCmacParameters) 100 .setAesKeyBytes(SecretBytes.randomBytes(AES_CMAC_KEY_SIZE)) 101 .setIdRequirement(5) 102 .build(), 103 HmacKey.builder() 104 .setParameters(crunchyHmacParameters) 105 .setKeyBytes(SecretBytes.randomBytes(HMAC_KEY_SIZE)) 106 .setIdRequirement(6) 107 .build(), 108 AesCmacKey.builder() 109 .setParameters(crunchyAesCmacParameters) 110 .setAesKeyBytes(SecretBytes.randomBytes(AES_CMAC_KEY_SIZE)) 111 .setIdRequirement(7) 112 .build(), 113 HmacKey.builder() 114 .setParameters(legacyHmacParameters) 115 .setKeyBytes(SecretBytes.randomBytes(HMAC_KEY_SIZE)) 116 .setIdRequirement(8) 117 .build(), 118 AesCmacKey.builder() 119 .setParameters(legacyAesCmacParameters) 120 .setAesKeyBytes(SecretBytes.randomBytes(AES_CMAC_KEY_SIZE)) 121 .setIdRequirement(9) 122 .build(), 123 }; 124 } catch (GeneralSecurityException e) { 125 throw new IllegalStateException(e); 126 } 127 } 128 createDefaultAesCmacParameters(Variant variant)129 private static AesCmacParameters createDefaultAesCmacParameters(Variant variant) { 130 try { 131 return AesCmacParameters.builder() 132 .setKeySizeBytes(AES_CMAC_KEY_SIZE) 133 .setTagSizeBytes(AES_CMAC_TAG_SIZE) 134 .setVariant(variant) 135 .build(); 136 } catch (GeneralSecurityException e) { 137 throw new IllegalStateException(e); 138 } 139 } 140 createDefaultHmacParameters(HmacParameters.Variant variant)141 private static HmacParameters createDefaultHmacParameters(HmacParameters.Variant variant) { 142 try { 143 return HmacParameters.builder() 144 .setKeySizeBytes(HMAC_KEY_SIZE) 145 .setTagSizeBytes(HMAC_TAG_SIZE) 146 .setVariant(variant) 147 .setHashType(HashType.SHA256) 148 .build(); 149 } catch (GeneralSecurityException e) { 150 throw new IllegalArgumentException("Incorrect parameters creation arguments", e); 151 } 152 } 153 154 @Theory computeWithMacVerifyWithChunkedMac_works(@romDataPoints"keys") Key key)155 public void computeWithMacVerifyWithChunkedMac_works(@FromDataPoints("keys") Key key) 156 throws GeneralSecurityException { 157 byte[] plaintext = "plaintext".getBytes(UTF_8); 158 KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(key); 159 if (key.getIdRequirementOrNull() == null) { 160 entry.withFixedId(1234); 161 } 162 KeysetHandle keysetHandle = KeysetHandle.newBuilder().addEntry(entry.makePrimary()).build(); 163 164 Mac mac = keysetHandle.getPrimitive(RegistryConfiguration.get(), Mac.class); 165 byte[] tag = mac.computeMac(plaintext); 166 ChunkedMac chunkedMac = 167 keysetHandle.getPrimitive(RegistryConfiguration.get(), ChunkedMac.class); 168 ChunkedMacVerification chunkedMacVerification = chunkedMac.createVerification(tag); 169 chunkedMacVerification.update(ByteBuffer.wrap(plaintext)); 170 171 chunkedMacVerification.verifyMac(); 172 } 173 174 @Theory computeWithChunkedMacVerifyWithMac_works(@romDataPoints"keys") Key key)175 public void computeWithChunkedMacVerifyWithMac_works(@FromDataPoints("keys") Key key) 176 throws GeneralSecurityException { 177 byte[] plaintext = "plaintext".getBytes(UTF_8); 178 KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(key); 179 if (key.getIdRequirementOrNull() == null) { 180 entry.withFixedId(1234); 181 } 182 KeysetHandle keysetHandle = KeysetHandle.newBuilder().addEntry(entry.makePrimary()).build(); 183 184 ChunkedMac chunkedMac = 185 keysetHandle.getPrimitive(RegistryConfiguration.get(), ChunkedMac.class); 186 ChunkedMacComputation chunkedMacComputation = chunkedMac.createComputation(); 187 chunkedMacComputation.update(ByteBuffer.wrap(plaintext)); 188 byte[] tag = chunkedMacComputation.computeMac(); 189 Mac mac = keysetHandle.getPrimitive(RegistryConfiguration.get(), Mac.class); 190 191 mac.verifyMac(tag, plaintext); 192 } 193 } 194