blob: ab6356b8c0bb3327233f8bfabb0e64445d091720 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// 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.hybrid.internal;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertThrows;
import com.google.common.io.Files;
import com.google.common.truth.Expect;
import com.google.crypto.tink.testing.HpkeTestId;
import com.google.crypto.tink.testing.HpkeTestSetup;
import com.google.crypto.tink.testing.HpkeTestUtil;
import com.google.crypto.tink.testing.HpkeTestVector;
import com.google.crypto.tink.testing.TestUtil;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link X25519HpkeKem}. */
@RunWith(JUnit4.class)
public final class X25519HpkeKemTest {
private static final byte[] EXPORT_ONLY_AEAD_ID = HpkeUtil.intToByteArray(2, 0xffff);
private static final String MAC_ALGORITHM = "HmacSha256";
private static Map<HpkeTestId, HpkeTestVector> testVectors;
@Rule public final Expect expect = Expect.create();
@BeforeClass
public static void setUpTestVectors() throws IOException {
String path = "testdata/testvectors/hpke_boringssl.json";
if (TestUtil.isAndroid()) {
path = "/sdcard/googletest/test_runfiles/google3/" + path; // Special prefix for Android.
}
testVectors = HpkeTestUtil.parseTestVectors(Files.newReader(new File(path), UTF_8));
}
private HpkeTestId getDefaultTestId() {
return new HpkeTestId(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.AES_128_GCM_AEAD_ID);
}
private void encapsulate(byte[] mode, byte[] kemId, byte[] kdfId, byte[] aeadId)
throws GeneralSecurityException {
HpkeTestId testId = new HpkeTestId(mode, kemId, kdfId, aeadId);
HpkeTestSetup testSetup = testVectors.get(testId).getTestSetup();
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf(MAC_ALGORITHM));
HpkeKemEncapOutput result =
kem.encapsulate(testSetup.recipientPublicKey, testSetup.senderPrivateKey);
expect.that(result.getSharedSecret()).isEqualTo(testSetup.sharedSecret);
expect.that(result.getEncapsulatedKey()).isEqualTo(testSetup.encapsulatedKey);
}
private void decapsulate(byte[] mode, byte[] kemId, byte[] kdfId, byte[] aeadId)
throws GeneralSecurityException {
HpkeTestId testId = new HpkeTestId(mode, kemId, kdfId, aeadId);
HpkeTestSetup testSetup = testVectors.get(testId).getTestSetup();
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf(MAC_ALGORITHM));
byte[] result =
kem.decapsulate(
testSetup.encapsulatedKey,
X25519HpkeKemPrivateKey.fromBytes(testSetup.recipientPrivateKey));
expect.that(result).isEqualTo(testSetup.sharedSecret);
}
@Test
public void encapsulate_succeedsWithX25519HkdfSha256Aes128Gcm() throws GeneralSecurityException {
encapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.AES_128_GCM_AEAD_ID);
}
@Test
public void encapsulate_succeedsWithX25519HkdfSha256Aes256Gcm() throws GeneralSecurityException {
encapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.AES_256_GCM_AEAD_ID);
}
@Test
public void encapsulate_succeedsWithX25519HkdfSha256ChaCha20Poly1305()
throws GeneralSecurityException {
encapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.CHACHA20_POLY1305_AEAD_ID);
}
@Test
public void encapsulate_succeedsWithX25519HkdfSha256ExportOnlyAead()
throws GeneralSecurityException {
encapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
EXPORT_ONLY_AEAD_ID);
}
@Test
public void encapsulate_failsWithInvalidMacAlgorithm() {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf("BadMac"));
HpkeTestSetup testSetup = testVectors.get(getDefaultTestId()).getTestSetup();
byte[] validRecipientPublicKey = testSetup.recipientPublicKey;
assertThrows(NoSuchAlgorithmException.class, () -> kem.encapsulate(validRecipientPublicKey));
}
@Test
public void encapsulate_failsWithInvalidRecipientPublicKey() {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf(MAC_ALGORITHM));
HpkeTestSetup testSetup = testVectors.get(getDefaultTestId()).getTestSetup();
byte[] invalidRecipientPublicKey =
Arrays.copyOf(testSetup.recipientPublicKey, testSetup.recipientPublicKey.length + 2);
assertThrows(InvalidKeyException.class, () -> kem.encapsulate(invalidRecipientPublicKey));
}
@Test
public void decapsulate_succeedsWithX25519HkdfSha256Aes128Gcm() throws GeneralSecurityException {
decapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.AES_128_GCM_AEAD_ID);
}
@Test
public void decapsulate_succeedsWithX25519HkdfSha256Aes256Gcm() throws GeneralSecurityException {
decapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.AES_256_GCM_AEAD_ID);
}
@Test
public void decapsulate_succeedsWithX25519HkdfSha256ChaCha20Poly1305()
throws GeneralSecurityException {
decapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
HpkeUtil.CHACHA20_POLY1305_AEAD_ID);
}
@Test
public void decapsulate_succeedsWithX25519HkdfSha256ExportOnlyAead()
throws GeneralSecurityException {
decapsulate(
HpkeUtil.BASE_MODE,
HpkeUtil.X25519_HKDF_SHA256_KEM_ID,
HpkeUtil.HKDF_SHA256_KDF_ID,
EXPORT_ONLY_AEAD_ID);
}
@Test
public void decapsulate_failsWithInvalidMacAlgorithm() throws GeneralSecurityException {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf("BadMac"));
HpkeTestSetup testSetup = testVectors.get(getDefaultTestId()).getTestSetup();
byte[] validEncapsulatedKey = testSetup.encapsulatedKey;
HpkeKemPrivateKey validRecipientPrivateKey =
X25519HpkeKemPrivateKey.fromBytes(testSetup.recipientPrivateKey);
assertThrows(
NoSuchAlgorithmException.class,
() -> kem.decapsulate(validEncapsulatedKey, validRecipientPrivateKey));
}
@Test
public void decapsulate_failsWithInvalidEncapsulatedPublicKey() throws GeneralSecurityException {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf(MAC_ALGORITHM));
HpkeTestSetup testSetup = testVectors.get(getDefaultTestId()).getTestSetup();
byte[] invalidEncapsulatedKey =
Arrays.copyOf(testSetup.encapsulatedKey, testSetup.encapsulatedKey.length + 2);
HpkeKemPrivateKey validRecipientPrivateKey =
X25519HpkeKemPrivateKey.fromBytes(testSetup.recipientPrivateKey);
assertThrows(
InvalidKeyException.class,
() -> kem.decapsulate(invalidEncapsulatedKey, validRecipientPrivateKey));
}
@Test
public void getKemId_succeeds() throws GeneralSecurityException {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf(MAC_ALGORITHM));
expect.that(kem.getKemId()).isEqualTo(HpkeUtil.X25519_HKDF_SHA256_KEM_ID);
}
@Test
public void getKemId_failsWithInvalidMacAlgorithm() {
X25519HpkeKem kem = new X25519HpkeKem(new HkdfHpkeKdf("BadMac"));
assertThrows(GeneralSecurityException.class, kem::getKemId);
}
}