Skip to content

Commit

Permalink
Add support for ECDSA's IEEE_P1363 signature format.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 205657904
GitOrigin-RevId: 7523f0021d17b79ab9eb4f365dd17ac65bbb4711
  • Loading branch information
cryptosubtlety authored and chuckx committed Jul 27, 2018
1 parent 949b4f2 commit a9870f7
Show file tree
Hide file tree
Showing 17 changed files with 216 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig;
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.subtle.EcdsaVerifyJce;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
Expand Down Expand Up @@ -440,7 +441,10 @@ private static void verify(
for (SenderVerifyingKeysProvider verifyingKeysProvider : senderVerifyingKeysProviders) {
for (ECPublicKey publicKey : verifyingKeysProvider.get(protocolVersion)) {
EcdsaVerifyJce verifier =
new EcdsaVerifyJce(publicKey, PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO);
new EcdsaVerifyJce(
publicKey,
PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO,
EcdsaEncoding.DER);
for (byte[] signature : signatures) {
try {
verifier.verify(signature, signedBytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig;
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.subtle.EcdsaSignJce;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.interfaces.ECPrivateKey;
Expand Down Expand Up @@ -86,7 +87,8 @@ public final class PaymentMethodTokenSender {
builder.senderIntermediateSigningKey != null
? builder.senderIntermediateSigningKey
: builder.senderSigningKey,
PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO);
PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO,
EcdsaEncoding.DER);
this.senderId = builder.senderId;
if (protocolVersionConfig.isEncryptionRequired) {
this.hybridEncrypter =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenConstants.ProtocolVersionConfig;
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.subtle.EcdsaSignJce;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import java.security.GeneralSecurityException;
import java.security.interfaces.ECPrivateKey;
import java.util.ArrayList;
Expand Down Expand Up @@ -67,7 +68,9 @@ private SenderIntermediateCertFactory(
for (ECPrivateKey senderSigningKey : senderSigningKeys) {
this.signers.add(
new EcdsaSignJce(
senderSigningKey, PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO));
senderSigningKey,
PaymentMethodTokenConstants.ECDSA_SHA256_SIGNING_ALGO,
EcdsaEncoding.DER));
}
this.intermediateSigningKey = intermediateSigningKey;
this.expiration = expiration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.subtle.EcdsaVerifyJce;
import com.google.crypto.tink.subtle.EllipticCurves;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import com.google.crypto.tink.util.KeysDownloader;
import java.io.IOException;
import java.net.URI;
Expand Down Expand Up @@ -151,7 +152,8 @@ private void verify(final byte[] tbs, long keyId, final byte[] signature)
if (publicKeys.containsKey(keyId)) {
foundKeyId = true;
ECPublicKey publicKey = publicKeys.get(keyId);
EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, "SHA256WithECDSA");
EcdsaVerifyJce verifier =
new EcdsaVerifyJce(publicKey, "SHA256WithECDSA", EcdsaEncoding.DER);
verifier.verify(signature, tbs);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.crypto.tink.subtle.Base64;
import com.google.crypto.tink.subtle.EcdsaSignJce;
import com.google.crypto.tink.subtle.EllipticCurves;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import com.google.crypto.tink.util.KeysDownloader;
import java.net.URI;
import java.nio.charset.Charset;
Expand Down Expand Up @@ -75,7 +76,9 @@ public class RewardedAdsVerifierTest {
private static String signUrl(String rewardUrl, String privateKey, long keyId) throws Exception {
EcdsaSignJce signer =
new EcdsaSignJce(
EllipticCurves.getEcPrivateKey(Base64.decode(privateKey)), "SHA256WithECDSA");
EllipticCurves.getEcPrivateKey(Base64.decode(privateKey)),
"SHA256WithECDSA",
EcdsaEncoding.DER);
String queryString = new URI(rewardUrl).getQuery();
return buildUrl(rewardUrl, signer.sign(queryString.getBytes(UTF_8)), keyId);
}
Expand Down Expand Up @@ -126,9 +129,9 @@ public void testShouldVerifyWithEncodedUrl() throws Exception {
String decodedQueryString = "foo=hello world&[email protected]";
EcdsaSignJce signer =
new EcdsaSignJce(
EllipticCurves.getEcPrivateKey(
Base64.decode(GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64)),
"SHA256WithECDSA");
EllipticCurves.getEcPrivateKey(Base64.decode(GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64)),
"SHA256WithECDSA",
EcdsaEncoding.DER);
byte[] signature = signer.sign(decodedQueryString.getBytes(UTF_8));
String signedUrl = buildUrl(rewardUrl, signature, KEY_ID);
verifier.verify(signedUrl);
Expand Down Expand Up @@ -214,7 +217,8 @@ public void testShouldFailIfSignatureWasChanged() throws Exception {
EcdsaSignJce signer =
new EcdsaSignJce(
EllipticCurves.getEcPrivateKey(Base64.decode(GOOGLE_SIGNING_PRIVATE_KEY_PKCS8_BASE64)),
"SHA256WithECDSA");
"SHA256WithECDSA",
EcdsaEncoding.DER);
RewardedAdsVerifier verifier =
new RewardedAdsVerifier.Builder()
.setVerifyingPublicKeys(GOOGLE_VERIFYING_PUBLIC_KEYS_JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public PublicKeySign getPrimitive(MessageLite key) throws GeneralSecurityExcepti
SigUtil.toCurveType(keyProto.getPublicKey().getParams().getCurve()),
keyProto.getKeyValue().toByteArray());
return new EcdsaSignJce(
privateKey, SigUtil.toEcdsaAlgo(keyProto.getPublicKey().getParams().getHashType()));
privateKey,
SigUtil.toEcdsaAlgo(keyProto.getPublicKey().getParams().getHashType()),
SigUtil.toEcdsaEncoding(keyProto.getPublicKey().getParams().getEncoding()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ public PublicKeyVerify getPrimitive(MessageLite key) throws GeneralSecurityExcep
SigUtil.toCurveType(keyProto.getParams().getCurve()),
keyProto.getX().toByteArray(),
keyProto.getY().toByteArray());
return new EcdsaVerifyJce(publicKey, SigUtil.toEcdsaAlgo(keyProto.getParams().getHashType()));
return new EcdsaVerifyJce(
publicKey,
SigUtil.toEcdsaAlgo(keyProto.getParams().getHashType()),
SigUtil.toEcdsaEncoding(keyProto.getParams().getEncoding()));
}

/**
Expand Down
20 changes: 18 additions & 2 deletions java/src/main/java/com/google/crypto/tink/signature/SigUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public static void validateEcdsaParams(EcdsaParams params) throws GeneralSecurit
HashType hash = params.getHashType();
EllipticCurveType curve = params.getCurve();
switch (encoding) {
case DER:
case DER: // fall through
case IEEE_P1363:
break;
// TODO(b/74249423): support other signature encodings.
default:
throw new GeneralSecurityException("unsupported signature encoding");
}
Expand Down Expand Up @@ -95,4 +95,20 @@ public static EllipticCurves.CurveType toCurveType(EllipticCurveType type)
throw new GeneralSecurityException("unknown curve type: " + type);
}
}

/**
* Converts protobuf enum {@code EcdsaSignatureEncoding} to raw Java enum {code
* EllipticCurves.EcdsaEncoding}.
*/
public static EllipticCurves.EcdsaEncoding toEcdsaEncoding(EcdsaSignatureEncoding encoding)
throws GeneralSecurityException {
switch (encoding) {
case IEEE_P1363:
return EllipticCurves.EcdsaEncoding.IEEE_P1363;
case DER:
return EllipticCurves.EcdsaEncoding.DER;
default:
throw new GeneralSecurityException("unknown ECDSA encoding: " + encoding);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,56 @@ public final class SignatureKeyTemplates {
*
* <ul>
* <li>Hash function: SHA512
* <li>Curve: NIST P-384
* <li>Curve: NIST P-521
* <li>Signature encoding: DER (this is the encoding that Java uses).
* </ul>
*/
public static final KeyTemplate ECDSA_P521 =
createEcdsaKeyTemplate(
HashType.SHA512, EllipticCurveType.NIST_P521, EcdsaSignatureEncoding.DER);

/**
* A {@link KeyTemplate} that generates new instances of {@link
* com.google.crypto.tink.proto.EcdsaPrivateKey} with the following parameters:
*
* <ul>
* <li>Hash function: SHA256
* <li>Curve: NIST P-256
* <li>Signature encoding: IEEE_P1363 (this is the encoding that JWS and WebCrypto use).
* </ul>
*/
public static final KeyTemplate ECDSA_P256_IEEE_P1363 =
createEcdsaKeyTemplate(
HashType.SHA256, EllipticCurveType.NIST_P256, EcdsaSignatureEncoding.IEEE_P1363);

/**
* A {@link KeyTemplate} that generates new instances of {@link
* com.google.crypto.tink.proto.EcdsaPrivateKey} with the following parameters:
*
* <ul>
* <li>Hash function: SHA512
* <li>Curve: NIST P-384
* <li>Signature encoding: IEEE_P1363 (this is the encoding that JWS and WebCrypto use).
* </ul>
*/
public static final KeyTemplate ECDSA_P384_IEEE_P1363 =
createEcdsaKeyTemplate(
HashType.SHA512, EllipticCurveType.NIST_P384, EcdsaSignatureEncoding.IEEE_P1363);

/**
* A {@link KeyTemplate} that generates new instances of {@link
* com.google.crypto.tink.proto.EcdsaPrivateKey} with the following parameters:
*
* <ul>
* <li>Hash function: SHA512
* <li>Curve: NIST P-521
* <li>Signature encoding: IEEE_P1363 (this is the encoding that JWS and WebCrypto use).
* </ul>
*/
public static final KeyTemplate ECDSA_P521_IEEE_P1363 =
createEcdsaKeyTemplate(
HashType.SHA512, EllipticCurveType.NIST_P521, EcdsaSignatureEncoding.IEEE_P1363);

/**
* A {@link KeyTemplate} that generates new instances of {@link
* com.google.crypto.tink.proto.Ed25519PrivateKey}.
Expand Down
14 changes: 12 additions & 2 deletions java/src/main/java/com/google/crypto/tink/subtle/EcdsaSignJce.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package com.google.crypto.tink.subtle;

import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.EllipticCurve;

/**
* ECDSA signing with JCE.
Expand All @@ -29,17 +31,25 @@
public final class EcdsaSignJce implements PublicKeySign {
private final ECPrivateKey privateKey;
private final String signatureAlgorithm;
private final EcdsaEncoding encoding;

public EcdsaSignJce(final ECPrivateKey priv, String signatureAlgorithm) {
public EcdsaSignJce(final ECPrivateKey priv, String signatureAlgorithm, EcdsaEncoding encoding) {
this.privateKey = priv;
this.signatureAlgorithm = signatureAlgorithm;
this.encoding = encoding;
}

@Override
public byte[] sign(final byte[] data) throws GeneralSecurityException {
Signature signer = EngineFactory.SIGNATURE.getInstance(signatureAlgorithm);
signer.initSign(privateKey);
signer.update(data);
return signer.sign();
byte[] signature = signer.sign();
if (encoding == EcdsaEncoding.IEEE_P1363) {
EllipticCurve curve = privateKey.getParams().getCurve();
signature =
EllipticCurves.ecdsaDer2Ieee(signature, 2 * EllipticCurves.fieldSizeInBytes(curve));
}
return signature;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package com.google.crypto.tink.subtle;

import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding;
import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.interfaces.ECPublicKey;
import java.security.spec.EllipticCurve;

/**
* ECDSA verifying with JCE.
Expand All @@ -29,25 +31,35 @@
public final class EcdsaVerifyJce implements PublicKeyVerify {
private final ECPublicKey publicKey;
private final String signatureAlgorithm;
private final EcdsaEncoding encoding;

public EcdsaVerifyJce(final ECPublicKey pubKey, String signatureAlgorithm)
public EcdsaVerifyJce(final ECPublicKey pubKey, String signatureAlgorithm, EcdsaEncoding encoding)
throws GeneralSecurityException {
EllipticCurves.checkPublicKey(pubKey);
this.publicKey = pubKey;
this.signatureAlgorithm = signatureAlgorithm;
this.encoding = encoding;
}

@Override
public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException {
if (!EllipticCurves.isValidDerEncoding(signature)) {
byte[] derSignature = signature;
if (encoding == EcdsaEncoding.IEEE_P1363) {
EllipticCurve curve = publicKey.getParams().getCurve();
if (signature.length != 2 * EllipticCurves.fieldSizeInBytes(curve)) {
throw new GeneralSecurityException("Invalid signature");
}
derSignature = EllipticCurves.ecdsaIeee2Der(signature);
}
if (!EllipticCurves.isValidDerEncoding(derSignature)) {
throw new GeneralSecurityException("Invalid signature");
}
Signature verifier = EngineFactory.SIGNATURE.getInstance(signatureAlgorithm);
verifier.initVerify(publicKey);
verifier.update(data);
boolean verified = false;
try {
verified = verifier.verify(signature);
verified = verifier.verify(derSignature);
} catch (java.lang.RuntimeException ex) {
verified = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ public enum CurveType {
NIST_P521,
}

/** Ecdsa signature encoding. */
public enum EcdsaEncoding {
IEEE_P1363,
DER,
}

public static ECParameterSpec getNistP256Params() {
return getNistCurveSpec(
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ public void testNewKeyWithVerifier() throws Exception {
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P256);
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P384);
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P521);
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P256_IEEE_P1363);
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P384_IEEE_P1363);
testNewKeyWithVerifier(SignatureKeyTemplates.ECDSA_P521_IEEE_P1363);
}

@Test
Expand All @@ -176,24 +179,6 @@ public void testNewKeyWithCorruptedFormat() {
}
}

@Test
public void testNewKeyUnsupportedEncoding() throws Exception {
EcdsaSignKeyManager signManager = new EcdsaSignKeyManager();
EcdsaParams ecdsaParams =
EcdsaParams.newBuilder()
.setHashType(HashType.SHA256)
.setCurve(EllipticCurveType.NIST_P256)
.setEncoding(EcdsaSignatureEncoding.IEEE_P1363)
.build();
EcdsaKeyFormat ecdsaFormat = EcdsaKeyFormat.newBuilder().setParams(ecdsaParams).build();
try {
signManager.newKey(ecdsaFormat);
fail("Unsupported encoding, should have thrown exception");
} catch (GeneralSecurityException expecpted) {
// Raw encoding is not supported yet.
}
}

private void testNewKeyUnsupportedKeyFormat(HashAndCurveType hashAndCurve) throws Exception {
HashType hashType = hashAndCurve.hashType;
EllipticCurveType curveType = hashAndCurve.curveType;
Expand Down
Loading

0 comments on commit a9870f7

Please sign in to comment.