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.prf; 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 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 Prf package. Uses only the public API. */ 40 @RunWith(Theories.class) 41 public final class PrfTest { 42 43 @BeforeClass setUp()44 public static void setUp() throws Exception { 45 PrfConfig.register(); 46 DeterministicAeadConfig.register(); // Needed for getPrimitiveFromNonMacKeyset_throws. 47 } 48 49 @DataPoints("templates") 50 public static final String[] TEMPLATES = 51 new String[] { 52 "AES_CMAC_PRF", 53 "HMAC_SHA256_PRF", 54 "HMAC_SHA512_PRF", 55 "HKDF_SHA256", 56 }; 57 58 @Theory create_computeVerify(@romDataPoints"templates") String templateName)59 public void create_computeVerify(@FromDataPoints("templates") String templateName) 60 throws Exception { 61 KeysetHandle handle = KeysetHandle.generateNew(KeyTemplates.get(templateName)); 62 int primaryId = handle.getPrimary().getId(); 63 PrfSet prfSet = handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 64 65 byte[] data = "data".getBytes(UTF_8); 66 byte[] outputPrimary = prfSet.computePrimary(data, 12); 67 byte[] output = prfSet.getPrfs().get(primaryId).compute(data, 12); 68 assertThat(output).isEqualTo(outputPrimary); 69 70 int invalidId = primaryId + 1; 71 assertThat(prfSet.getPrfs().get(invalidId)).isNull(); 72 } 73 74 // A keyset with one MAC key, serialized in Tink's JSON format. 75 private static final String JSON_PRF_KEYSET = 76 "" 77 + "{" 78 + " \"primaryKeyId\": 166506972," 79 + " \"key\": [" 80 + " {" 81 + " \"keyData\": {" 82 + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacPrfKey\"," 83 + " \"value\": \"GkAlMHOHF4em1ax2/xzlhOX9696c6OIuSuYJ//DmzMshOjeGDjVazNZZKXo" 84 + "yo+USpExayMyab+GtjOfCCVjsECxnEgIIBA==\"," 85 + " \"keyMaterialType\": \"SYMMETRIC\"" 86 + " }," 87 + " \"status\": \"ENABLED\"," 88 + " \"keyId\": 166506972," 89 + " \"outputPrefixType\": \"RAW\"" 90 + " }" 91 + " ]" 92 + "}"; 93 94 @Theory readKeysetEncryptDecrypt()95 public void readKeysetEncryptDecrypt() 96 throws Exception { 97 KeysetHandle handle = 98 TinkJsonProtoKeysetFormat.parseKeyset(JSON_PRF_KEYSET, InsecureSecretKeyAccess.get()); 99 PrfSet prfSet = handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 100 101 byte[] data = "data".getBytes(UTF_8); 102 byte[] output1 = prfSet.computePrimary(data, 12); 103 byte[] output2 = prfSet.getPrfs().get(166506972).compute(data, 12); 104 assertThat(output2).isEqualTo(output1); 105 } 106 107 // A keyset with multiple keys. The first key is the same as in JSON_AEAD_KEYSET. 108 private static final String JSON_PRF_KEYSET_WITH_MULTIPLE_KEYS = 109 "" 110 + "{" 111 + " \"primaryKeyId\": 1781110497," 112 + " \"key\": [" 113 + " {" 114 + " \"keyData\": {" 115 + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacPrfKey\"," 116 + " \"value\": \"GkAlMHOHF4em1ax2/xzlhOX9696c6OIuSuYJ//DmzMshOjeGDjVazNZZKXo" 117 + "yo+USpExayMyab+GtjOfCCVjsECxnEgIIBA==\"," 118 + " \"keyMaterialType\": \"SYMMETRIC\"" 119 + " }," 120 + " \"status\": \"ENABLED\"," 121 + " \"keyId\": 166506972," 122 + " \"outputPrefixType\": \"RAW\"" 123 + " }, {" 124 + " \"keyData\": {" 125 + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HkdfPrfKey\"," 126 + " \"value\": \"GiC+cZnHCSh8CGzIoe9/jYhJeyk+vNdVSH+77Rc+BaGNvxICCAM=\"," 127 + " \"keyMaterialType\": \"SYMMETRIC\"" 128 + " }," 129 + " \"status\": \"ENABLED\"," 130 + " \"keyId\": 1781110497," 131 + " \"outputPrefixType\": \"RAW\"" 132 + " }," 133 + " {" 134 + " \"keyData\": {" 135 + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.HmacPrfKey\"," 136 + " \"value\": \"GkBO8P7LMfUeCuqUZUY0xiAOi3q7lABfCA81kHv0qowLsjwmYwAa3leo9tD" 137 + "ez28gJtnWtghWQ3fVfWsZstNIOw0lEgIIBA==\"," 138 + " \"keyMaterialType\": \"SYMMETRIC\"" 139 + " }," 140 + " \"status\": \"ENABLED\"," 141 + " \"keyId\": 1593211602," 142 + " \"outputPrefixType\": \"RAW\"" 143 + " }" 144 + " ]" 145 + "}"; 146 147 @Theory multipleKeysReadKeysetWithEncryptDecrypt()148 public void multipleKeysReadKeysetWithEncryptDecrypt() 149 throws Exception { 150 KeysetHandle handle = 151 TinkJsonProtoKeysetFormat.parseKeyset( 152 JSON_PRF_KEYSET_WITH_MULTIPLE_KEYS, InsecureSecretKeyAccess.get()); 153 154 PrfSet prfSet = handle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 155 156 byte[] data = "data".getBytes(UTF_8); 157 byte[] outputPrimary = prfSet.computePrimary(data, 12); 158 159 byte[] output1 = prfSet.getPrfs().get(166506972).compute(data, 12); 160 assertThat(output1).isNotEqualTo(outputPrimary); 161 byte[] output2 = prfSet.getPrfs().get(1781110497).compute(data, 12); 162 assertThat(output2).isEqualTo(outputPrimary); 163 byte[] output3 = prfSet.getPrfs().get(1593211602).compute(data, 12); 164 assertThat(output3).isNotEqualTo(outputPrimary); 165 166 } 167 168 // A keyset with a valid DeterministicAead key. This keyset can't be used with the Mac primitive. 169 private static final String JSON_DAEAD_KEYSET = 170 "" 171 + "{" 172 + " \"primaryKeyId\": 961932622," 173 + " \"key\": [" 174 + " {" 175 + " \"keyData\": {" 176 + " \"typeUrl\": \"type.googleapis.com/google.crypto.tink.AesSivKey\"," 177 + " \"keyMaterialType\": \"SYMMETRIC\"," 178 + " \"value\": \"EkCJ9r5iwc5uxq5ugFyrHXh5dijTa7qalWUgZ8Gf08RxNd545FjtLMYL7ObcaFtCS" 179 + "kvV2+7u6F2DN+kqUjAfkf2W\"" 180 + " }," 181 + " \"outputPrefixType\": \"TINK\"," 182 + " \"keyId\": 961932622," 183 + " \"status\": \"ENABLED\"" 184 + " }" 185 + " ]" 186 + "}"; 187 188 @Theory getPrimitiveFromNonMacKeyset_throws()189 public void getPrimitiveFromNonMacKeyset_throws() throws Exception { 190 KeysetHandle handle = 191 TinkJsonProtoKeysetFormat.parseKeyset(JSON_DAEAD_KEYSET, InsecureSecretKeyAccess.get()); 192 // Test that the keyset can create a DeterministicAead primitive, but not a Mac. 193 Object unused = handle.getPrimitive(RegistryConfiguration.get(), DeterministicAead.class); 194 assertThrows( 195 GeneralSecurityException.class, 196 () -> handle.getPrimitive(RegistryConfiguration.get(), Mac.class)); 197 } 198 } 199