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

refactor: add alternative EVP signing method #5141

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions codebuild/spec/buildspec_openssl3fips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ phases:
# openssl3fips is still a work-in-progress. Not all tests pass.
- make -C build test -- ARGS="-R 's2n_build_test|s2n_fips_test'"
- make -C build test -- ARGS="-R 's2n_hash_test|s2n_hash_all_algs_test|s2n_openssl_test|s2n_init_test'"
- make -C build test -- ARGS="-R 's2n_evp_signing_test'"
191 changes: 152 additions & 39 deletions crypto/s2n_evp_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "crypto/s2n_evp_signing.h"

#include "crypto/s2n_evp.h"
#include "crypto/s2n_libcrypto.h"
#include "crypto/s2n_pkey.h"
#include "crypto/s2n_rsa_pss.h"
#include "error/s2n_errno.h"
Expand All @@ -24,12 +25,6 @@

DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free);

/*
* 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
Expand All @@ -50,28 +45,16 @@ static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx)
#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.
/* Always use EVP signing.
*
* TODO: Migrate the rest of the s2n_pkey methods to EVP and delete the legacy
* pkey logic and this method.
*/
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;
}
RESULT_ENSURE_REF(pkey);
pkey->sign = &s2n_evp_sign;
pkey->verify = &s2n_evp_verify;
return S2N_RESULT_OK;
}

Expand All @@ -89,15 +72,110 @@ static S2N_RESULT s2n_evp_signing_validate_sig_alg(const struct s2n_pkey *key, s
return S2N_RESULT_OK;
}

static EVP_PKEY_CTX *s2n_evp_pkey_ctx_new(EVP_PKEY *pkey, s2n_hash_algorithm hash_alg)
{
switch (hash_alg) {
#if S2N_LIBCRYPTO_SUPPORTS_PROVIDERS
/* For openssl-3.0, pkey methods will do an implicit fetch for the signing
* algorithm, which includes the hash algorithm. If using a legacy hash
* algorithm, specify the non-fips version.
*/
case S2N_HASH_MD5:
case S2N_HASH_MD5_SHA1:
case S2N_HASH_SHA1:
return EVP_PKEY_CTX_new_from_pkey(NULL, pkey, "-fips");
#endif
default:
return EVP_PKEY_CTX_new(pkey, NULL);
}
}

/* Our "digest-and-sign" EVP signing logic is intended to support FIPS 140-3.
* FIPS 140-3 does not allow signing or verifying externally calculated digests
* (except for signing, but not verifying, with ECDSA).
* See https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures,
* and note that "component" tests only exist for ECDSA sign.
*
* In order to avoid signing externally calculated digests, we naively would
* need access to the full message to be signed at the time of signing. That's
* a problem for TLS1.2, where the client cert verify message requires signing
* every handshake message sent or received before the client cert verify message.
* To avoid storing every single handshake message in its entirety, we instead
* keep a running hash of the messages in an EVP hash state. Then, instead of
* digesting that hash state, we pass it unmodified to EVP_DigestSignFinal.
* That would normally not be allowed, since the hash state was initialized without
* a key using EVP_DigestInit instead of with a key using EVP_DigestSignInit.
* We make it work by using the EVP_MD_CTX_set_pkey_ctx method to attach a key
* to an existing hash state.
*
* All that means that "digest-and-sign" requires two things:
* - An EVP hash state to sign. So we MUST use EVP hashing instead of the legacy
* low-level hash implementation for all hash algs.
* - EVP_MD_CTX_set_pkey_ctx to exist and to behave as expected. Existence
* alone is not sufficient: the method exists in openssl-3.0-fips, but
* it cannot be use to setup a hash state for EVP_DigestSignFinal.
*
* Currently only awslc-fips meets both these requirements. New libcryptos
* should be assumed not to meet these requirements until proven otherwise.
*/
int s2n_evp_digest_and_sign(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(pctx);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);

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;
POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL));

return S2N_SUCCESS;
}

