blob: f4e5fd176d9ad5a45733a53191ac120df6a9d777 [file] [log] [blame]
// 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.aead;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.KeyTemplate;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.KmsClients;
import com.google.crypto.tink.internal.KeyTypeManager;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.KmsEnvelopeAeadKey;
import com.google.crypto.tink.proto.KmsEnvelopeAeadKeyFormat;
import com.google.crypto.tink.subtle.Random;
import com.google.crypto.tink.testing.FakeKmsClient;
import com.google.crypto.tink.testing.TestUtil;
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for {@code KmsEnvelopeAead} and {@code KmsEnvelopeAeadKeyManager}.
*/
@RunWith(JUnit4.class)
public class KmsEnvelopeAeadKeyManagerTest {
private final KmsEnvelopeAeadKeyManager manager = new KmsEnvelopeAeadKeyManager();
private final KeyTypeManager.KeyFactory<KmsEnvelopeAeadKeyFormat, KmsEnvelopeAeadKey> factory =
manager.keyFactory();
@BeforeClass
public static void setUp() throws Exception {
KmsClients.add(new FakeKmsClient());
AeadConfig.register();
}
@Test
public void basics() throws Exception {
assertThat(manager.getKeyType())
.isEqualTo("type.googleapis.com/google.crypto.tink.KmsEnvelopeAeadKey");
assertThat(manager.getVersion()).isEqualTo(0);
assertThat(manager.keyMaterialType()).isEqualTo(KeyMaterialType.REMOTE);
}
@Test
public void validateKeyFormat_empty() throws Exception {
assertThrows(
GeneralSecurityException.class,
() -> factory.validateKeyFormat(KmsEnvelopeAeadKeyFormat.getDefaultInstance()));
}
@Test
public void validateKeyFormat_noKekUri() throws Exception {
assertThrows(
GeneralSecurityException.class,
() ->
factory.validateKeyFormat(
KmsEnvelopeAeadKeyFormat.newBuilder()
.setDekTemplate(
com.google.crypto.tink.proto.KeyTemplate.newBuilder()
.setTypeUrl("foo")
.setValue(ByteString.EMPTY)
.build())
.build()));
}
@Test
public void validateKeyFormat_noDekTemplate() throws Exception {
assertThrows(
GeneralSecurityException.class,
() ->
factory.validateKeyFormat(
KmsEnvelopeAeadKeyFormat.newBuilder().setKekUri("foo").build()));
}
@Test
public void createKey() throws Exception {
String kekUri = FakeKmsClient.createFakeKeyUri();
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KmsEnvelopeAeadKey key =
factory.createKey(KmsEnvelopeAeadKeyManager.createKeyFormat(kekUri, dekTemplate));
Aead aead = manager.getPrimitive(key, Aead.class);
byte[] plaintext = Random.randBytes(20);
byte[] associatedData = Random.randBytes(20);
assertThat(aead.decrypt(aead.encrypt(plaintext, associatedData), associatedData))
.isEqualTo(plaintext);
}
@Test
public void createKey_multipleKeysWithSameKek() throws Exception {
String kekUri = FakeKmsClient.createFakeKeyUri();
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KmsEnvelopeAeadKey key1 =
factory.createKey(KmsEnvelopeAeadKeyManager.createKeyFormat(kekUri, dekTemplate));
Aead aead1 = manager.getPrimitive(key1, Aead.class);
KmsEnvelopeAeadKey key2 =
factory.createKey(KmsEnvelopeAeadKeyManager.createKeyFormat(kekUri, dekTemplate));
Aead aead2 = manager.getPrimitive(key2, Aead.class);
byte[] plaintext = Random.randBytes(20);
byte[] associatedData = Random.randBytes(20);
assertThat(aead1.decrypt(aead2.encrypt(plaintext, associatedData), associatedData))
.isEqualTo(plaintext);
}
@Test
public void getPrimitive() throws Exception {
String kekUri = FakeKmsClient.createFakeKeyUri();
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KmsEnvelopeAeadKey key =
factory.createKey(KmsEnvelopeAeadKeyManager.createKeyFormat(kekUri, dekTemplate));
Aead aead = manager.getPrimitive(key, Aead.class);
TestUtil.runBasicAeadTests(aead);
}
@Test
public void getPrimitive_parsingInvalidCiphetexts() throws Exception {
String kekUri = FakeKmsClient.createFakeKeyUri();
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KmsEnvelopeAeadKey key =
factory.createKey(KmsEnvelopeAeadKeyManager.createKeyFormat(kekUri, dekTemplate));
Aead aead = manager.getPrimitive(key, Aead.class);
byte[] plaintext = Random.randBytes(20);
byte[] aad = Random.randBytes(20);
byte[] ciphertext = aead.encrypt(plaintext, aad);
ByteBuffer buffer = ByteBuffer.wrap(ciphertext);
int encryptedDekSize = buffer.getInt();
byte[] encryptedDek = new byte[encryptedDekSize];
buffer.get(encryptedDek, 0, encryptedDekSize);
byte[] payload = new byte[buffer.remaining()];
buffer.get(payload, 0, buffer.remaining());
// valid, should work
byte[] ciphertext2 = ByteBuffer.allocate(ciphertext.length)
.putInt(encryptedDekSize)
.put(encryptedDek)
.put(payload)
.array();
assertArrayEquals(plaintext, aead.decrypt(ciphertext2, aad));
// negative length
byte[] ciphertext3 =
ByteBuffer.allocate(ciphertext.length)
.putInt(-1)
.put(encryptedDek)
.put(payload)
.array();
assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext3, aad));
// length larger than actual value
byte[] ciphertext4 =
ByteBuffer.allocate(ciphertext.length)
.putInt(encryptedDek.length + 1)
.put(encryptedDek)
.put(payload)
.array();
assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext4, aad));
// length larger than total ciphertext length
byte[] ciphertext5 =
ByteBuffer.allocate(ciphertext.length)
.putInt(encryptedDek.length + payload.length + 1)
.put(encryptedDek)
.put(payload)
.array();
assertThrows(GeneralSecurityException.class, () -> aead.decrypt(ciphertext5, aad));
}
@Test
public void createKeyTemplate() throws Exception {
// Intentionally using "weird" or invalid values for parameters,
// to test that the function correctly puts them in the resulting template.
String kekUri = "some example KEK URI";
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KeyTemplate template = KmsEnvelopeAeadKeyManager.createKeyTemplate(kekUri, dekTemplate);
assertThat(new KmsEnvelopeAeadKeyManager().getKeyType()).isEqualTo(template.getTypeUrl());
assertThat(KeyTemplate.OutputPrefixType.RAW).isEqualTo(template.getOutputPrefixType());
KmsEnvelopeAeadKeyFormat format =
KmsEnvelopeAeadKeyFormat.parseFrom(
template.getValue(), ExtensionRegistryLite.getEmptyRegistry());
assertThat(kekUri).isEqualTo(format.getKekUri());
assertThat(dekTemplate.getTypeUrl()).isEqualTo(format.getDekTemplate().getTypeUrl());
assertThat(dekTemplate.getValue()).isEqualTo(format.getDekTemplate().getValue().toByteArray());
}
@Test
public void createKeyTemplate_multipleKeysWithSameKek() throws Exception {
String kekUri = FakeKmsClient.createFakeKeyUri();
KeyTemplate dekTemplate = AesCtrHmacAeadKeyManager.aes128CtrHmacSha256Template();
KeyTemplate kt1 = KmsEnvelopeAeadKeyManager.createKeyTemplate(kekUri, dekTemplate);
KeysetHandle handle1 = KeysetHandle.generateNew(kt1);
Aead aead1 = handle1.getPrimitive(Aead.class);
KeyTemplate kt2 = KmsEnvelopeAeadKeyManager.createKeyTemplate(kekUri, dekTemplate);
KeysetHandle handle2 = KeysetHandle.generateNew(kt2);
Aead aead2 = handle2.getPrimitive(Aead.class);
byte[] plaintext = Random.randBytes(20);
byte[] associatedData = Random.randBytes(20);
assertThat(aead1.decrypt(aead2.encrypt(plaintext, associatedData), associatedData))
.isEqualTo(plaintext);
}
}