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

ConcatinationKDF: SSKDF from NIST.SP.800-56Cr2 Key Derivation #397

Merged
merged 3 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
79 changes: 41 additions & 38 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -247,38 +247,47 @@ ADD_CUSTOM_COMMAND(
### Native library configuration
include_directories(${OPENSSL_INCLUDE_DIR} ${JNI_INCLUDE_DIRS} ${JNI_HEADER_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src/cpp)

add_library(
amazonCorrettoCryptoProvider SHARED
csrc/aes_gcm.cpp
csrc/aes_xts.cpp
csrc/aes_cbc.cpp
csrc/aes_kwp.cpp
csrc/agreement.cpp
csrc/bn.cpp
csrc/buffer.cpp
csrc/ec_gen.cpp
csrc/ec_utils.cpp
csrc/env.cpp
csrc/hkdf.cpp
csrc/hmac.cpp
csrc/keyutils.cpp
csrc/java_evp_keys.cpp
csrc/libcrypto_rng.cpp
csrc/loader.cpp
csrc/md5.cpp
csrc/rsa_cipher.cpp
csrc/rsa_gen.cpp
csrc/sha1.cpp
csrc/sha256.cpp
csrc/sha384.cpp
csrc/sha512.cpp
csrc/sign.cpp
csrc/testhooks.cpp
csrc/util.cpp
csrc/util_class.cpp
csrc/fips_kat_self_test.cpp
${JNI_HEADER_DIR}/generated-headers.h
)
set(C_SRC
csrc/aes_gcm.cpp
csrc/aes_xts.cpp
csrc/aes_cbc.cpp
csrc/aes_kwp.cpp
csrc/agreement.cpp
csrc/bn.cpp
csrc/buffer.cpp
csrc/ec_gen.cpp
csrc/ec_utils.cpp
csrc/env.cpp
csrc/hkdf.cpp
csrc/hmac.cpp
csrc/keyutils.cpp
csrc/java_evp_keys.cpp
csrc/libcrypto_rng.cpp
csrc/loader.cpp
csrc/md5.cpp
csrc/rsa_cipher.cpp
csrc/rsa_gen.cpp
csrc/sha1.cpp
csrc/sha256.cpp
csrc/sha384.cpp
csrc/sha512.cpp
csrc/sign.cpp
csrc/testhooks.cpp
csrc/util.cpp
csrc/util_class.cpp
csrc/fips_kat_self_test.cpp
${JNI_HEADER_DIR}/generated-headers.h)

if(FIPS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFIPS_BUILD")
set(TEST_FIPS_PROPERTY "-DFIPS=true")
else()
set(TEST_FIPS_PROPERTY "-DFIPS=false")
set(C_SRC ${C_SRC}
csrc/concatenation_kdf.cpp)
endif()

add_library(amazonCorrettoCryptoProvider SHARED ${C_SRC})

add_custom_command(
OUTPUT ${ACCP_JAR_SOURCE}
Expand Down Expand Up @@ -616,12 +625,6 @@ set(COVERAGE_ARGUMENTS
-javaagent:${JACOCO_AGENT_JAR}=destfile=coverage/jacoco.exec,classdumpdir=coverage/classes
)

if(FIPS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFIPS_BUILD")
set(TEST_FIPS_PROPERTY "-DFIPS=true")
else()
set(TEST_FIPS_PROPERTY "-DFIPS=false")
endif()

if(ALWAYS_ALLOW_EXTERNAL_LIB)
set(EXTERNAL_LIB_PROPERTY "-Djava.library.path=$<TARGET_FILE_DIR:amazonCorrettoCryptoProvider>")
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ SecretKeyFactory:
* HkdfWithHmacSHA256
* HkdfWithHmacSHA384
* HkdfWithHmacSHA512
* ConcatenationKdfWithSHA256 (not available in FIPS builds)
* ConcatenationKdfWithSHA384 (not available in FIPS builds)
* ConcatenationKdfWithSHA512 (not available in FIPS builds)
* ConcatenationKdfWithHmacSHA256 (not available in FIPS builds)
* ConcatenationKdfWithHmacSHA512 (not available in FIPS builds)

SecureRandom:
* ACCP's SecureRandom uses [AWS-LC's DRBG implementation](https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/rand/rand.c).
Expand Down
2 changes: 1 addition & 1 deletion aws-lc
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ext.isFips = Boolean.getBoolean('FIPS')
if (ext.isFips) {
ext.awsLcGitVersionId = 'AWS-LC-FIPS-2.0.13'
} else {
ext.awsLcGitVersionId = 'v1.30.1'
ext.awsLcGitVersionId = 'v1.33.0'
}

// Check for user inputted git version ID.
Expand Down
61 changes: 61 additions & 0 deletions csrc/concatenation_kdf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "buffer.h"
#include "env.h"
#include "generated-headers.h"
#include <openssl/evp.h>
#include <openssl/kdf.h>

using namespace AmazonCorrettoCryptoProvider;

extern "C" JNIEXPORT void Java_com_amazon_corretto_crypto_provider_ConcatenationKdfSpi_nSskdfDigest(JNIEnv* env,
jclass,
jint digestCode,
jbyteArray jSecret,
jint secretLen,
jbyteArray jInfo,
jint infoLen,
jbyteArray jOutput,
jint outputLen)
{
try {
EVP_MD const* digest = digest_code_to_EVP_MD(digestCode);
JBinaryBlob secret(env, nullptr, jSecret);
JBinaryBlob info(env, nullptr, jInfo);
JBinaryBlob output(env, nullptr, jOutput);
if (SSKDF_digest(output.get(), outputLen, digest, secret.get(), secretLen, info.get(), infoLen) != 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "SSKDF_digest failed.");
}
} catch (java_ex& ex) {
ex.throw_to_java(env);
}
}

extern "C" JNIEXPORT void JNICALL Java_com_amazon_corretto_crypto_provider_ConcatenationKdfSpi_nSskdfHmac(JNIEnv* env,
jclass,
jint digestCode,
jbyteArray jSecret,
jint secretLen,
jbyteArray jInfo,
jint infoLen,
jbyteArray jSalt,
jint saltLen,
jbyteArray jOutput,
jint outputLen)
{
try {
EVP_MD const* digest = digest_code_to_EVP_MD(digestCode);
JBinaryBlob secret(env, nullptr, jSecret);
JBinaryBlob info(env, nullptr, jInfo);
JBinaryBlob salt(env, nullptr, jSalt);
JBinaryBlob output(env, nullptr, jOutput);
if (SSKDF_hmac(
output.get(), outputLen, digest, secret.get(), secretLen, info.get(), infoLen, salt.get(), saltLen)
!= 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "SSKDF_hmac failed.");
}

} catch (java_ex& ex) {
ex.throw_to_java(env);
}
}
2 changes: 1 addition & 1 deletion csrc/keyutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ RSA* new_private_RSA_key_with_no_e(BIGNUM const* n, BIGNUM const* d)

#else

RSA* result = RSA_new_private_key_no_e(n, d);
RSA* result = ::RSA_new_private_key_no_e(n, d);

if (result == nullptr) {
throw_openssl("RSA_new_private_key_no_e failed.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import static com.amazon.corretto.crypto.provider.AesCbcSpi.AES_CBC_ISO10126_PADDING_NAMES;
import static com.amazon.corretto.crypto.provider.AesCbcSpi.AES_CBC_NO_PADDING_NAMES;
import static com.amazon.corretto.crypto.provider.AesCbcSpi.AES_CBC_PKCS7_PADDING_NAMES;
import static com.amazon.corretto.crypto.provider.ConcatenationKdfSpi.CKDF_WITH_HMAC_SHA256;
import static com.amazon.corretto.crypto.provider.ConcatenationKdfSpi.CKDF_WITH_HMAC_SHA512;
import static com.amazon.corretto.crypto.provider.ConcatenationKdfSpi.CKDF_WITH_SHA256;
import static com.amazon.corretto.crypto.provider.ConcatenationKdfSpi.CKDF_WITH_SHA384;
import static com.amazon.corretto.crypto.provider.ConcatenationKdfSpi.CKDF_WITH_SHA512;
import static com.amazon.corretto.crypto.provider.HkdfSecretKeyFactorySpi.HKDF_WITH_SHA1;
import static com.amazon.corretto.crypto.provider.HkdfSecretKeyFactorySpi.HKDF_WITH_SHA256;
import static com.amazon.corretto.crypto.provider.HkdfSecretKeyFactorySpi.HKDF_WITH_SHA384;
Expand Down Expand Up @@ -87,6 +92,16 @@ private void buildServiceMap() {
addService("SecretKeyFactory", HKDF_WITH_SHA384, hkdfSpi, false);
addService("SecretKeyFactory", HKDF_WITH_SHA512, hkdfSpi, false);

// Once these KDFs are added to a FIPS branch of AWS-LC, we can remove this check.
if (!Loader.FIPS_BUILD) {
final String concatenationKdfSpi = "ConcatenationKdf";
addService("SecretKeyFactory", CKDF_WITH_SHA256, concatenationKdfSpi, false);
addService("SecretKeyFactory", CKDF_WITH_SHA384, concatenationKdfSpi, false);
addService("SecretKeyFactory", CKDF_WITH_SHA512, concatenationKdfSpi, false);
addService("SecretKeyFactory", CKDF_WITH_HMAC_SHA256, concatenationKdfSpi, false);
addService("SecretKeyFactory", CKDF_WITH_HMAC_SHA512, concatenationKdfSpi, false);
}

addService("KeyPairGenerator", "RSA", "RsaGen");
addService("KeyPairGenerator", "EC", "EcGen");

Expand Down Expand Up @@ -309,6 +324,12 @@ public Object newInstance(final Object constructorParameter) throws NoSuchAlgori
if (spi != null) {
return spi;
}

final ConcatenationKdfSpi ckdfSpi =
ConcatenationKdfSpi.INSTANCES.get(ConcatenationKdfSpi.getSpiFactoryForAlgName(algo));
if (ckdfSpi != null) {
return ckdfSpi;
}
}

if ("KeyGenerator".equalsIgnoreCase(type) && "AES".equalsIgnoreCase(algo)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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.spec.KeySpec;
import java.util.Objects;

/**
* Represents the inputs to ConcatenationKdf algorithms.
*
* <p>If info or salt is not provided, empty byte arrays are used.
*
* <p>The algorithmName is the name of algorithm used to create SecretKeySpec.
Comment on lines +11 to +13

Choose a reason for hiding this comment

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

Suggested change
* <p>If info or salt is not provided, empty byte arrays are used.
*
* <p>The algorithmName is the name of algorithm used to create SecretKeySpec.
* <p>If info or salt is not provided, empty byte arrays are used.</p>
*
* <p>The algorithmName is the name of algorithm used to create SecretKeySpec.</p>

Choose a reason for hiding this comment

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

Turns out this is unnecessary in a java docstring, and the linter will even remove the close tags. 🤦

*/
public class ConcatenationKdfSpec implements KeySpec {
private final byte[] secret;
private final int outputLen;
private final String algorithmName;
private final byte[] info;
private final byte[] salt;

public ConcatenationKdfSpec(
final byte[] secret,
final int outputLen,
final String algorithmName,
final byte[] info,
final byte[] salt) {
this.secret = Objects.requireNonNull(secret);
if (this.secret.length == 0) {
throw new IllegalArgumentException("Secret must be byte array with non-zero length.");
}
if (outputLen <= 0) {
throw new IllegalArgumentException("Output size must be greater than zero.");
}
this.outputLen = outputLen;
this.algorithmName = Objects.requireNonNull(algorithmName);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should you validate the algorithmName here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The KDFs are used in different context. It's hard to come up with the exhaustive list of all acceptable algorithm names.

this.info = Objects.requireNonNull(info);
this.salt = Objects.requireNonNull(salt);
}

public ConcatenationKdfSpec(
final byte[] secret, final int outputLen, final String algorithmName) {
this(secret, outputLen, algorithmName, Utils.EMPTY_ARRAY, Utils.EMPTY_ARRAY);
}

public ConcatenationKdfSpec(
final byte[] secret, final int outputLen, final String algorithmName, final byte[] info) {
this(secret, outputLen, algorithmName, info, Utils.EMPTY_ARRAY);
}

public byte[] getSecret() {
return secret;
}

public byte[] getInfo() {
return info;
}

public int getOutputLen() {
return outputLen;
}

public byte[] getSalt() {
return salt;
}

public String getAlgorithmName() {
return algorithmName;
}
}
Loading
Loading