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 2 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ SecretKeyFactory:
* HkdfWithHmacSHA256
* HkdfWithHmacSHA384
* HkdfWithHmacSHA512
* ConcatenationKdfWithSHA224 (not available in FIPS builds)
amirhosv marked this conversation as resolved.
Show resolved Hide resolved
* 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
2 changes: 2 additions & 0 deletions csrc/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ EVP_MD const* digest_code_to_EVP_MD(int digestCode)
switch (digestCode) {
case com_amazon_corretto_crypto_provider_Utils_SHA1_CODE:
return EVP_sha1();
case com_amazon_corretto_crypto_provider_Utils_SHA224_CODE:
return EVP_sha224();
case com_amazon_corretto_crypto_provider_Utils_SHA256_CODE:
return EVP_sha256();
case com_amazon_corretto_crypto_provider_Utils_SHA384_CODE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
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_SHA224;
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 +93,17 @@ 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_SHA224, concatenationKdfSpi, false);
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 +326,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,71 @@
// 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 static final byte[] EMPTY = new byte[0];
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, EMPTY, EMPTY);
}

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

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