Skip to content

Commit

Permalink
Use libcrypto signing methods in compliance with FIPS 140-3 (#3142)
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart authored Dec 18, 2021
1 parent 5b5add9 commit b55ab81
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 28 deletions.
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,19 @@ if (LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH)
target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH)
endif()

# Determine if EVP_MD_CTX_set_pkey_ctx is available in libcrypto
try_compile(
LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX
${CMAKE_BINARY_DIR}
SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/evp_md_ctx_set_pkey_ctx.c"
LINK_LIBRARIES crypto ${OS_LIBS}
CMAKE_FLAGS
"-DINCLUDE_DIRECTORIES=$<TARGET_PROPERTY:crypto,INTERFACE_INCLUDE_DIRECTORIES>"
)
if (LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX)
target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX)
endif()

if (S2N_INTERN_LIBCRYPTO)
if (NOT LibCrypto_STATIC_LIBRARY)
message(FATAL_ERROR "libcrypto interning requires a static build of libcrypto.a to be available")
Expand Down
2 changes: 2 additions & 0 deletions crypto/s2n_ecdsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "crypto/s2n_ecdsa.h"
#include "crypto/s2n_ecc_evp.h"
#include "crypto/s2n_evp_signing.h"
#include "crypto/s2n_hash.h"
#include "crypto/s2n_openssl.h"
#include "crypto/s2n_pkey.h"
Expand Down Expand Up @@ -183,6 +184,7 @@ int s2n_ecdsa_pkey_init(struct s2n_pkey *pkey) {
pkey->match = &s2n_ecdsa_keys_match;
pkey->free = &s2n_ecdsa_key_free;
pkey->check_key = &s2n_ecdsa_check_key_exists;
POSIX_GUARD_RESULT(s2n_evp_signing_set_pkey_overrides(pkey));
return 0;
}

Expand Down
7 changes: 7 additions & 0 deletions crypto/s2n_evp.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,12 @@ struct s2n_evp_hmac_state {
#define S2N_EVP_MD_CTX_FREE(md_ctx) (EVP_MD_CTX_destroy(md_ctx))
#endif

/* On some versions of OpenSSL, "EVP_PKEY_CTX_set_signature_md()" is just a macro that casts digest_alg to "void*",
* which fails to compile when the "-Werror=cast-qual" compiler flag is enabled. So we work around this OpenSSL
* issue by turning off this compiler check for this one function with a cast through.
*/
#define S2N_EVP_PKEY_CTX_set_signature_md(ctx, md) \
EVP_PKEY_CTX_set_signature_md(ctx, (EVP_MD*) (uintptr_t) md)

extern int s2n_digest_allow_md5_for_fips(struct s2n_evp_digest *evp_digest);
extern S2N_RESULT s2n_digest_is_md5_allowed_for_fips(struct s2n_evp_digest *evp_digest, bool *out);
153 changes: 153 additions & 0 deletions crypto/s2n_evp_signing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include "error/s2n_errno.h"

#include "crypto/s2n_evp.h"
#include "crypto/s2n_evp_signing.h"
#include "crypto/s2n_pkey.h"
#include "crypto/s2n_rsa_pss.h"

#include "utils/s2n_safety.h"

/*
* FIPS 140-3 requires that we don't pass raw digest bytes to the libcrypto signing methods.
* In order to do that, we need to use signing methods that both calculate the digest and
* perform the signature.
*/

static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx)
{
#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX
EVP_MD_CTX_set_pkey_ctx(ctx, pctx);
return S2N_RESULT_OK;
#else
RESULT_BAIL(S2N_ERR_UNIMPLEMENTED);
#endif
}

static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx)
{
#if RSA_PSS_SIGNING_SUPPORTED
RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT);
return S2N_RESULT_OK;
#else
RESULT_BAIL(S2N_ERR_UNIMPLEMENTED);
#endif
}

bool s2n_evp_signing_supported()
{
#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX
/* We can only use EVP signing if the hash state has an EVP_MD_CTX
* that we can pass to the EVP signing methods.
*/
return s2n_hash_evp_fully_supported();
#else
return false;
#endif
}

/* If using EVP signing, override the sign and verify pkey methods.
* The EVP methods can handle all pkey types / signature algorithms.
*/
S2N_RESULT s2n_evp_signing_set_pkey_overrides(struct s2n_pkey *pkey)
{
if (s2n_evp_signing_supported()) {
RESULT_ENSURE_REF(pkey);
pkey->sign = &s2n_evp_sign;
pkey->verify = &s2n_evp_verify;
}
return S2N_RESULT_OK;
}

