Skip to content

Commit

Permalink
Merge pull request #80 from corretto/develop
Browse files Browse the repository at this point in the history
Merge develop into master to release 1.3.0
  • Loading branch information
SalusaSecondus authored Jan 10, 2020
2 parents 74697b2 + b8b3072 commit e612563
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 85 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 1.3.0

### Improvements
* Now supports ECDSA signatures in IEEE P1363 Format. (Also known as "raw" or "plain".) [PR #75](https://github.com/corretto/amazon-corretto-crypto-provider/pull/75)
* Now allows cloning of `Mac` objects. [PR #78](https://github.com/corretto/amazon-corretto-crypto-provider/pull/78)

### Maintenance
* You can disable parallel execution of tests by setting the `ACCP_TEST_PARALLEL` environment variable to `false`

## 1.2.0

### Improvements
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ add_custom_command(
OUTPUT ${ACCP_JAR_SOURCE}
COMMAND ${Java_JAR_EXECUTABLE} cf ${ACCP_JAR_SOURCE} -C ${CMAKE_CURRENT_SOURCE_DIR}/src .
COMMAND ${Java_JAR_EXECUTABLE} uf ${ACCP_JAR_SOURCE} -C ${CMAKE_CURRENT_SOURCE_DIR} csrc
COMMAND ${Java_JAR_EXECUTABLE} uf ${ACCP_JAR_SOURCE} -C ${CMAKE_CURRENT_SOURCE_DIR}/extra-jar-files test-data
COMMAND ${Java_JAR_EXECUTABLE} uf ${ACCP_JAR_SOURCE} -C ${CMAKE_CURRENT_SOURCE_DIR}/extra-jar-files com/amazon/corretto/crypto/provider/testdata
COMMAND ${Java_JAR_EXECUTABLE} uf ${ACCP_JAR_SOURCE} -C ${CMAKE_CURRENT_BINARY_DIR}/generated-java/ .
DEPENDS ${GENERATED_JAVA_SRC}
)
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,15 @@ Signature algorithms:
* SHA384withDSA
* NONEwithECDSA
* SHA1withECDSA
* SHA1withECDSAinP1363Format
* SHA224withECDSA
* SHA224withECDSAinP1363Format
* SHA256withECDSA
* SHA256withECDSAinP1363Format
* SHA384withECDSA
* SHA384withECDSAinP1363Format
* SHA512withECDSA
* SHA512withECDSAinP1363Format

KeyPairGenerator algorithms:
* EC
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = 'software.amazon.cryptools'
version = '1.2.0'
version = '1.3.0'

configurations {
jacocoAgent
Expand All @@ -24,7 +24,7 @@ dependencies {
jacocoAgent group: 'org.jacoco', name: 'org.jacoco.agent', version: '0.8.3', classifier: 'runtime'

testDep 'junit:junit:4.12'
testDep 'org.bouncycastle:bcprov-jdk15on:1.61'
testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.61'
testDep 'org.bouncycastle:bcpkix-jdk15on:1.61'
testDep 'commons-codec:commons-codec:1.12'
testDep 'org.hamcrest:hamcrest:2.1'
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ private void addSignatures() {
for (final String base : bases) {
for (final String hash : hashes) {
final String algorithm = format("%swith%s", hash, base);
addService("Signature", algorithm, String.format("EvpSignature$%s", algorithm));
final String className = format("EvpSignature$%s", algorithm);
addService("Signature", algorithm, className);
if (base.equals("ECDSA")) {
addService("Signature", algorithm + EvpSignatureBase.P1363_FORMAT_SUFFIX, className);
}
}
}

Expand All @@ -121,6 +125,7 @@ private ACCPService addService(final String type, final String algorithm, final

private class ACCPService extends Service {
private final MethodHandle ctor;
private final MethodHandle algorithmSetter;

// @GuardedBy("this") // Restore once replacement for JSR-305 available
private boolean failMessagePrinted = false;
Expand Down Expand Up @@ -148,9 +153,22 @@ public ACCPService(
.bindTo(AmazonCorrettoCryptoProvider.this);
}
ctor = tmpCtor;

MethodHandle tmpAlgSetter = null;
final MethodType setterSignature = MethodType.methodType(void.class, String.class);
try {
tmpAlgSetter = LOOKUP.findVirtual(klass, "setAlgorithmName", setterSignature);
} catch (final NoSuchMethodException ex) {
if (type.equals("Signature")) {
throw ex;
}
// Just ignore this
}
algorithmSetter = tmpAlgSetter;
} catch (Exception e) {
throw new RuntimeException(e);
}

}

@Override public Object newInstance(final Object constructorParameter) throws NoSuchAlgorithmException {
Expand All @@ -165,7 +183,11 @@ public ACCPService(
}

try {
return (Object) ctor.invokeExact();
Object result = (Object) ctor.invokeExact();
if (algorithmSetter != null) {
algorithmSetter.invoke(result, getAlgorithm());
}
return result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
Expand Down
25 changes: 19 additions & 6 deletions src/com/amazon/corretto/crypto/provider/EvpSignature.java
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ protected synchronized void engineReset() {
protected synchronized byte[] engineSign() throws SignatureException {
ensureInitialized(true);
try {
return signingBuffer.doFinal();
return maybeConvertSignatureToReturn(signingBuffer.doFinal());
} finally {
engineReset();
}
Expand Down Expand Up @@ -521,18 +521,31 @@ protected synchronized boolean engineVerify(final byte[] sigBytes) throws Signat
}

@Override
protected synchronized boolean engineVerify(final byte[] sigBytes, final int off, final int len)
protected synchronized boolean engineVerify(byte[] sigBytes, int off, int len)
throws SignatureException {
ensureInitialized(false);
byte[] tempSig = maybeConvertSignatureToVerify(sigBytes, off, len);
final byte[] finalSigBytes;
final int finalOff;
final int finalLen;
if (tempSig != null) {
finalSigBytes = tempSig;
finalOff = 0;
finalLen = finalSigBytes.length;
} else {
finalSigBytes = sigBytes;
finalOff = off;
finalLen = len;
}
try {
return verifyingBuffer
.withDoFinal((ignored) -> {
final boolean result;
if (keyUsageCount_ > KEY_REUSE_THRESHOLD) {
result = ctx_.use(c -> verifyFinish(c, sigBytes, off, len, true));
result = ctx_.use(c -> verifyFinish(c, finalSigBytes, finalOff, finalLen, true));
} else {
try {
result = verifyFinish(ctx_.take(), sigBytes, off, len, false);
result = verifyFinish(ctx_.take(), finalSigBytes, finalOff, finalLen, false);
} finally {
ctx_ = null;
}
Expand All @@ -545,12 +558,12 @@ protected synchronized boolean engineVerify(final byte[] sigBytes, final int off
if (ctx_ != null) {
result = ctx_.use(c -> verify(keyDer_, new long[]{c}, keyType_.nativeValue,
digestName_, paddingType_, null, 0,
src, offset, length, sigBytes, off, len));
src, offset, length, finalSigBytes, finalOff, finalLen));
} else {
final long[] handle = keyUsageCount_ > KEY_REUSE_THRESHOLD ? new long[1] : null;
result = verify(keyDer_, handle, keyType_.nativeValue,
digestName_, paddingType_, null, 0,
src, offset, length, sigBytes, off, len);
src, offset, length, finalSigBytes, finalOff, finalLen);
if (handle != null) {
ctx_ = new EvpContext(handle[0]);
}
Expand Down
147 changes: 147 additions & 0 deletions src/com/amazon/corretto/crypto/provider/EvpSignatureBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.amazon.corretto.crypto.provider;

import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
Expand All @@ -12,12 +13,17 @@
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.interfaces.ECKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

abstract class EvpSignatureBase extends SignatureSpi {
// Package visible so main Provider can use it
static final String P1363_FORMAT_SUFFIX = "inP1363Format";
protected static final int RSA_PKCS1_PADDING = 1;
protected final EvpKeyType keyType_;
protected final int paddingType_;
Expand All @@ -26,6 +32,7 @@ abstract class EvpSignatureBase extends SignatureSpi {
protected boolean signMode;
protected int keyUsageCount_ = 0;
protected EvpContext ctx_ = null;
protected String algorithmName_ = null;

EvpSignatureBase(
final EvpKeyType keyType,
Expand All @@ -37,6 +44,11 @@ abstract class EvpSignatureBase extends SignatureSpi {

protected abstract void engineReset();

// Called reflectively upon creation
void setAlgorithmName(String algorithmName) {
this.algorithmName_ = algorithmName;
}

/**
* Destroys the native context.
*
Expand Down Expand Up @@ -154,4 +166,139 @@ protected EvpContext(final long ptr) {
}
}

/**
* Converts and returns the modified signature to verify <em>only if necessary</em> and returns {@code null} otherwise.
* If {@code null} is returned then the passed in parameters should be used for later verification.
* Otherwise, the entire returned array should be used.
* This method has a somewhat odd API since we want to avoid unnecessary array copies/allocations and it is an
* internal API anyway.
*
* @return the converted signature or {@code null} if no conversion is necessary
* @throws SignatureException if the signature is badly malformed
*/
protected byte[] maybeConvertSignatureToVerify(byte[] signature, int offset, int length) throws SignatureException {
if (algorithmName_ != null && algorithmName_.endsWith(P1363_FORMAT_SUFFIX)) {
final ECKey ecKey = (ECKey) key_;
final int numLen = (ecKey.getParams().getOrder().bitLength() + 7) / 8;
return ieeeP1363toAsn1(signature, offset, length, numLen);
} else {
return null;
}
}

/**
* Determines if we need to convert the signature <em>we generated</em> and performs said conversion.
* <em>This methods may throw {@link AssertionError} on invalid input so should only be given trusted inputs.</em>.
*/
protected byte[] maybeConvertSignatureToReturn(byte[] signature) throws SignatureException {
if (algorithmName_ != null && algorithmName_.endsWith(P1363_FORMAT_SUFFIX)) {
final ECKey ecKey = (ECKey) key_;
final int numLen = (ecKey.getParams().getOrder().bitLength() + 7) / 8;
return asn1ToiIeeeP1363(signature, numLen);
} else {
return signature;
}
}

/**
* This is a trivial conversion from two equal-length concatenated integers to an ASN.1 sequence.
*
* Since the resulting structure is so simple, we do not need a full ASN.1 engine and can cover all cases by hand.
*/
protected static byte[] ieeeP1363toAsn1(byte[] signature, final int offset, final int length, int numLen) throws SignatureException {
if (2 * numLen != length) {
throw new SignatureException();
}

// This is the easiest way to trim unneeded zero-bytes
final byte[] r = (new BigInteger(1, Arrays.copyOfRange(signature, offset, offset + numLen))).toByteArray();
final byte[] s = (new BigInteger(1, Arrays.copyOfRange(signature, offset + numLen, offset + 2 * numLen))).toByteArray();

if (r.length > 127 || s.length > 127) {
throw new SignatureException("R or S value is too large");
}

// Encode the total sequence length. This might be one or two bytes
final int seqLength = r.length + s.length + 4;
final byte[] encodedSeqLength;
if (seqLength <= 127) {
encodedSeqLength = new byte[]{ (byte) (seqLength & 0xFF) };
} else if (seqLength <= 256) {
encodedSeqLength = new byte[]{ (byte) 0x81, (byte) (seqLength & 0xFF)};
} else {
throw new SignatureException("R or S value is too large");
}

final byte[] result = new byte[1 + encodedSeqLength.length + seqLength];
int position = 0;
result[position++] = 0x30; // SEQUENCE
System.arraycopy(encodedSeqLength, 0, result, position, encodedSeqLength.length);
position += encodedSeqLength.length;
result[position++] = 0x02; // INTEGER
result[position++] = (byte) (r.length & 0xFF); // Length of R
System.arraycopy(r, 0, result, position, r.length);
position += r.length;
result[position++] = 0x02; // INTEGER
result[position++] = (byte) (s.length & 0xFF); // Length of S
System.arraycopy(s, 0, result, position, s.length);
position += s.length;
if (position != result.length) {
throw new AssertionError("Final position of " + position + " does not match expected value of " + result.length);
}

return result;
}

/** Note: This should only be used on trusted inputs **/
protected static byte[] asn1ToiIeeeP1363(byte[] signature, int numLen) throws SignatureException {
// Check the ASN.1 for correctness and extract offsets
int position = 0;
if (signature[position++] != 0x30) {
throw new AssertionError();
}

// Length may be one or two bytes
int seqLen = Byte.toUnsignedInt(signature[position++]);
if (seqLen == 0x81) {
// Two byte length with second byte being the length
seqLen = Byte.toUnsignedInt(signature[position++]);
} else if (seqLen > 127) {
// Unhandled long, reserved, or indefinite length
throw new AssertionError();
}
if (seqLen != signature.length - position) {
throw new AssertionError();
}

final int rOffset = position;
if (signature[rOffset] != 0x02) {
throw new AssertionError();
}
int rLen = Byte.toUnsignedInt(signature[rOffset + 1]);
int rStart = rOffset + 2;

final int sOffset = rStart + rLen;
if (signature[sOffset] != 0x02) {
throw new AssertionError(Base64.getEncoder().encodeToString(signature) + " : " +

String.format("%x, %x, %x", signature[sOffset - 1], signature[sOffset], signature[sOffset + 1]));
}
int sLen = Byte.toUnsignedInt(signature[sOffset + 1]);
int sStart = sOffset + 2;

// Remove leading zero bytes
if (signature[rStart] == 0) {
rStart++;
rLen--;
}
if (signature[sStart] == 0) {
sStart++;
sLen--;
}

byte[] result = new byte[numLen * 2];
System.arraycopy(signature, rStart, result, numLen - rLen, rLen);
System.arraycopy(signature, sStart, result, numLen + numLen - sLen, sLen);
return result;
}
}
2 changes: 1 addition & 1 deletion src/com/amazon/corretto/crypto/provider/InputBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ public void update(final byte val) {
//@ pure
@Override
protected Object clone() throws CloneNotSupportedException {
if (!stateCloner.isPresent()) {
if (state != null && !stateCloner.isPresent()) {
throw new CloneNotSupportedException("No stateCloner configured");
}
@SuppressWarnings("unchecked")
Expand Down
Loading

0 comments on commit e612563

Please sign in to comment.