Skip to content

Commit

Permalink
Migrate the RsaSsaPss{Sign,Verify}KeyManagerTest to directly operate …
Browse files Browse the repository at this point in the history
…on the KeyTypeManager.

PiperOrigin-RevId: 267382292
  • Loading branch information
tholenst authored and copybara-github committed Sep 5, 2019
1 parent 488b99e commit 455c59d
Show file tree
Hide file tree
Showing 2 changed files with 372 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,173 +16,245 @@

package com.google.crypto.tink.signature;

import static org.junit.Assert.assertArrayEquals;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import com.google.crypto.tink.KeyManager;
import com.google.crypto.tink.KeyManagerImpl;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PrivateKeyManager;
import com.google.crypto.tink.PrivateKeyManagerImpl;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.TestUtil;
import com.google.crypto.tink.proto.KeyData;
import com.google.crypto.tink.proto.KeyTemplate;
import com.google.crypto.tink.proto.HashType;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.proto.RsaSsaPssKeyFormat;
import com.google.crypto.tink.proto.RsaSsaPssParams;
import com.google.crypto.tink.proto.RsaSsaPssPrivateKey;
import com.google.crypto.tink.proto.RsaSsaPssPublicKey;
import com.google.crypto.tink.subtle.EngineFactory;
import com.google.crypto.tink.subtle.Random;
import com.google.crypto.tink.subtle.RsaSsaPssVerifyJce;
import com.google.protobuf.ByteString;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Set;
import java.util.TreeSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Unit tests for RsaSsaPssSignKeyManager. */
@RunWith(JUnit4.class)
public class RsaSsaPssSignKeyManagerTest {
@Before
public void setUp() throws Exception {
SignatureConfig.register();
private final RsaSsaPssSignKeyManager manager = new RsaSsaPssSignKeyManager();
private final RsaSsaPssSignKeyManager.KeyFactory<RsaSsaPssKeyFormat, RsaSsaPssPrivateKey>
factory = manager.keyFactory();

private static RsaSsaPssKeyFormat createKeyFormat(
HashType sigHash,
HashType mgf1Hash,
int saltLength,
int modulusSizeInBits,
BigInteger publicExponent) {
return RsaSsaPssKeyFormat.newBuilder()
.setParams(
RsaSsaPssParams.newBuilder()
.setSigHash(sigHash)
.setMgf1Hash(mgf1Hash)
.setSaltLength(saltLength))
.setModulusSizeInBits(modulusSizeInBits)
.setPublicExponent(ByteString.copyFrom(publicExponent.toByteArray()))
.build();
}

private static RsaSsaPssKeyFormat validKeyFormat() {
return createKeyFormat(HashType.SHA256, HashType.SHA256, 32, 3072, RSAKeyGenParameterSpec.F4);
}

@Test
public void basics() throws Exception {
assertThat(manager.getKeyType())
.isEqualTo("type.googleapis.com/google.crypto.tink.RsaSsaPssPrivateKey");
assertThat(manager.getVersion()).isEqualTo(0);
assertThat(manager.keyMaterialType()).isEqualTo(KeyMaterialType.ASYMMETRIC_PRIVATE);
}

@Test
public void validateKeyFormat_empty() throws Exception {
try {
factory.validateKeyFormat(RsaSsaPssKeyFormat.getDefaultInstance());
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

@Test
public void validateKeyFormat_valid() throws Exception {
RsaSsaPssKeyFormat format = validKeyFormat();
factory.validateKeyFormat(format);
}

@Test
public void validateKeyFormat_Sha512Allowed() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA512, 32, 3072, RSAKeyGenParameterSpec.F4);
factory.validateKeyFormat(format);
}

final byte[] msg = Random.randBytes(20);
@Test
public void validateKeyFormat_Sha1Disallowed_throws() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA1, HashType.SHA1, 32, 3072, RSAKeyGenParameterSpec.F4);
try {
factory.validateKeyFormat(format);
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

@Test
public void validateKeyFormat_smallModulusDisallowed_throws() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA256, HashType.SHA256, 32, 512, RSAKeyGenParameterSpec.F4);
try {
factory.validateKeyFormat(format);
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

@Test
public void validateKeyFormat_hashMismatchDisallowed1_throws() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA256, 32, 3072, RSAKeyGenParameterSpec.F4);
try {
factory.validateKeyFormat(format);
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

@Test
public void validateKeyFormat_hashMismatchDisallowed2_throws() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA256, HashType.SHA512, 32, 3072, RSAKeyGenParameterSpec.F4);
try {
factory.validateKeyFormat(format);
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

@Test
public void validateKeyFormat_unkownHashDisallowed_throws() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(
HashType.UNKNOWN_HASH, HashType.UNKNOWN_HASH, 32, 3072, RSAKeyGenParameterSpec.F4);
try {
factory.validateKeyFormat(format);
fail();
} catch (GeneralSecurityException e) {
// expected
}
}

private static void checkConsistency(RsaSsaPssPrivateKey privateKey,
RsaSsaPssKeyFormat keyFormat) {
assertThat(privateKey.getPublicKey().getParams()).isEqualTo(keyFormat.getParams());
assertThat(privateKey.getPublicKey().getE()).isEqualTo(keyFormat.getPublicExponent());
assertThat(privateKey.getPublicKey().getN().toByteArray().length)
.isGreaterThan(keyFormat.getModulusSizeInBits() / 8);
}

private void checkKey(RsaSsaPssPrivateKey privateKey) throws Exception {
RsaSsaPssPublicKey publicKey = privateKey.getPublicKey();
assertThat(privateKey.getVersion()).isEqualTo(0);
assertThat(publicKey.getVersion()).isEqualTo(privateKey.getVersion());

BigInteger p = new BigInteger(1, privateKey.getP().toByteArray());
BigInteger q = new BigInteger(1, privateKey.getQ().toByteArray());
BigInteger n = new BigInteger(1, privateKey.getPublicKey().getN().toByteArray());
BigInteger d = new BigInteger(1, privateKey.getD().toByteArray());
BigInteger dp = new BigInteger(1, privateKey.getDp().toByteArray());
BigInteger dq = new BigInteger(1, privateKey.getDq().toByteArray());
BigInteger crt = new BigInteger(1, privateKey.getCrt().toByteArray());
assertThat(p).isGreaterThan(BigInteger.ONE);
assertThat(q).isGreaterThan(BigInteger.ONE);
assertEquals(n, p.multiply(q));
assertEquals(dp, d.mod(p.subtract(BigInteger.ONE)));
assertEquals(dq, d.mod(q.subtract(BigInteger.ONE)));
assertEquals(crt, q.modInverse(p));
}

private void testNewKeyWithVerifier(KeyTemplate keyTemplate) throws Exception {
if (TestUtil.isTsan()) {
// This test times out in tsan mode.
return;
}
// Call newKey multiple times and make sure that it generates different keys.
int numTests = 3;
RsaSsaPssPrivateKey[] privKeys = new RsaSsaPssPrivateKey[numTests];
PrivateKeyManager<PublicKeySign> signManager =
new PrivateKeyManagerImpl<>(
new RsaSsaPssSignKeyManager(), new RsaSsaPssVerifyKeyManager(), PublicKeySign.class);
Set<String> keys = new TreeSet<String>();

privKeys[0] =
(RsaSsaPssPrivateKey)
signManager.newKey(RsaSsaPssKeyFormat.parseFrom(keyTemplate.getValue()));
keys.add(TestUtil.hexEncode(privKeys[0].toByteArray()));

privKeys[1] = (RsaSsaPssPrivateKey) signManager.newKey(keyTemplate.getValue());
keys.add(TestUtil.hexEncode(privKeys[1].toByteArray()));

privKeys[2] =
RsaSsaPssPrivateKey.parseFrom(signManager.newKeyData(keyTemplate.getValue()).getValue());
keys.add(TestUtil.hexEncode(privKeys[2].toByteArray()));

assertEquals(numTests, keys.size());
// Check key.
for (int i = 0; i < numTests; i++) {
checkKey(privKeys[i]);
}

// Test whether signer works correctly with the corresponding verifier.
KeyManager<PublicKeyVerify> verifyManager =
new KeyManagerImpl<>(new RsaSsaPssVerifyKeyManager(), PublicKeyVerify.class);
for (int j = 0; j < numTests; j++) {
PublicKeySign signer = signManager.getPrimitive(privKeys[j]);
byte[] signature = signer.sign(msg);
for (int k = 0; k < numTests; k++) {
PublicKeyVerify verifier = verifyManager.getPrimitive(privKeys[k].getPublicKey());
if (j == k) { // The same key
try {
verifier.verify(signature, msg);
} catch (GeneralSecurityException ex) {
throw new AssertionError("Valid signature, should not throw exception", ex);
}
} else { // Different keys
try {
verifier.verify(signature, msg);
fail("Invalid signature, should have thrown exception");
} catch (GeneralSecurityException expected) {
// Expected
}
}
}
}
@Test
public void createKey_smallKey() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA256, HashType.SHA256, 32, 3072, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
checkConsistency(key, format);
checkKey(key);
}

@Test
public void testNewKeyWithVerifier() throws Exception {
testNewKeyWithVerifier(SignatureKeyTemplates.RSA_SSA_PSS_3072_SHA256_SHA256_32_F4);
testNewKeyWithVerifier(SignatureKeyTemplates.RSA_SSA_PSS_4096_SHA512_SHA512_64_F4);
public void createKey_largeKey() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA512, 64, 4096, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
checkConsistency(key, format);
checkKey(key);
}

@Test
public void testNewKeyWithCorruptedFormat() {
ByteString serialized = ByteString.copyFrom(new byte[128]);
KeyTemplate keyTemplate =
KeyTemplate.newBuilder()
.setTypeUrl(new RsaSsaPssSignKeyManager().getKeyType())
.setValue(serialized)
.build();
PrivateKeyManager<PublicKeySign> keyManager =
new PrivateKeyManagerImpl<>(
new RsaSsaPssSignKeyManager(), new RsaSsaPssVerifyKeyManager(), PublicKeySign.class);
try {
keyManager.newKey(serialized);
fail("Corrupted format, should have thrown exception");
} catch (GeneralSecurityException expected) {
// Expected
}
try {
keyManager.newKeyData(keyTemplate.getValue());
fail("Corrupted format, should have thrown exception");
} catch (GeneralSecurityException expected) {
// Expected
public void createKey_alwaysNewElement() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA256, HashType.SHA256, 32, 3072, RSAKeyGenParameterSpec.F4);
Set<String> keys = new TreeSet<>();
// Calls newKey multiple times and make sure that they generate different keys -- takes about a
// second per key.
int numTests = TestUtil.isTsan() ? 2 : 5;
for (int i = 0; i < numTests; i++) {
RsaSsaPssPrivateKey key = factory.createKey(format);
keys.add(TestUtil.hexEncode(key.getQ().toByteArray()));
keys.add(TestUtil.hexEncode(key.getP().toByteArray()));
}
assertThat(keys).hasSize(2 * numTests);
}

/** Tests that a public key is extracted properly from a private key. */
@Test
public void testGetPublicKeyData() throws Exception {
if (TestUtil.isTsan()) {
// This test times out in tsan mode.
return;
}
KeysetHandle privateHandle =
KeysetHandle.generateNew(SignatureKeyTemplates.RSA_SSA_PSS_4096_SHA512_SHA512_64_F4);
KeyData privateKeyData = TestUtil.getKeyset(privateHandle).getKey(0).getKeyData();
PrivateKeyManager<PublicKeySign> privateManager =
new PrivateKeyManagerImpl<>(
new RsaSsaPssSignKeyManager(), new RsaSsaPssVerifyKeyManager(), PublicKeySign.class);
KeyData publicKeyData = privateManager.getPublicKeyData(privateKeyData.getValue());
assertEquals(new RsaSsaPssVerifyKeyManager().getKeyType(), publicKeyData.getTypeUrl());
assertEquals(KeyData.KeyMaterialType.ASYMMETRIC_PUBLIC, publicKeyData.getKeyMaterialType());
RsaSsaPssPrivateKey privateKey = RsaSsaPssPrivateKey.parseFrom(privateKeyData.getValue());
assertArrayEquals(
privateKey.getPublicKey().toByteArray(), publicKeyData.getValue().toByteArray());
KeyManager<PublicKeyVerify> publicManager =
new KeyManagerImpl<>(new RsaSsaPssVerifyKeyManager(), PublicKeyVerify.class);
PublicKeySign signer = privateManager.getPrimitive(privateKeyData.getValue());
PublicKeyVerify verifier = publicManager.getPrimitive(publicKeyData.getValue());
byte[] message = Random.randBytes(20);
try {
verifier.verify(signer.sign(message), message);
} catch (GeneralSecurityException e) {
fail("Should not fail: " + e);
}
public void getPublicKey_correctValues() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA512, 64, 4096, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
assertThat(manager.getPublicKey(key)).isEqualTo(key.getPublicKey());
}

@Test
public void createPrimitive() throws Exception {
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA512, 64, 4096, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
PublicKeySign signer = manager.getPrimitive(key, PublicKeySign.class);

KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("RSA");
BigInteger modulus = new BigInteger(1, key.getPublicKey().getN().toByteArray());
BigInteger exponent = new BigInteger(1, key.getPublicKey().getE().toByteArray());
RSAPublicKey publicKey =
(RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(modulus, exponent));
RsaSsaPssParams params = key.getPublicKey().getParams();
PublicKeyVerify verifier = new RsaSsaPssVerifyJce(
publicKey,
SigUtil.toHashType(params.getSigHash()),
SigUtil.toHashType(params.getMgf1Hash()),
params.getSaltLength());

byte[] message = Random.randBytes(135);
verifier.verify(signer.sign(message), message);
}
}
Loading

0 comments on commit 455c59d

Please sign in to comment.