/* "digest-then-sign" means that we calculate the digest for a hash state,
* then sign the digest bytes. That is not allowed by FIPS 140-3, but is allowed
* in all other cases.
*
* This version of EVP signing works with either EVP hash states or legacy
* low-level hash states.
*/
int s2n_evp_digest_then_sign(EVP_PKEY_CTX *pctx,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(pctx);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);

uint8_t digest_length = 0;
POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length));
POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN);

uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 };
POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length));

size_t signature_size = signature->size;
POSIX_GUARD_OSSL(EVP_PKEY_sign(pctx, signature->data, &signature_size,
digest_out, digest_length),
S2N_ERR_SIGN);
POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH);
signature->size = signature_size;

return S2N_SUCCESS;
}

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);

DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(priv->pkey, NULL), EVP_PKEY_CTX_free_pointer);
DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(priv->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer);
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);
Expand All @@ -107,15 +185,51 @@ int s2n_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg,
POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx));
}

if (s2n_libcrypto_is_awslc_fips()) {
POSIX_GUARD(s2n_evp_digest_and_sign(pctx, sig_alg, hash_state, signature));
} else {
POSIX_GUARD(s2n_evp_digest_then_sign(pctx, hash_state, signature));
}

return S2N_SUCCESS;
}

/* See s2n_evp_digest_and_sign for more information */
int s2n_evp_digest_and_verify(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(pctx);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);

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;
POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE);
POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL));

return S2N_SUCCESS;
}

/* See s2n_evp_digest_then_sign for more information */
int s2n_evp_digest_then_verify(EVP_PKEY_CTX *pctx,
struct s2n_hash_state *hash_state, struct s2n_blob *signature)
{
POSIX_ENSURE_REF(pctx);
POSIX_ENSURE_REF(hash_state);
POSIX_ENSURE_REF(signature);

uint8_t digest_length = 0;
POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length));
POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN);

uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 };
POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length));

POSIX_GUARD_OSSL(EVP_PKEY_verify(pctx, signature->data, signature->size,
digest_out, digest_length),
S2N_ERR_VERIFY_SIGNATURE);
return S2N_SUCCESS;
}

Expand All @@ -125,10 +239,9 @@ int s2n_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg,
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_sig_alg(pub, sig_alg));

DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pub->pkey, NULL), EVP_PKEY_CTX_free_pointer);
DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(pub->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer);
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);
Expand All @@ -138,11 +251,11 @@ int s2n_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg,
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));
if (s2n_libcrypto_is_awslc_fips()) {
POSIX_GUARD(s2n_evp_digest_and_verify(pctx, sig_alg, hash_state, signature));
} else {
POSIX_GUARD(s2n_evp_digest_then_verify(pctx, hash_state, signature));
}

POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE);
POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL));
return S2N_SUCCESS;
}
1 change: 0 additions & 1 deletion crypto/s2n_evp_signing.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#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);
Expand Down
4 changes: 2 additions & 2 deletions crypto/s2n_rsa_pss.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ static int s2n_rsa_pss_validate_sign_verify_match(const struct s2n_pkey *pub, co

/* Sign and Verify the Hash of the Random Blob */
s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE);
POSIX_GUARD(s2n_rsa_pss_key_sign(priv, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data));
POSIX_GUARD(s2n_rsa_pss_key_verify(pub, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data));
POSIX_GUARD(s2n_pkey_sign(priv, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data));
POSIX_GUARD(s2n_pkey_verify(pub, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data));
Comment on lines +109 to +110
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this, s2n_evp_signing_test can't load the RSA-PSS certificate it needs for testing.


return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions tests/features/S2N_LIBCRYPTO_SUPPORTS_PROVIDERS.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ int main()
EVP_MD *md = EVP_MD_fetch(NULL, NULL, NULL);
EVP_MD_free(md);

/* Supports property queries for pkey context implicit fetching */
EVP_PKEY_CTX *pkey_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, NULL, NULL);

return 0;
}
Loading
Loading