// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package com.google.crypto.tink; import static com.google.common.truth.Truth.assertThat; import static com.google.crypto.tink.testing.TestUtil.assertExceptionContains; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import com.google.crypto.tink.config.TinkConfig; import com.google.crypto.tink.internal.MonitoringAnnotations; import com.google.crypto.tink.internal.MutableMonitoringRegistry; import com.google.crypto.tink.internal.testing.FakeMonitoringClient; import com.google.crypto.tink.proto.Keyset; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for CleartextKeysetHandle. */ @RunWith(JUnit4.class) public class CleartextKeysetHandleTest { @BeforeClass public static void setUp() throws GeneralSecurityException { TinkConfig.register(); } @Test public void testParse() throws Exception { // Create a keyset that contains a single HmacKey. KeyTemplate template = KeyTemplates.get("HMAC_SHA256_128BITTAG"); KeysetHandle handle = KeysetHandle.generateNew(template); Keyset keyset = CleartextKeysetHandle.getKeyset(handle); handle = CleartextKeysetHandle.parseFrom(keyset.toByteArray()); assertEquals(keyset, handle.getKeyset()); Object unused = handle.getPrimitive(RegistryConfiguration.get(), Mac.class); } @Test public void testRead() throws Exception { // Create a keyset that contains a single HmacKey. KeyTemplate template = KeyTemplates.get("HMAC_SHA256_128BITTAG"); KeysetHandle handle = KeysetHandle.generateNew(template); Keyset keyset1 = handle.getKeyset(); KeysetHandle handle1 = CleartextKeysetHandle.read(BinaryKeysetReader.withBytes(keyset1.toByteArray())); assertEquals(keyset1, handle1.getKeyset()); assertThat(handle1.size()).isEqualTo(1); assertThat(handle1.getAt(0).getId()).isEqualTo(handle.getAt(0).getId()); assertThat(handle1.getAt(0).getStatus()).isEqualTo(handle.getAt(0).getStatus()); assertTrue(handle1.getAt(0).getKey().equalsKey(handle.getAt(0).getKey())); } @Test public void testWriteRead_samePrimitive() throws Exception { // Create a keyset that contains a single HmacKey. KeysetHandle handle = KeysetHandle.generateNew(KeyTemplates.get("HMAC_SHA256_128BITTAG")); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); KeysetWriter writer = BinaryKeysetWriter.withOutputStream(outputStream); CleartextKeysetHandle.write(handle, writer); byte[] serializedKeyset = outputStream.toByteArray(); ByteArrayInputStream inputStream1 = new ByteArrayInputStream(serializedKeyset); KeysetReader reader1 = BinaryKeysetReader.withInputStream(inputStream1); KeysetHandle readHandle1 = CleartextKeysetHandle.read(reader1); ByteArrayInputStream inputStream2 = new ByteArrayInputStream(serializedKeyset); KeysetReader reader2 = BinaryKeysetReader.withInputStream(inputStream2); KeysetHandle readHandle2 = CleartextKeysetHandle.read(reader2, new HashMap()); // Check that the handle returned by CleartextKeysetHandle.read generates the same MAC. Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class); Mac readMac1 = readHandle1.getPrimitive(RegistryConfiguration.get(), Mac.class); Mac readMac2 = readHandle2.getPrimitive(RegistryConfiguration.get(), Mac.class); byte[] data = "data".getBytes(UTF_8); assertThat(readMac1.computeMac(data)).isEqualTo(mac.computeMac(data)); assertThat(readMac2.computeMac(data)).isEqualTo(mac.computeMac(data)); } @Test public void testReadInvalidKeyset() throws Exception { // Create a keyset that contains a single HmacKey. KeyTemplate template = KeyTemplates.get("HMAC_SHA256_128BITTAG"); Keyset keyset = KeysetHandle.generateNew(template).getKeyset(); byte[] proto = keyset.toByteArray(); proto[0] = (byte) ~proto[0]; assertThrows( IOException.class, () -> { KeysetHandle unused = CleartextKeysetHandle.read(BinaryKeysetReader.withBytes(proto)); }); assertThrows( IOException.class, () -> { KeysetHandle unused = CleartextKeysetHandle.read( BinaryKeysetReader.withBytes(proto), new HashMap()); }); } @Test public void testVoidInputs() throws Exception { GeneralSecurityException e = assertThrows( GeneralSecurityException.class, () -> CleartextKeysetHandle.read(BinaryKeysetReader.withBytes(new byte[0]))); assertExceptionContains(e, "empty keyset"); GeneralSecurityException e2 = assertThrows( GeneralSecurityException.class, () -> CleartextKeysetHandle.read( BinaryKeysetReader.withBytes(new byte[0]), new HashMap())); assertExceptionContains(e2, "empty keyset"); GeneralSecurityException e3 = assertThrows( GeneralSecurityException.class, () -> CleartextKeysetHandle.parseFrom(new byte[0])); assertExceptionContains(e3, "empty keyset"); } @Test public void testReadWithAnnotations_getLoggedByMonitoringClient() throws Exception { FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient(); MutableMonitoringRegistry.globalInstance().clear(); MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient); // Generate a serialized keyset ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CleartextKeysetHandle.write( KeysetHandle.generateNew(KeyTemplates.get("HMAC_SHA256_128BITTAG")), BinaryKeysetWriter.withOutputStream(outputStream)); byte[] serializedKeyset = outputStream.toByteArray(); Map annotations = new HashMap<>(); annotations.put("annotation_name", "annotation_value"); KeysetHandle handle = CleartextKeysetHandle.read(BinaryKeysetReader.withBytes(serializedKeyset), annotations); // Trigger monitoring event and verify that it gets logged with the annotations are set. Mac mac = handle.getPrimitive(RegistryConfiguration.get(), Mac.class); byte[] unused = mac.computeMac("data".getBytes(UTF_8)); List logEntries = fakeMonitoringClient.getLogEntries(); assertThat(logEntries).hasSize(1); FakeMonitoringClient.LogEntry entry = logEntries.get(0); MonitoringAnnotations expectedAnnotations = MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build(); assertThat(entry.getKeysetInfo().getAnnotations()).isEqualTo(expectedAnnotations); } }