Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "pure" ML-DSA #422

Merged
merged 8 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* [PR 397:](https://github.com/corretto/amazon-corretto-crypto-provider/pull/397) Support for Concatenation KDFs
* [PR 399:](https://github.com/corretto/amazon-corretto-crypto-provider/pull/399) Support for Counter KDFs
* [PR 394:](https://github.com/corretto/amazon-corretto-crypto-provider/pull/394) Support for Ed25519 DSA
* Use AWS-LC [v1.42.0](https://github.com/aws/aws-lc/releases/tag/v1.42.0) for ACCP
* Use AWS-LC [AWS-LC-FIPS-3.0.0](https://github.com/aws/aws-lc/releases/tag/AWS-LC-FIPS-3.0.0) for ACCP-FIPS
* [PR 421:](https://github.com/corretto/amazon-corretto-crypto-provider/pull/421) Bump AWS-LC version to 1.42.0 and AWS-LC-FIPS version to 3.0.0
* [PR 422:](https://github.com/corretto/amazon-corretto-crypto-provider/pull/422) Support "pure" ML-DSA, Bump AWS-LC version to 1.43.0

## 2.4.1

Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ else()
set(TEST_FIPS_PROPERTY "-DFIPS=false")
endif()

# The source files under this guard should be removed and added to all builds, including FIPS,
# once the corresponding algorithms are added to a FIPS branch of AWS-LC consumable by ACCP.
if(EXPERIMENTAL_FIPS OR (NOT FIPS))
set(C_SRC ${C_SRC} csrc/mldsa_gen.cpp)
endif()

add_library(amazonCorrettoCryptoProvider SHARED ${C_SRC})

add_custom_command(
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ ACCP did not track a FIPS branch/release version of AWS-LC until ACCP v2.3.0. Be
| 2.3.3 | 1.17.0 | 2.0.2 |
| 2.4.0 | 1.30.1 | 2.0.13 |
| 2.4.1 | 1.30.1 | 2.0.13 |
| 2.5.0 | 1.42.0 | 3.0.0 |
| 2.5.0 | 1.43.0 | 3.0.0 |

Notable differences between ACCP and ACCP-FIPS:
* ACCP uses [the latest release of AWS-LC](https://github.com/aws/aws-lc/releases), whereas, ACCP-FIPS uses [the fips-2022-11-02 branch of AWS-LC](https://github.com/aws/aws-lc/tree/fips-2022-11-02).
Expand Down
2 changes: 1 addition & 1 deletion aws-lc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.corretto.crypto.provider.benchmarks;

import java.security.KeyPair;
import java.security.KeyPairGenerator;

import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
public class KeyGenMlDSA {

@Param({AmazonCorrettoCryptoProvider.PROVIDER_NAME, "BC"})
public String provider;

@Param({"ML-DSA-44", "ML-DSA-65", "ML-DSA-87"})
public String algo;
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

private KeyPairGenerator kpg;

@Setup
public void setup() throws Exception {
BenchmarkUtils.setupProvider(provider);
kpg = KeyPairGenerator.getInstance(algo, provider);
}

@Benchmark
public KeyPair generate() {
return kpg.generateKeyPair();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected void setup(
BenchmarkUtils.setupProvider(provider);
final KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlg, provider);
// Ed25519 in ACCP doesn't currently support initialization
if (!keyAlg.equals("Ed25519")) {
if (!keyAlg.equals("Ed25519") && !keyAlg.startsWith("ML-DSA")) {
kpg.initialize(keyParams);
}
keyPair = kpg.generateKeyPair();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.corretto.crypto.provider.benchmarks;

import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
public class SignatureMlDSA extends SignatureBase {
@Param({AmazonCorrettoCryptoProvider.PROVIDER_NAME, "BC"})
public String provider;

@Param({"ML-DSA-44", "ML-DSA-65", "ML-DSA-87"})
public String algo;
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

@Setup
public void setup() throws Exception {
super.setup(provider, algo, null, "ML-DSA", null);
}

@Benchmark
public byte[] sign() throws Exception {
return super.sign();
}

@Benchmark
public boolean verify() throws Exception {
return super.verify();
}
}
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (ext.isExperimentalFips) {

if (ext.isExperimentalFips || !ext.isFips) {
// Experimental FIPS uses the same AWS-LC version as non-FIPS builds.
ext.awsLcGitVersionId = 'v1.42.0'
ext.awsLcGitVersionId = 'v1.43.0'
} else {
ext.awsLcGitVersionId = 'AWS-LC-FIPS-3.0.0'
}
Expand Down Expand Up @@ -181,8 +181,8 @@ dependencies {
testDep 'org.apiguardian:apiguardian-api:1.1.2'
testDep 'org.junit.jupiter:junit-jupiter:5.8.2'
testDep 'org.junit.vintage:junit-vintage-engine:5.8.2'
testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.69'
testDep 'org.bouncycastle:bcpkix-jdk15on:1.69'
testDep 'org.bouncycastle:bcpkix-jdk18on:1.80'
testDep 'org.bouncycastle:bcprov-jdk18on:1.80'
testDep 'commons-codec:commons-codec:1.12'
testDep 'org.hamcrest:hamcrest:2.1'
testDep 'org.jacoco:org.jacoco.core:0.8.3'
Expand Down
42 changes: 42 additions & 0 deletions csrc/mldsa_gen.cpp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked through EVP logic - lgtm

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "auto_free.h"
#include "env.h"
#include "generated-headers.h"

#include <openssl/evp.h>
#include <openssl/nid.h>

using namespace AmazonCorrettoCryptoProvider;

JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_MlDsaGen_generateEvpMlDsaKey(
JNIEnv* pEnv, jclass, jint level)
{
try {
raii_env env(pEnv);
EVP_PKEY_auto key;
EVP_PKEY_CTX_auto ctx = EVP_PKEY_CTX_auto::from(EVP_PKEY_CTX_new_id(EVP_PKEY_PQDSA, NULL));
CHECK_OPENSSL(ctx.isInitialized());
int nid;
switch (level) {
case 2:
nid = NID_MLDSA44;
break;
case 3:
nid = NID_MLDSA65;
break;
case 5:
nid = NID_MLDSA87;
break;
default:
throw java_ex(EX_ILLEGAL_ARGUMENT, "Invalid level");
}
CHECK_OPENSSL(EVP_PKEY_CTX_pqdsa_set_params(ctx, nid));
CHECK_OPENSSL(EVP_PKEY_keygen_init(ctx) == 1);
CHECK_OPENSSL(EVP_PKEY_keygen(ctx, key.getAddressOfPtr()));
return reinterpret_cast<jlong>(key.take());
} catch (java_ex& ex) {
ex.throw_to_java(pEnv);
}
return 0;
}
12 changes: 12 additions & 0 deletions csrc/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ bool initializeContext(raii_env& env,
EVP_PKEY_up_ref(pKey);
ctx->setKey(pKey);

#if defined(FIPS_BUILD) && !defined(EXPERIMENTAL_FIPS_BUILD)
if (md != nullptr || EVP_PKEY_id(pKey) == EVP_PKEY_ED25519) {
#else
if (md != nullptr || EVP_PKEY_id(pKey) == EVP_PKEY_ED25519 || EVP_PKEY_id(pKey) == EVP_PKEY_PQDSA) {
#endif
if (!ctx->setDigestCtx(EVP_MD_CTX_create())) {
throw_openssl("Unable to create MD_CTX");
}
Expand Down Expand Up @@ -456,7 +460,11 @@ JNIEXPORT jbyteArray JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignatu

int keyType = EVP_PKEY_id(reinterpret_cast<EVP_PKEY*>(pKey));

#if defined(FIPS_BUILD) && !defined(EXPERIMENTAL_FIPS_BUILD)
if (keyType == EVP_PKEY_ED25519) {
#else
if (keyType == EVP_PKEY_ED25519 || keyType == EVP_PKEY_PQDSA) {
#endif
jni_borrow message(env, messageBuf, "message");

if (!EVP_DigestSign(ctx.getDigestCtx(), NULL, &sigLength, message.data(), message.len())) {
Expand Down Expand Up @@ -526,7 +534,11 @@ JNIEXPORT jboolean JNICALL Java_com_amazon_corretto_crypto_provider_EvpSignature

int ret;
int keyType = EVP_PKEY_id(reinterpret_cast<EVP_PKEY*>(pKey));
#if defined(FIPS_BUILD) && !defined(EXPERIMENTAL_FIPS_BUILD)
if (keyType == EVP_PKEY_ED25519) {
#else
if (keyType == EVP_PKEY_ED25519 || keyType == EVP_PKEY_PQDSA) {
#endif
ret = EVP_DigestVerify(
ctx.getDigestCtx(), signature.data(), signature.len(), message.data(), message.len());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ private void buildServiceMap() {
addService("KeyFactory", "RSA", "EvpKeyFactory$RSA");
addService("KeyFactory", "EC", "EvpKeyFactory$EC");

if (!isFips() || isExperimentalFips()) {
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
addService("KeyFactory", "ML-DSA", "EvpKeyFactory$MlDSA");
addService("KeyFactory", "ML-DSA-44", "EvpKeyFactory$MlDSA");
addService("KeyFactory", "ML-DSA-65", "EvpKeyFactory$MlDSA");
addService("KeyFactory", "ML-DSA-87", "EvpKeyFactory$MlDSA");
}

if (shouldRegisterEdDSA) {
// KeyFactories are used to convert key encodings to Java Key objects. ACCP's KeyFactory for
// Ed25519 returns keys of type EvpEdPublicKey and EvpEdPrivateKey that do not implement
Expand Down Expand Up @@ -127,6 +134,13 @@ private void buildServiceMap() {
addService("KeyPairGenerator", "RSA", "RsaGen");
addService("KeyPairGenerator", "EC", "EcGen");

if (!isFips() || isExperimentalFips()) {
addService("KeyPairGenerator", "ML-DSA", "MlDsaGen$MlDsaGen44");
addService("KeyPairGenerator", "ML-DSA-44", "MlDsaGen$MlDsaGen44");
addService("KeyPairGenerator", "ML-DSA-65", "MlDsaGen$MlDsaGen65");
addService("KeyPairGenerator", "ML-DSA-87", "MlDsaGen$MlDsaGen87");
}

addService("KeyGenerator", "AES", "keygeneratorspi.SecretKeyGenerator", false);

addService("Cipher", "AES/XTS/NoPadding", "AesXtsSpi", false);
Expand Down Expand Up @@ -213,6 +227,10 @@ private void addSignatures() {
addService("Signature", "EdDSA", "EvpSignatureRaw$Ed25519");
addService("Signature", "Ed25519", "EvpSignatureRaw$Ed25519");
}

if (!isFips() || isExperimentalFips()) {
addService("Signature", "ML-DSA", "EvpSignatureRaw$MlDSA");
}
}

private ACCPService addService(
Expand Down Expand Up @@ -714,6 +732,7 @@ private void readObjectNoData() {
private transient volatile KeyFactory rsaFactory;
private transient volatile KeyFactory ecFactory;
private transient volatile KeyFactory edFactory;
private transient volatile KeyFactory mlDsaFactory;

KeyFactory getKeyFactory(EvpKeyType keyType) {
try {
Expand All @@ -733,8 +752,13 @@ KeyFactory getKeyFactory(EvpKeyType keyType) {
edFactory = new EdKeyFactory(this);
}
return edFactory;
case MlDSA:
if (mlDsaFactory == null) {
mlDsaFactory = KeyFactory.getInstance(keyType.jceName, this);
}
return mlDsaFactory;
default:
throw new AssertionError("Unsupported key type");
throw new AssertionError(String.format("Unsupported key type: %s", keyType.jceName));
}
} catch (final NoSuchAlgorithmException ex) {
throw new AssertionError(ex);
Expand Down
4 changes: 0 additions & 4 deletions src/com/amazon/corretto/crypto/provider/EvpEdPublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
class EvpEdPublicKey extends EvpEdKey implements PublicKey {
private static final long serialVersionUID = 1;

private static native byte[] getPublicKey(long ptr);

private volatile byte[] publicKey;

EvpEdPublicKey(final long ptr) {
this(new InternalKey(ptr));
}
Expand Down
24 changes: 18 additions & 6 deletions src/com/amazon/corretto/crypto/provider/EvpKeyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
}

protected boolean keyNeedsConversion(Key key) throws InvalidKeyException {
if (!type.jceName.equalsIgnoreCase(key.getAlgorithm())) {
throw new InvalidKeyException("Incorrect key algorithm: " + key.getAlgorithm());
if (key.getAlgorithm() == null || !key.getAlgorithm().startsWith(type.jceName)) {
throw new InvalidKeyException(
"Incorrect key algorithm: " + key.getAlgorithm() + ". Expected: " + type.jceName);
}
return !(key instanceof EvpKey);
}
Expand Down Expand Up @@ -303,10 +304,9 @@ protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
}
}

static class EdDSA extends EvpKeyFactory {

EdDSA(AmazonCorrettoCryptoProvider provider) {
super(EvpKeyType.EdDSA, provider);
private abstract static class StandardEvpKeyFactory extends EvpKeyFactory {
StandardEvpKeyFactory(EvpKeyType type, AmazonCorrettoCryptoProvider provider) {
super(type, provider);
}

@Override
Expand All @@ -326,4 +326,16 @@ protected <T extends KeySpec> T engineGetKeySpec(final Key key, final Class<T> k
return super.engineGetKeySpec(key, keySpec);
}
}

static class EdDSA extends StandardEvpKeyFactory {
EdDSA(AmazonCorrettoCryptoProvider provider) {
super(EvpKeyType.EdDSA, provider);
}
}

static class MlDSA extends StandardEvpKeyFactory {
MlDSA(AmazonCorrettoCryptoProvider provider) {
super(EvpKeyType.MlDSA, provider);
}
}
}
7 changes: 6 additions & 1 deletion src/com/amazon/corretto/crypto/provider/EvpKeyType.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
enum EvpKeyType {
RSA("RSA", 6, RSAPublicKey.class, RSAPrivateKey.class),
EC("EC", 408, ECPublicKey.class, ECPrivateKey.class),
EdDSA("EdDSA", 949, PublicKey.class, PrivateKey.class);
EdDSA("EdDSA", 949, PublicKey.class, PrivateKey.class),
MlDSA("ML-DSA", 993, PublicKey.class, PrivateKey.class);
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

final String jceName;
final int nativeValue;
Expand Down Expand Up @@ -58,6 +59,8 @@ <X extends Throwable> PrivateKey buildPrivateKey(
return new EvpEcPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue));
case EdDSA:
return new EvpEdPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue));
case MlDSA:
return new EvpMlDsaPrivateKey(fn.applyAsLong(der.getEncoded(), nativeValue));
default:
throw new AssertionError("Unsupported key type");
}
Expand All @@ -73,6 +76,8 @@ <X extends Throwable> PublicKey buildPublicKey(
return new EvpEcPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue));
case EdDSA:
return new EvpEdPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue));
case MlDSA:
return new EvpMlDsaPublicKey(fn.applyAsLong(der.getEncoded(), nativeValue));
default:
throw new AssertionError("Unsupported key type");
}
Expand Down
11 changes: 11 additions & 0 deletions src/com/amazon/corretto/crypto/provider/EvpMlDsaKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.corretto.crypto.provider;

abstract class EvpMlDsaKey extends EvpKey {
private static final long serialVersionUID = 1;

EvpMlDsaKey(InternalKey key, final boolean isPublicKey) {
super(key, EvpKeyType.MlDSA, isPublicKey);
}
}
25 changes: 25 additions & 0 deletions src/com/amazon/corretto/crypto/provider/EvpMlDsaPrivateKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.corretto.crypto.provider;

import java.security.PrivateKey;

class EvpMlDsaPrivateKey extends EvpMlDsaKey implements PrivateKey {
private static final long serialVersionUID = 1;

EvpMlDsaPrivateKey(final long ptr) {
this(new InternalKey(ptr));
}

EvpMlDsaPrivateKey(final InternalKey key) {
super(key, false);
}

public EvpMlDsaPublicKey getPublicKey() {
this.ephemeral = false;
this.sharedKey = true;
final EvpMlDsaPublicKey result = new EvpMlDsaPublicKey(internalKey);
result.sharedKey = true;
return result;
}
}
Loading