1 // Copyright 2020 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 package com.google.crypto.tink.prf; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static java.nio.charset.StandardCharsets.UTF_8; 20 import static org.junit.Assert.assertArrayEquals; 21 import static org.junit.Assert.assertThrows; 22 23 import com.google.crypto.tink.InsecureSecretKeyAccess; 24 import com.google.crypto.tink.KeysetHandle; 25 import com.google.crypto.tink.Registry; 26 import com.google.crypto.tink.RegistryConfiguration; 27 import com.google.crypto.tink.internal.LegacyKeyManagerImpl; 28 import com.google.crypto.tink.internal.MonitoringAnnotations; 29 import com.google.crypto.tink.internal.MutableKeyCreationRegistry; 30 import com.google.crypto.tink.internal.MutableMonitoringRegistry; 31 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 32 import com.google.crypto.tink.internal.PrimitiveConstructor; 33 import com.google.crypto.tink.internal.PrimitiveRegistry; 34 import com.google.crypto.tink.internal.testing.FakeMonitoringClient; 35 import com.google.crypto.tink.prf.HkdfPrfParameters.HashType; 36 import com.google.crypto.tink.prf.internal.HkdfPrfProtoSerialization; 37 import com.google.crypto.tink.proto.KeyData.KeyMaterialType; 38 import com.google.crypto.tink.subtle.Hex; 39 import com.google.crypto.tink.util.SecretBytes; 40 import com.google.errorprone.annotations.Immutable; 41 import java.security.GeneralSecurityException; 42 import java.util.List; 43 import org.junit.BeforeClass; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.JUnit4; 47 48 /** Tests for PrfSetWrapper. */ 49 @RunWith(JUnit4.class) 50 public class PrfSetWrapperTest { 51 private static final int KEY_SIZE = 32; 52 53 private static HkdfPrfKey hkdfPrfKey0; 54 private static HkdfPrfKey hkdfPrfKey1; 55 private static HkdfPrfKey hkdfPrfKeyFixed; 56 57 @BeforeClass setUp()58 public static void setUp() throws Exception { 59 createTestKeys(); 60 } 61 createTestKeys()62 private static void createTestKeys() throws GeneralSecurityException { 63 hkdfPrfKey0 = 64 HkdfPrfKey.builder() 65 .setParameters( 66 HkdfPrfParameters.builder() 67 .setKeySizeBytes(KEY_SIZE) 68 .setHashType(HashType.SHA256) 69 .build()) 70 .setKeyBytes(SecretBytes.randomBytes(KEY_SIZE)) 71 .build(); 72 hkdfPrfKey1 = 73 HkdfPrfKey.builder() 74 .setParameters( 75 HkdfPrfParameters.builder() 76 .setKeySizeBytes(KEY_SIZE) 77 .setHashType(HashType.SHA256) 78 .build()) 79 .setKeyBytes(SecretBytes.randomBytes(KEY_SIZE)) 80 .build(); 81 hkdfPrfKeyFixed = 82 HkdfPrfKey.builder() 83 .setParameters( 84 HkdfPrfParameters.builder() 85 .setKeySizeBytes(KEY_SIZE) 86 .setHashType(HashType.SHA256) 87 .build()) 88 .setKeyBytes( 89 SecretBytes.copyFrom( 90 Hex.decode("0000000000000000000000000000000000000000000000000000000000000000"), 91 InsecureSecretKeyAccess.get())) 92 .build(); 93 } 94 95 @Test compute_works()96 public void compute_works() throws Exception { 97 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 98 PrfConfig.register(); 99 100 KeysetHandle keysetHandle = 101 KeysetHandle.newBuilder() 102 .addEntry(KeysetHandle.importKey(hkdfPrfKeyFixed).withFixedId(42).makePrimary()) 103 .build(); 104 PrfSet prfSet = keysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 105 byte[] plaintext = "blah".getBytes(UTF_8); 106 107 byte[] prs = prfSet.computePrimary(plaintext, 12); 108 109 assertThat(prfSet.getPrfs()).hasSize(1); 110 assertThat(prs).isEqualTo(Hex.decode("04f108788845580686b70d61")); 111 } 112 113 @Test compute_usesPrimaryKey()114 public void compute_usesPrimaryKey() throws Exception { 115 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 116 PrfConfig.register(); 117 118 KeysetHandle keysetHandle = 119 KeysetHandle.newBuilder() 120 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(42).makePrimary()) 121 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(43)) 122 .build(); 123 PrfSet prfSet = keysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 124 byte[] plaintext = "blah".getBytes(UTF_8); 125 126 byte[] prs = prfSet.computePrimary(plaintext, 12); 127 byte[] prsPrimary = prfSet.getPrfs().get(42).compute(plaintext, 12); 128 129 assertThat(prfSet.getPrimaryId()).isEqualTo(42); 130 assertArrayEquals(prsPrimary, prs); 131 } 132 133 @Test prfsCorrespondToCorrectKeys()134 public void prfsCorrespondToCorrectKeys() throws Exception { 135 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 136 PrfConfig.register(); 137 138 KeysetHandle keysetHandle = 139 KeysetHandle.newBuilder() 140 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(42).makePrimary()) 141 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(43)) 142 .build(); 143 KeysetHandle singleKeyKeysetHandle = 144 KeysetHandle.newBuilder() 145 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(43).makePrimary()) 146 .build(); 147 PrfSet prfSet = keysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 148 PrfSet singleKeyPrfSet = 149 singleKeyKeysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 150 byte[] plaintext = "blah".getBytes(UTF_8); 151 152 byte[] prs = prfSet.getPrfs().get(43).compute(plaintext, 12); 153 byte[] singleKeyPrs = singleKeyPrfSet.computePrimary(plaintext, 12); 154 155 assertArrayEquals(singleKeyPrs, prs); 156 } 157 158 @Test getPrfs_containsOnlyExistingKeys()159 public void getPrfs_containsOnlyExistingKeys() throws Exception { 160 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 161 PrfConfig.register(); 162 163 KeysetHandle keysetHandle = 164 KeysetHandle.newBuilder() 165 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(42).makePrimary()) 166 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(43)) 167 .build(); 168 PrfSet prfSet = keysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 169 170 assertThat(prfSet.getPrfs().keySet()).containsExactly(42, 43); 171 } 172 173 @Test testWithEmptyAnnotations_noMonitoring()174 public void testWithEmptyAnnotations_noMonitoring() throws Exception { 175 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 176 PrfConfig.register(); 177 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 178 MutableMonitoringRegistry.globalInstance().clear(); 179 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 180 181 KeysetHandle keysetHandle = 182 KeysetHandle.newBuilder() 183 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(42).makePrimary()) 184 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(43)) 185 .build(); 186 PrfSet prfSet = keysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 187 byte[] plaintext = "blah".getBytes(UTF_8); 188 189 byte[] unused = prfSet.computePrimary(plaintext, 12); 190 unused = prfSet.getPrfs().get(42).compute(plaintext, 12); 191 unused = prfSet.getPrfs().get(43).compute(plaintext, 12); 192 193 // Without annotations, nothing gets logged. 194 assertThat(fakeMonitoringClient.getLogEntries()).isEmpty(); 195 assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty(); 196 } 197 198 @Test testWithAnnotations_hasMonitoring()199 public void testWithAnnotations_hasMonitoring() throws Exception { 200 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 201 PrfConfig.register(); 202 203 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 204 MutableMonitoringRegistry.globalInstance().clear(); 205 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 206 207 MonitoringAnnotations annotations = 208 MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build(); 209 KeysetHandle hkdfKeysetHandle = 210 KeysetHandle.newBuilder() 211 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(5).makePrimary()) 212 .addEntry(KeysetHandle.importKey(hkdfPrfKey1).withFixedId(6)) 213 .setMonitoringAnnotations(annotations) 214 .build(); 215 byte[] plaintext = "blah".getBytes(UTF_8); 216 217 PrfSet prfSet = hkdfKeysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 218 byte[] prsPrimary = prfSet.computePrimary(plaintext, 12); 219 byte[] prs5 = prfSet.getPrfs().get(5).compute(plaintext, 12); 220 byte[] prs6 = prfSet.getPrfs().get(6).compute(plaintext, 12); 221 222 assertThat(prfSet.getPrimaryId()).isEqualTo(5); 223 224 assertThat(prfSet.getPrfs()).hasSize(2); 225 assertThat(prsPrimary).hasLength(12); 226 assertThat(prs5).isEqualTo(prsPrimary); 227 assertThat(prsPrimary).isNotEqualTo(prs6); 228 229 assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty(); 230 231 List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries(); 232 assertThat(logEntries).hasSize(3); 233 FakeMonitoringClient.LogEntry entry0 = logEntries.get(0); 234 assertThat(entry0.getKeyId()).isEqualTo(5); 235 assertThat(entry0.getPrimitive()).isEqualTo("prf"); 236 assertThat(entry0.getApi()).isEqualTo("compute"); 237 assertThat(entry0.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 238 FakeMonitoringClient.LogEntry entry1 = logEntries.get(1); 239 assertThat(entry1.getKeyId()).isEqualTo(5); 240 assertThat(entry1.getPrimitive()).isEqualTo("prf"); 241 assertThat(entry1.getApi()).isEqualTo("compute"); 242 assertThat(entry1.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 243 FakeMonitoringClient.LogEntry entry2 = logEntries.get(2); 244 assertThat(entry2.getKeyId()).isEqualTo(6); 245 assertThat(entry2.getPrimitive()).isEqualTo("prf"); 246 assertThat(entry2.getApi()).isEqualTo("compute"); 247 assertThat(entry2.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 248 } 249 250 @Immutable 251 private static class AlwaysFailingPrf implements Prf { 252 AlwaysFailingPrf(HkdfPrfKey key)253 AlwaysFailingPrf(HkdfPrfKey key) {} 254 255 @Override compute(byte[] input, int outputLength)256 public byte[] compute(byte[] input, int outputLength) throws GeneralSecurityException { 257 throw new GeneralSecurityException("fail"); 258 } 259 } 260 261 /** Perform registrations such as HkdfKeyManager.register, but with a failing PRF */ doHkdfKeyManagerRegistrationWithFailingPrf()262 private static void doHkdfKeyManagerRegistrationWithFailingPrf() throws Exception { 263 HkdfPrfProtoSerialization.register(); 264 MutablePrimitiveRegistry.globalInstance() 265 .registerPrimitiveConstructor( 266 PrimitiveConstructor.create(AlwaysFailingPrf::new, HkdfPrfKey.class, Prf.class)); 267 MutableKeyCreationRegistry.globalInstance() 268 .add(HkdfPrfKeyManager.KEY_CREATOR, HkdfPrfParameters.class); 269 Registry.registerKeyManager( 270 LegacyKeyManagerImpl.create( 271 HkdfPrfKeyManager.getKeyType(), 272 Prf.class, 273 KeyMaterialType.SYMMETRIC, 274 com.google.crypto.tink.proto.HkdfPrfKey.parser()), 275 true); 276 } 277 278 @Test testAlwaysFailingPrfWithAnnotations_hasMonitoring()279 public void testAlwaysFailingPrfWithAnnotations_hasMonitoring() throws Exception { 280 MutablePrimitiveRegistry.resetGlobalInstanceTestOnly(); 281 PrfSetWrapper.register(); 282 doHkdfKeyManagerRegistrationWithFailingPrf(); 283 284 FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); 285 MutableMonitoringRegistry.globalInstance().clear(); 286 MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); 287 288 MonitoringAnnotations annotations = 289 MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build(); 290 KeysetHandle hkdfKeysetHandle = 291 KeysetHandle.newBuilder() 292 .addEntry(KeysetHandle.importKey(hkdfPrfKey0).withFixedId(5).makePrimary()) 293 .setMonitoringAnnotations(annotations) 294 .build(); 295 PrfSet prfSet = hkdfKeysetHandle.getPrimitive(RegistryConfiguration.get(), PrfSet.class); 296 byte[] plaintext = "blah".getBytes(UTF_8); 297 298 assertThrows(GeneralSecurityException.class, () -> prfSet.computePrimary(plaintext, 12)); 299 assertThrows( 300 GeneralSecurityException.class, () -> prfSet.getPrfs().get(5).compute(plaintext, 12)); 301 302 assertThat(fakeMonitoringClient.getLogEntries()).isEmpty(); 303 304 List<FakeMonitoringClient.LogFailureEntry> failures = 305 fakeMonitoringClient.getLogFailureEntries(); 306 assertThat(failures).hasSize(2); 307 FakeMonitoringClient.LogFailureEntry failure0 = failures.get(0); 308 assertThat(failure0.getPrimitive()).isEqualTo("prf"); 309 assertThat(failure0.getApi()).isEqualTo("compute"); 310 assertThat(failure0.getKeysetInfo().getPrimaryKeyId()).isEqualTo(5); 311 assertThat(failure0.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 312 FakeMonitoringClient.LogFailureEntry failure1 = failures.get(1); 313 assertThat(failure1.getPrimitive()).isEqualTo("prf"); 314 assertThat(failure1.getApi()).isEqualTo("compute"); 315 assertThat(failure1.getKeysetInfo().getPrimaryKeyId()).isEqualTo(5); 316 assertThat(failure1.getKeysetInfo().getAnnotations()).isEqualTo(annotations); 317 } 318 319 @Test registerToInternalPrimitiveRegistry_works()320 public void registerToInternalPrimitiveRegistry_works() throws Exception { 321 PrimitiveRegistry.Builder initialBuilder = PrimitiveRegistry.builder(); 322 PrimitiveRegistry initialRegistry = initialBuilder.build(); 323 PrimitiveRegistry.Builder processedBuilder = PrimitiveRegistry.builder(initialRegistry); 324 325 PrfSetWrapper.registerToInternalPrimitiveRegistry(processedBuilder); 326 PrimitiveRegistry processedRegistry = processedBuilder.build(); 327 328 assertThrows( 329 GeneralSecurityException.class, () -> initialRegistry.getInputPrimitiveClass(PrfSet.class)); 330 assertThat(processedRegistry.getInputPrimitiveClass(PrfSet.class)).isEqualTo(Prf.class); 331 } 332 } 333