Skip to content

Commit

Permalink
ConcatinationKDF: SSKDF from NIST.SP.800-56Cr2 Key Derivation
Browse files Browse the repository at this point in the history
* The following algorithms for SecretKeyFactory are added for non-FIPS
  builds:
  * ConcatenationKdfWithSHA224
  * ConcatenationKdfWithSHA256
  * ConcatenationKdfWithSHA384
  * ConcatenationKdfWithSHA512
  * ConcatenationKdfWithHmacSHA256
  * ConcatenationKdfWithHmacSHA512
  • Loading branch information
amirhosv committed Aug 8, 2024
1 parent d71028c commit 4dbd6c0
Show file tree
Hide file tree
Showing 14 changed files with 1,717 additions and 43 deletions.
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)
* 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
60 changes: 60 additions & 0 deletions src/com/amazon/corretto/crypto/provider/ConcatenationKdfSpec.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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;
import java.util.Optional;

/**
* Represents the inputs to ConcatenationKdf algorithms.
*
* <p>When using HMAC variants, salt must be provided. The algorithmName is the name of algorithm
* used to create SecretKeySpec.
*/
public class ConcatenationKdfSpec implements KeySpec {
private final byte[] secret;
private final byte[] info;
private final Optional<byte[]> salt;
private final int outputLen;
private final String algorithmName;

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

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

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

public int getOutputLen() {
return outputLen;
}

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

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

0 comments on commit 4dbd6c0

Please sign in to comment.