static S2N_RESULT s2n_evp_signing_validate_hash_alg(s2n_signature_algorithm sig_alg, s2n_hash_algorithm hash_alg)
{
switch(hash_alg) {
case S2N_HASH_NONE:
case S2N_HASH_MD5:
/* MD5 alone is never supported */
RESULT_BAIL(S2N_ERR_HASH_INVALID_ALGORITHM);
break;
case S2N_HASH_MD5_SHA1:
/* Only RSA supports MD5+SHA1.
* This should not be a problem, as we only allow MD5+SHA1 when
* falling back to TLS1.0 or 1.1, which only support RSA.
*/
RESULT_ENSURE(sig_alg == S2N_SIGNATURE_RSA, S2N_ERR_HASH_INVALID_ALGORITHM);
break;
default:
break;
}
/* Hash algorithm must be recognized and supported by EVP_MD */
RESULT_ENSURE(s2n_hash_alg_to_evp_md(hash_alg) != NULL, S2N_ERR_HASH_INVALID_ALGORITHM);
return S2N_RESULT_OK;
}

int s2n_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(priv);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);
POSIX_ENSURE(s2n_evp_signing_supported(), S2N_ERR_HASH_NOT_READY);
POSIX_GUARD_RESULT(s2n_evp_signing_validate_hash_alg(sig_alg, hash_state->alg));

EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(priv->pkey, NULL);
POSIX_ENSURE_REF(pctx);
POSIX_GUARD_OSSL(EVP_PKEY_sign_init(pctx), S2N_ERR_PKEY_CTX_INIT);
POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT);

if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) {
POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT);
POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx));
}

EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx;
POSIX_ENSURE_REF(ctx);
POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx));

size_t signature_size = signature->size;
POSIX_GUARD_OSSL(EVP_DigestSignFinal(ctx, signature->data, &signature_size), S2N_ERR_SIGN);
POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH);
signature->size = signature_size;
return S2N_SUCCESS;
}

int s2n_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(pub);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);
POSIX_ENSURE(s2n_evp_signing_supported(), S2N_ERR_HASH_NOT_READY);
POSIX_GUARD_RESULT(s2n_evp_signing_validate_hash_alg(sig_alg, hash_state->alg));

EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pub->pkey, NULL);
POSIX_ENSURE_REF(pctx);
POSIX_GUARD_OSSL(EVP_PKEY_verify_init(pctx), S2N_ERR_PKEY_CTX_INIT);
POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT);

if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) {
POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT);
}

EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx;
POSIX_ENSURE_REF(ctx);
POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx));

POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE);
return S2N_SUCCESS;
}
29 changes: 29 additions & 0 deletions crypto/s2n_evp_signing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#pragma once

#include "api/s2n.h"

#include "crypto/s2n_hash.h"
#include "crypto/s2n_signature.h"
#include "utils/s2n_blob.h"

bool s2n_evp_signing_supported();
S2N_RESULT s2n_evp_signing_set_pkey_overrides(struct s2n_pkey *pkey);
int s2n_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *digest, struct s2n_blob *signature);
int s2n_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *digest, struct s2n_blob *signature);
12 changes: 11 additions & 1 deletion crypto/s2n_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ static bool s2n_use_custom_md5_sha1()
#endif
}

static bool s2n_use_evp_impl()
{
return s2n_is_in_fips_mode();
}

bool s2n_hash_evp_fully_supported()
{
return s2n_use_evp_impl() && !s2n_use_custom_md5_sha1();
}

