Skip to content

Commit

Permalink
Merge pull request #149 from corretto/develop
Browse files Browse the repository at this point in the history
Merge v1.6.0 to master in preparation for release
  • Loading branch information
alex-chew authored Mar 15, 2021
2 parents 130ceaa + ff85424 commit 59b129a
Show file tree
Hide file tree
Showing 20 changed files with 717 additions and 71 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
*.ipr
*.iws
*.iml
*.orig
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 1.6.0
### Breaking Change
In accordance with our [versioning policy](https://github.com/corretto/amazon-corretto-crypto-provider/blob/master/VERSIONING.rst),
this release contains a low-risk breaking change. For details please see the [1.5.0](#150) section of this document.
This change only impacts libraries that generate EC keys using the
[KeyPairGenerator.initialize(int keysize)](https://docs.oracle.com/javase/8/docs/api/java/security/KeyPairGenerator.html#initialize-int-)
method.

### Improvements
* Stricter guarantees about which curves are used for EC key generation. [PR #127](https://github.com/corretto/amazon-corretto-crypto-provider/pull/127)
* Reduce timing signal from trimming zeros of TLSPremasterSecrets from DH KeyAgreement. [PR #129](https://github.com/corretto/amazon-corretto-crypto-provider/pull/129)
* Reuse state in `MessageDigest` to decrease object allocation rate. [PR #131](https://github.com/corretto/amazon-corretto-crypto-provider/pull/131)
* Now uses [OpenSSL 1.1.1j](https://www.openssl.org/source/openssl-1.1.1j.tar.gz). [PR #145](https://github.com/corretto/amazon-corretto-crypto-provider/pull/145)
(ACCP is not impacted by [CVE-2020-1971](https://www.openssl.org/news/secadv/20201208.txt), [CVE-2021-23841](https://www.openssl.org/news/secadv/20210216.txt), or [CVE-2021-23839](https://www.openssl.org/news/secadv/20210216.txt) as ACCP does not use or expose any of the relevant functionality.
ACCP is not impacted by [CVE-2021-23840](https://www.openssl.org/news/secadv/20210216.txt) as ACCP does not use the relevant functionality under the affected conditions.)

### Patches
* Add version gating to some tests introduced in 1.5.0 [PR #128](https://github.com/corretto/amazon-corretto-crypto-provider/pull/128)
* More accurate output size estimates from `Cipher.getOutputSize()` [PR #138](https://github.com/corretto/amazon-corretto-crypto-provider/pull/138)
* Validate that `AesGcmSpi` receives a non-null key on init to prevent unnecessarily late NPE [PR #146](https://github.com/corretto/amazon-corretto-crypto-provider/pull/146)
* Gracefully handle calling `Cipher.doFinal()` without any input bytes in `RsaCipher` [PR #147](https://github.com/corretto/amazon-corretto-crypto-provider/pull/147)

## 1.5.0
### Breaking Change Warning
In accordance with our [versioning policy](https://github.com/corretto/amazon-corretto-crypto-provider/blob/master/VERSIONING.rst),
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ set(ACCP_SRC
src/com/amazon/corretto/crypto/provider/AccessibleByteArrayOutputStream.java
src/com/amazon/corretto/crypto/provider/AesCtrDrbg.java
src/com/amazon/corretto/crypto/provider/AesGcmSpi.java
src/com/amazon/corretto/crypto/provider/ConstantTime.java
src/com/amazon/corretto/crypto/provider/EcGen.java
src/com/amazon/corretto/crypto/provider/EvpKeyAgreement.java
src/com/amazon/corretto/crypto/provider/EvpKeyType.java
Expand Down Expand Up @@ -528,6 +529,7 @@ add_jar(
tst/com/amazon/corretto/crypto/provider/test/AESGenerativeTest.java
tst/com/amazon/corretto/crypto/provider/test/AesCtrDrbgTest.java
tst/com/amazon/corretto/crypto/provider/test/AesTest.java
tst/com/amazon/corretto/crypto/provider/test/ConstantTimeTests.java
tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java
tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementTest.java
tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementSpecificTest.java
Expand Down
169 changes: 169 additions & 0 deletions DEVELOPMENT-GUIDE.md

Large diffs are not rendered by default.

81 changes: 81 additions & 0 deletions DIFFERENCES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Important Differences
The [JCA/JCE](https://docs.oracle.com/en/java/javase/11/security/java-cryptography-architecture-jca-reference-guide.html) specification does not completely define all behaviors of a given provider.
Although the Amazon Corretto Crypto Provider (ACCP) is fully compliant with the JCE, its behavior differs from the behavior of the default Java providers in several ways.
The following list of behavioral differences is not exhaustive, but is intended to capture the most important differences.

Despite these differences, ACCP remains a drop-in replacement for the vast majority of Java applications and doesn't change the application behavior, other than improving its performance.

# Behavior Changes
These differences are those most likely to be noticed by a consuming application. You might need to make minor changes to accommodate them.

## SignatureException
The official documentation does not fully specify when the [Signature](https://docs.oracle.com/javase/8/docs/api/java/security/Signature.html) object is expected to throw a [SignatureException](https://docs.oracle.com/javase/8/docs/api/java/security/SignatureException.html).
Having multiple different ways to reject a signature (such as `signature.verify() == false` and throwing a `SignatureException`) is an anti-pattern and we should try to avoid it.
ACCP throws a `SignatureException` from `Signature.verify()` only when not throwing would introduce compatibility issues (such as with the [JCK](https://en.wikipedia.org/wiki/Technology_Compatibility_Kit#TCK_for_the_Java_platform).)
Currently, ACCP will throw a `SignatureException` only when verifying an EDSA or DSA signature that is not properly encoded.
In all other cases, ACCP will return `false` from `Signature.verify()` when given an invalid signature.
This is different from the default OpenJDK implementation, which also inspects the inner structure of RSA signatures and rejects them with a `SignatureException` if they are improperly encoded.
ACCP follows the guidance provided in [PKCS #1 section 8.2.2](https://tools.ietf.org/html/rfc8017#section-8.2.2) in that it does not parse the inner structure but, instead, does a binary comparison against the expected value.

For this reason, regardless of whether you use ACCP or not, we recommend the following structure for signature verification:
```java
Signature signatureObject = Signature.getInstance(SIGNATURE_ALGORITHM);
signatureObject.initVerify(publicKey);
signatureObject.update(messageToVerify);
boolean signatureValid = false;
try {
signatureValid = signatureObject.verify(signature);
} catch (final SignatureException ex) {
signatureValid = false;
}
```

## Elliptic Curve KeyPairGeneration by curve size
Neither the JCE nor the default OpenJDK provider for Elliptic Curve Cryptography (SunEC) specify the effect of calling `KeyPairGenerator.initialize(int keysize)` with an arbitrary value.
This behavior is fully specified only for values of 192, 224, 256, 384, and 521.
This means that applications cannot depend on receiving a specific curve for any other value. Also, the application might encounter compatibility issues if SunEC ever changes its behavior or if the application changes to a different JCE provider.
In ACCP (after version 1.5.0), the `KeyPairGenerator.initialize(int keysize)` method fails with an `InvalidParameterException` for `keysize` values other than 192, 224, 256, 384, and 521.

For this reason, even if you don't use ACCP, we recommend that you use only the [KeyPairGenerator.initialize(AlgorithmParameterSpec params)](https://docs.oracle.com/javase/8/docs/api/java/security/KeyPairGenerator.html#initialize-java.security.spec.AlgorithmParameterSpec-) method with an [ECGenParameterSpec](https://docs.oracle.com/javase/8/docs/api/java/security/spec/ECGenParameterSpec.html) to generate EC keys.
This construction is safe for all known JCE providers and is expected to remain safe even if the behavior of a provider changes.

For more information, see the [changelog](./CHANGELOG.md) notes for version 1.5.0.

## Cipher.getOutputSize() for AES-GCM
ACCP might overestimate the amount of space needed when encrypted with `AES/GCM/NoPadding`.
While this is compliant with the JCE (which [permits overestimation](https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getOutputSize-int-)) it has caused confusion for some developers.
We are tracking this as [issue #135](https://github.com/corretto/amazon-corretto-crypto-provider/issues/135) and will improve this behavior.

## SecureRandom is never deterministic
Some implementation of `SecureRandom` (such as `SHA1PRNG`, provided by the default OpenJDK cryptographic providers) can operate deterministically if `SecureRandom.setSeed(byte[])` is called prior to any other methods.
This behavior allows for insecure seeding and might make the application less secure if it requires the `SecureRandom` instance to provide secure entropy (such as for cryptographic use).
The `SecureRandom` implementation provided by ACCP automatically seeds itself upon creation and cannot be used in a deterministic manner.
This change is relevant only to systems that need deterministic behavior based on a seed, such as in some simulations.
Systems that need deterministic behavior should not use an ACCP implementation of `SecureRandom`. They should select an implementation/algorithm that specifically meets their needs.

## SecureRandom uses thread local state internally
To avoid the costs of both RNG initialization and thread contention, ACCP maintains a single internal instance of SecureRandom for each thread.
Any time an instance of `SecureRandom` is used, ACCP routes the requests to the appropriate backing instance for the calling thread.
Because the output of calls to `SecureRandom` is computationally indistinguishable from actual random data, this implementation detail has no impact on callers other than improving performance.

# Extensions
Applications are unlikely to directly encounter any of these changes but may choose to take advantage of them.

## AES-GCM supports IvParameterSpec
ACCP allows use of [IvParameterSpec](https://docs.oracle.com/javase/8/docs/api/javax/crypto/spec/IvParameterSpec.html) when calling [Cipher.init()](https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#init-int-java.security.Key-java.security.spec.AlgorithmParameterSpec-).
This is equivalent to using a [GCMParameterSpec](https://docs.oracle.com/javase/8/docs/api/javax/crypto/spec/GCMParameterSpec.html) with the same IV value and a tag length of 128 bits.
By supporting the same ParameterSpec as other ciphers (such as `AES/CBC/PKCS5Padding`, which should not be used as it is no longer secure), ACCP makes it easier to migrate to the secure choice of `AES/GCM/NoPadding`.
(This behavior is identical to how [BouncyCastle](https://bouncycastle.org/java.html) treats `IvParameterSpec` when used with AES-GCM.)

## KeyAgreement supports reuse without reinitialization
ACCP permits reuse of a [KeyAgreement](https://docs.oracle.com/javase/8/docs/api/javax/crypto/KeyAgreement.html) object without calling `.init()` more than once.
This results in better performance for Static-Ephemeral key agreement protocols.

## AES is supported as a target key type for all KeyAgreement algorithms and supports an explicit size
[KeyAgreement.generateSecret(String)](https://docs.oracle.com/javase/8/docs/api/javax/crypto/KeyAgreement.html#generateSecret-java.lang.String-) can be called with an input of "AES" for all Key Agreement algorithms.
(The default Java implementation does not support "AES" as input with "ECDH" key agreement.)
If "AES" is passed to this method, ACCP returns the largest possible AES key corresponding to the agreed secret.
Alternatively, you can request an AES key of a particular size by appending the size (in bits) surrounded by brackets to this string.
(Ex: "AES[128]" or "AES[256]")
This returns a key of the requested strength or an `InvalidKeyException` if the agreed secret is not long enough for the requested AES key length.
(This method of specifying key size is identical to the way [BouncyCastle](https://bouncycastle.org/java.html) specifies key size for `KeyAgreement.generateSecret(String)`.)
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Amazon Corretto Crypto Provider
The Amazon Corretto Crypto Provider (ACCP) is a collection of high-performance cryptographic implementations exposed via the standard [JCA/JCE](https://docs.oracle.com/en/java/javase/11/security/java-cryptography-architecture-jca-reference-guide.html) interfaces.
This means that it can be used as a drop in replacement for many different Java applications.
Currently algorithms are primarily backed by OpenSSL's implementations (1.1.1g as of ACCP 1.5.0) but this may change in the future.
(Differences from the default OpenJDK implementations are [documented here](./DIFFERENCES.md).)
Currently algorithms are primarily backed by OpenSSL's implementations (1.1.1j as of ACCP 1.6.0) but this may change in the future.

[Security issue notifications](./CONTRIBUTING.md#security-issue-notifications)

Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ plugins {
}

group = 'software.amazon.cryptools'
version = '1.5.0'
version = '1.6.0'

def openssl_version = '1.1.1g'
def openssl_version = '1.1.1j'
def opensslSrcPath = "${buildDir}/openssl/openssl-${openssl_version}"

configurations {
Expand Down Expand Up @@ -453,4 +453,4 @@ task clean(overwrite: true, type: Delete) {

task deep_clean(type: Delete) {
delete buildDir
}
}
2 changes: 1 addition & 1 deletion openssl.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46 openssl-1.1.1g.tar.gz
aaf2fcb575cdf6491b98ab4829abf78a3dec8402b8b81efc8f23c00d443981bf openssl-1.1.1j.tar.gz
13 changes: 9 additions & 4 deletions src/com/amazon/corretto/crypto/provider/AesGcmSpi.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,7 @@ protected int engineGetKeySize(final Key key) throws InvalidKeyException {
protected int engineGetOutputSize(int inputLen) {
switch (opMode) {
case NATIVE_MODE_ENCRYPT:
// Ensure we have room (on doFinal) for either an extra ciphertext block that was buffered in OpenSSL,
// or the final tag (whichever is bigger)
return inputLen + Math.max(tagLength, engineGetBlockSize() - 1);
return getUpdateOutputSize(inputLen) + tagLength;
case NATIVE_MODE_DECRYPT:
return Math.max(0, decryptInputBuf.size() + inputLen - tagLength);
default:
Expand All @@ -253,7 +251,7 @@ protected int engineGetOutputSize(int inputLen) {
private synchronized int getUpdateOutputSize(int inputLen) {
switch (opMode) {
case NATIVE_MODE_ENCRYPT:
return inputLen + engineGetBlockSize() - 1;
return inputLen;
case NATIVE_MODE_DECRYPT:
// We do not return data from engineUpdate when decrypting - all data is returned from engineDoFinal()
return 0;
Expand Down Expand Up @@ -296,6 +294,10 @@ protected synchronized void engineInit(int opMode, Key key, SecureRandom secureR
protected synchronized void engineInit(
int jceOpMode, Key key, AlgorithmParameterSpec algorithmParameterSpec, SecureRandom secureRandom
) throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Key can't be null");
}

final GCMParameterSpec spec;
if (algorithmParameterSpec instanceof GCMParameterSpec) {
spec = (GCMParameterSpec) algorithmParameterSpec;
Expand Down Expand Up @@ -854,6 +856,9 @@ protected int engineDoFinal(ByteBuffer input, ByteBuffer output) throws ShortBuf
}

private void checkOutputBuffer(int inputLength, byte[] output, int outputOffset) throws ShortBufferException {
if (inputLength < 0 || outputOffset < 0 || outputOffset > output.length) {
throw new ArrayIndexOutOfBoundsException();
}
if (output.length - outputOffset < getUpdateOutputSize(inputLength)) {
throw new ShortBufferException("Expected a buffer of at least " + engineGetOutputSize(inputLength) + " bytes; got " + (output.length - outputOffset));
}
Expand Down
69 changes: 69 additions & 0 deletions src/com/amazon/corretto/crypto/provider/ConstantTime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazon.corretto.crypto.provider;

/**
* Contains several constant time utilities
*/
final class ConstantTime {
private ConstantTime() {
// Prevent instantiation
}

/**
* Equivalent to {@code val != 0 ? 1 : 0}
*/
static final int isNonZero(int val) {
return ((val | -val) >>> 31) & 0x01; // Unsigned bitshift
}

/**
* Equivalent to {@code val == 0 ? 1 : 0}
*/
static final int isZero(int val) {
return 1 - isNonZero(val);
}

/**
* Equivalent to {@code val < 0 ? 1 : 0}
*/
static final int isNegative(int val) {
return (val >>> 31) & 0x01;
}

/**
* Equivalent to {@code x == y ? 1 : 0}
*/
static final int equal(int x, int y) {
final int difference = x - y;
// Difference is 0 iff x == y
return isZero(difference);
}

/**
* Equivalent to {@code x > y ? 1 : 0}
*/
static final int gt(int x, int y) {
// Convert to long to avoid underflow
final long xl = x;
final long yl = y;
final long difference = yl - xl;
// If xl > yl, then difference is negative.
// Thus, we can just return the sign-bit
return (int) ((difference >>> 63) & 0x01); // Unsigned bitshift
}

/**
* Equivalent to {@code selector != 0 ? a : b}
*/
static final int select(int selector, int a, int b) {
final int mask = isZero(selector) - 1;
// Mask == -1 (all bits 1) iff selector != 0
// Mask == 0 (all bits 0) iff selector == 0

final int combined = a ^ b;

return b ^ (combined & mask);
}
}
42 changes: 20 additions & 22 deletions src/com/amazon/corretto/crypto/provider/EcGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,28 @@ private static byte[] encodeSpec(final AlgorithmParameterSpec spec) {
public void initialize(final int keysize, final SecureRandom random)
throws InvalidParameterException {
try {
final String curveName = "secp" + keysize + "r1";
// Explicitly list default curves
// Mapping from OpenJDK
// TODO: Uncomment following code at version 1.6.0
// final String curveName;
// switch (keysize) {
// case 192:
// curveName = "secp192r1"; // NIST P-192
// break;
// case 224:
// curveName = "secp224r1"; // NIST P-224
// break;
// case 256:
// curveName = "secp256r1"; // NIST P-256
// break;
// case 384:
// curveName = "secp384r1"; // NIST P-384
// break;
// case 521:
// curveName = "secp521r1"; // NIST P-521
// break;
// default:
// throw new InvalidParameterException("No default NIST prime curve for keysize " + keysize);
// }
final String curveName;
switch (keysize) {
case 192:
curveName = "secp192r1"; // NIST P-192
break;
case 224:
curveName = "secp224r1"; // NIST P-224
break;
case 256:
curveName = "secp256r1"; // NIST P-256
break;
case 384:
curveName = "secp384r1"; // NIST P-384
break;
case 521:
curveName = "secp521r1"; // NIST P-521
break;
default:
throw new InvalidParameterException("No default NIST prime curve for keysize " + keysize);
}
initialize(new ECGenParameterSpec(curveName), random);
} catch (final InvalidAlgorithmParameterException ex) {
throw new InvalidParameterException(ex.getMessage());
Expand Down
Loading

0 comments on commit 59b129a

Please sign in to comment.