blob: 657ab2725d5c623e8bd6a8caebc4bb3f0906c93f [file] [log] [blame]
// Copyright 2018 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.signature;
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.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.TestUtil;
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.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for RsaSsaPssSignKeyManager. */
@RunWith(JUnit4.class)
public class RsaSsaPssSignKeyManagerTest {
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);
}
@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));
}
@Test
public void createKey_smallKey() throws Exception {
if (TestUtil.isTsan()) {
// factory.createKey is too slow in Tsan.
return;
}
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA256, HashType.SHA256, 32, 3072, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
checkConsistency(key, format);
checkKey(key);
}
@Test
public void createKey_largeKey() throws Exception {
if (TestUtil.isTsan()) {
// factory.createKey is too slow in Tsan.
return;
}
RsaSsaPssKeyFormat format =
createKeyFormat(HashType.SHA512, HashType.SHA512, 64, 4096, RSAKeyGenParameterSpec.F4);
RsaSsaPssPrivateKey key = factory.createKey(format);
checkConsistency(key, format);
checkKey(key);
}
@Test
public void createKey_alwaysNewElement() throws Exception {
if (TestUtil.isTsan()) {
// factory.createKey is too slow in Tsan.
return;
}
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 = 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);
}
@Test
public void getPublicKey_correctValues() throws Exception {
if (TestUtil.isTsan()) {
// factory.createKey is too slow in Tsan.
return;
}
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 {
if (TestUtil.isTsan()) {
// factory.createKey is too slow in Tsan.
return;
}
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);
}
}