const EVP_MD* s2n_hash_alg_to_evp_md(s2n_hash_algorithm alg)
{
switch (alg) {
Expand Down Expand Up @@ -475,7 +485,7 @@ static const struct s2n_hash s2n_evp_hash = {
static int s2n_hash_set_impl(struct s2n_hash_state *state)
{
state->hash_impl = &s2n_low_level_hash;
if (s2n_is_in_fips_mode()) {
if (s2n_use_evp_impl()) {
state->hash_impl = &s2n_evp_hash;
}
return S2N_SUCCESS;
Expand Down
1 change: 1 addition & 0 deletions crypto/s2n_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct s2n_hash {
int (*free) (struct s2n_hash_state *state);
};

bool s2n_hash_evp_fully_supported();
const EVP_MD* s2n_hash_alg_to_evp_md(s2n_hash_algorithm alg);
extern int s2n_hash_digest_size(s2n_hash_algorithm alg, uint8_t *out);
extern int s2n_hash_block_size(s2n_hash_algorithm alg, uint64_t *block_size);
Expand Down
2 changes: 2 additions & 0 deletions crypto/s2n_rsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "crypto/s2n_drbg.h"
#include "crypto/s2n_hash.h"
#include "crypto/s2n_pkey.h"
#include "crypto/s2n_evp_signing.h"
#include "crypto/s2n_rsa_signing.h"
#include "error/s2n_errno.h"
#include "stuffer/s2n_stuffer.h"
Expand Down Expand Up @@ -193,6 +194,7 @@ int s2n_rsa_pkey_init(struct s2n_pkey *pkey)
pkey->match = &s2n_rsa_keys_match;
pkey->free = &s2n_rsa_key_free;
pkey->check_key = &s2n_rsa_check_key_exists;
POSIX_GUARD_RESULT(s2n_evp_signing_set_pkey_overrides(pkey));
return 0;
}

2 changes: 2 additions & 0 deletions crypto/s2n_rsa_pss.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "stuffer/s2n_stuffer.h"

#include "crypto/s2n_evp_signing.h"
#include "crypto/s2n_hash.h"
#include "crypto/s2n_openssl.h"
#include "crypto/s2n_rsa.h"
Expand Down Expand Up @@ -223,6 +224,7 @@ int s2n_rsa_pss_pkey_init(struct s2n_pkey *pkey)
pkey->match = &s2n_rsa_pss_keys_match;
pkey->free = &s2n_rsa_pss_key_free;

POSIX_GUARD_RESULT(s2n_evp_signing_set_pkey_overrides(pkey));
return 0;
}

Expand Down
28 changes: 3 additions & 25 deletions crypto/s2n_rsa_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ static int s2n_hash_alg_to_NID[] = {
[S2N_HASH_SHA224] = NID_sha224,
[S2N_HASH_SHA256] = NID_sha256,
[S2N_HASH_SHA384] = NID_sha384,
[S2N_HASH_SHA512] = NID_sha512 };
[S2N_HASH_SHA512] = NID_sha512
};

int s2n_hash_NID_type(s2n_hash_algorithm alg, int *out)
{
Expand Down Expand Up @@ -117,32 +118,9 @@ int s2n_is_rsa_pss_signing_supported()

#if RSA_PSS_SIGNING_SUPPORTED

const EVP_MD* s2n_hash_alg_to_evp_alg(s2n_hash_algorithm alg)
{
switch (alg) {
case S2N_HASH_MD5_SHA1:
return EVP_md5_sha1();
case S2N_HASH_SHA1:
return EVP_sha1();
case S2N_HASH_SHA224:
return EVP_sha224();
case S2N_HASH_SHA256:
return EVP_sha256();
case S2N_HASH_SHA384:
return EVP_sha384();
case S2N_HASH_SHA512:
return EVP_sha512();
default:
return NULL;
}
}

/* On some versions of OpenSSL, "EVP_PKEY_CTX_set_signature_md()" is just a macro that casts digest_alg to "void*",
* which fails to compile when the "-Werror=cast-qual" compiler flag is enabled. So we work around this OpenSSL
* issue by turning off this compiler check for this one function with a cast through. */
static int s2n_evp_pkey_ctx_set_rsa_signature_digest(EVP_PKEY_CTX *ctx, const EVP_MD* digest_alg)
{
POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_signature_md(ctx,(EVP_MD*) (uintptr_t) digest_alg), S2N_ERR_INVALID_SIGNATURE_ALGORITHM);
POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(ctx, digest_alg), S2N_ERR_INVALID_SIGNATURE_ALGORITHM);
POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, (EVP_MD*) (uintptr_t) digest_alg), S2N_ERR_INVALID_SIGNATURE_ALGORITHM);
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_INSUFFICIENT_MEM_SIZE, "The provided buffer size is not large enough to contain the output data. Try increasing the allocation size.") \
ERR_ENTRY(S2N_ERR_KEYING_MATERIAL_EXPIRED, "The lifetime of the connection keying material has exceeded the limit. Perform a new full handshake.") \
ERR_ENTRY(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, "Unable to decrypt rejected early data") \

ERR_ENTRY(S2N_ERR_PKEY_CTX_INIT, "Unable to initialize the libcrypto pkey context") \
/* clang-format on */

#define ERR_STR_CASE(ERR, str) case ERR: return str;
Expand Down
1 change: 1 addition & 0 deletions error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ typedef enum {
S2N_ERR_PQ_DISABLED,
S2N_ERR_INVALID_CERT_STATE,
S2N_ERR_INVALID_EARLY_DATA_STATE,
S2N_ERR_PKEY_CTX_INIT,
S2N_ERR_T_INTERNAL_END,

/* S2N_ERR_T_USAGE */
Expand Down
6 changes: 6 additions & 0 deletions s2n.mk
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ ifeq ($(TRY_EVP_MD5_SHA1_HASH), 0)
DEFAULT_CFLAGS += -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH
endif

# Determine if EVP_MD_CTX_set_pkey_ctx is available
TRY_EVP_MD_CTX_SET_PKEY_CTX := $(call try_compile,$(S2N_ROOT)/tests/features/evp_md_ctx_set_pkey_ctx.c)
ifeq ($(TRY_EVP_MD_CTX_SET_PKEY_CTX), 0)
DEFAULT_CFLAGS += -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX
endif

CFLAGS_LLVM = ${DEFAULT_CFLAGS} -emit-llvm -c -g -O1

$(BITCODE_DIR)%.bc: %.c
Expand Down
20 changes: 20 additions & 0 deletions tests/features/evp_md_ctx_set_pkey_ctx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include <openssl/evp.h>
int main() {
EVP_MD_CTX_set_pkey_ctx(NULL, NULL);
return 0;
}
Loading

0 comments on commit b55ab81

Please sign in to comment.