Skip to content

Commit

Permalink
Add fips checks for ecdsa signatures
Browse files Browse the repository at this point in the history
Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from #12745)
  • Loading branch information
slontis authored and mattcaswell committed Sep 18, 2020
1 parent e43b448 commit 0645110
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 173 deletions.
1 change: 1 addition & 0 deletions providers/common/include/prov/provider_util.h
Expand Up @@ -132,3 +132,4 @@ void ossl_prov_cache_exported_algorithms(const OSSL_ALGORITHM_CAPABLE *in,
int ossl_prov_digest_md_to_nid(const EVP_MD *md, const OSSL_ITEM *it,
size_t it_len);
int ossl_prov_digest_get_approved_nid(const EVP_MD *md, int sha1_allowed);
int ossl_prov_ec_check(const EC_KEY *ec, int protect);
63 changes: 63 additions & 0 deletions providers/common/provider_util.c
Expand Up @@ -353,3 +353,66 @@ int ossl_prov_digest_get_approved_nid(const EVP_MD *md, int sha1_allowed)
#endif
return mdnid;
}

/*
* In FIPS mode:
* protect should be 1 for any operations that need 112 bits of security
* strength (such as signing, and key exchange), or 0 for operations that allow
* a lower security strength (such as verify).
*
* For ECDH key agreement refer to SP800-56A
* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf
* "Appendix D"
*
* For ECDSA signatures refer to
* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
* "Table 2"
*/
int ossl_prov_ec_check(const EC_KEY *ec, int protect)
{
#ifdef FIPS_MODULE
int nid, strength;
const char *curve_name;
const EC_GROUP *group = EC_KEY_get0_group(ec);

if (group == NULL) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_CURVE, "No group");
return 0;
}
nid = EC_GROUP_get_curve_name(group);
if (nid == NID_undef) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_CURVE,
"Explicit curves are not allowed in fips mode");
return 0;
}

curve_name = EC_curve_nid2nist(nid);
if (curve_name == NULL) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_CURVE,
"Curve %s is not approved in FIPS mode", curve_name);
return 0;
}

/*
* For EC the security strength is the (order_bits / 2)
* e.g. P-224 is 112 bits.
*/
strength = EC_GROUP_order_bits(group) / 2;
/* The min security strength allowed for legacy verification is 80 bits */
if (strength < 80) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
return 0;
}

/*
* For signing/or key agreement only allow curves with at least 112 bits of
* security strength
*/
if (protect && strength < 112) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_CURVE,
"Curve %s cannot be used for signing", curve_name);
return 0;
}
#endif
return 1;
}
89 changes: 40 additions & 49 deletions providers/implementations/signature/ecdsa.c
Expand Up @@ -28,18 +28,19 @@
#include "prov/providercommonerr.h"
#include "prov/implementations.h"
#include "prov/provider_ctx.h"
#include "prov/provider_util.h"
#include "crypto/ec.h"
#include "prov/der_ec.h"

static OSSL_FUNC_signature_newctx_fn ecdsa_newctx;
static OSSL_FUNC_signature_sign_init_fn ecdsa_signature_init;
static OSSL_FUNC_signature_verify_init_fn ecdsa_signature_init;
static OSSL_FUNC_signature_sign_init_fn ecdsa_sign_init;
static OSSL_FUNC_signature_verify_init_fn ecdsa_verify_init;
static OSSL_FUNC_signature_sign_fn ecdsa_sign;
static OSSL_FUNC_signature_verify_fn ecdsa_verify;
static OSSL_FUNC_signature_digest_sign_init_fn ecdsa_digest_signverify_init;
static OSSL_FUNC_signature_digest_sign_init_fn ecdsa_digest_sign_init;
static OSSL_FUNC_signature_digest_sign_update_fn ecdsa_digest_signverify_update;
static OSSL_FUNC_signature_digest_sign_final_fn ecdsa_digest_sign_final;
static OSSL_FUNC_signature_digest_verify_init_fn ecdsa_digest_signverify_init;
static OSSL_FUNC_signature_digest_verify_init_fn ecdsa_digest_verify_init;
static OSSL_FUNC_signature_digest_verify_update_fn ecdsa_digest_signverify_update;
static OSSL_FUNC_signature_digest_verify_final_fn ecdsa_digest_verify_final;
static OSSL_FUNC_signature_freectx_fn ecdsa_freectx;
Expand Down Expand Up @@ -70,6 +71,7 @@ typedef struct {
unsigned char *aid;
size_t aid_len;
size_t mdsize;
int operation;

EVP_MD *md;
EVP_MD_CTX *mdctx;
Expand Down Expand Up @@ -114,7 +116,7 @@ static void *ecdsa_newctx(void *provctx, const char *propq)
return ctx;
}

static int ecdsa_signature_init(void *vctx, void *ec)
static int ecdsa_signverify_init(void *vctx, void *ec, int operation)
{
PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;

Expand All @@ -125,7 +127,18 @@ static int ecdsa_signature_init(void *vctx, void *ec)
return 0;
EC_KEY_free(ctx->ec);
ctx->ec = ec;
return 1;
ctx->operation = operation;
return ossl_prov_ec_check(ec, operation == EVP_PKEY_OP_SIGN);
}

static int ecdsa_sign_init(void *vctx, void *ec)
{
return ecdsa_signverify_init(vctx, ec, EVP_PKEY_OP_SIGN);
}

static int ecdsa_verify_init(void *vctx, void *ec)
{
return ecdsa_signverify_init(vctx, ec, EVP_PKEY_OP_VERIFY);
}

static int ecdsa_sign(void *vctx, unsigned char *sig, size_t *siglen,
Expand Down Expand Up @@ -174,44 +187,11 @@ static int ecdsa_verify(void *vctx, const unsigned char *sig, size_t siglen,
return ECDSA_verify(0, tbs, tbslen, sig, siglen, ctx->ec);
}

static int get_md_nid(const EVP_MD *md)
static int get_md_nid(const PROV_ECDSA_CTX *ctx, const EVP_MD *md)
{
/*
* Because the ECDSA library deals with NIDs, we need to translate.
* We do so using EVP_MD_is_a(), and therefore need a name to NID
* map.
*/
static const OSSL_ITEM name_to_nid[] = {
{ NID_sha1, OSSL_DIGEST_NAME_SHA1 },
{ NID_sha224, OSSL_DIGEST_NAME_SHA2_224 },
{ NID_sha256, OSSL_DIGEST_NAME_SHA2_256 },
{ NID_sha384, OSSL_DIGEST_NAME_SHA2_384 },
{ NID_sha512, OSSL_DIGEST_NAME_SHA2_512 },
{ NID_sha3_224, OSSL_DIGEST_NAME_SHA3_224 },
{ NID_sha3_256, OSSL_DIGEST_NAME_SHA3_256 },
{ NID_sha3_384, OSSL_DIGEST_NAME_SHA3_384 },
{ NID_sha3_512, OSSL_DIGEST_NAME_SHA3_512 },
/* TODO - Add SHAKE OIDS when they are standardized */

};
size_t i;
int mdnid = NID_undef;

if (md == NULL)
goto end;

for (i = 0; i < OSSL_NELEM(name_to_nid); i++) {
if (EVP_MD_is_a(md, name_to_nid[i].ptr)) {
mdnid = (int)name_to_nid[i].id;
break;
}
}

if (mdnid == NID_undef)
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN);

end:
return mdnid;
return ossl_prov_digest_get_approved_nid(md, sha1_allowed);
}

static void free_md(PROV_ECDSA_CTX *ctx)
Expand All @@ -226,7 +206,7 @@ static void free_md(PROV_ECDSA_CTX *ctx)
}

static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,
void *ec)
void *ec, int operation)
{
PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
int md_nid = NID_undef;
Expand All @@ -237,11 +217,12 @@ static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,

free_md(ctx);

if (!ecdsa_signature_init(vctx, ec))
if (!ecdsa_signverify_init(vctx, ec, operation))
return 0;

ctx->md = EVP_MD_fetch(ctx->libctx, mdname, ctx->propq);
if ((md_nid = get_md_nid(ctx->md)) == NID_undef)
md_nid = get_md_nid(ctx, ctx->md);
if (md_nid == NID_undef)
goto error;

ctx->mdsize = EVP_MD_size(ctx->md);
Expand Down Expand Up @@ -273,6 +254,16 @@ static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,
return 0;
}

static int ecdsa_digest_sign_init(void *vctx, const char *mdname, void *ec)
{
return ecdsa_digest_signverify_init(vctx, mdname, ec, EVP_PKEY_OP_SIGN);
}

static int ecdsa_digest_verify_init(void *vctx, const char *mdname, void *ec)
{
return ecdsa_digest_signverify_init(vctx, mdname, ec, EVP_PKEY_OP_VERIFY);
}

int ecdsa_digest_signverify_update(void *vctx, const unsigned char *data,
size_t datalen)
{
Expand Down Expand Up @@ -521,18 +512,18 @@ static const OSSL_PARAM *ecdsa_settable_ctx_md_params(void *vctx)

const OSSL_DISPATCH ecdsa_signature_functions[] = {
{ OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ecdsa_newctx },
{ OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))ecdsa_signature_init },
{ OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))ecdsa_sign_init },
{ OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ecdsa_sign },
{ OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))ecdsa_signature_init },
{ OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))ecdsa_verify_init },
{ OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ecdsa_verify },
{ OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
(void (*)(void))ecdsa_digest_signverify_init },
(void (*)(void))ecdsa_digest_sign_init },
{ OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
(void (*)(void))ecdsa_digest_signverify_update },
{ OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
(void (*)(void))ecdsa_digest_sign_final },
{ OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
(void (*)(void))ecdsa_digest_signverify_init },
(void (*)(void))ecdsa_digest_verify_init },
{ OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
(void (*)(void))ecdsa_digest_signverify_update },
{ OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
Expand Down
7 changes: 5 additions & 2 deletions test/evp_test.c
Expand Up @@ -1627,8 +1627,11 @@ static int pderive_test_parse(EVP_TEST *t,
EVP_PKEY *peer;
if (find_key(&peer, value, public_keys) == 0)
return -1;
if (EVP_PKEY_derive_set_peer(kdata->ctx, peer) <= 0)
return -1;
if (EVP_PKEY_derive_set_peer(kdata->ctx, peer) <= 0) {
t->err = "DERIVE_SET_PEER_ERROR";
return 1;
}
t->err = NULL;
return 1;
}
if (strcmp(keyword, "SharedSecret") == 0)
Expand Down
83 changes: 82 additions & 1 deletion test/recipes/30-test_evp_data/evppkey_ecdsa.txt
Expand Up @@ -90,7 +90,6 @@ Ctrl = digest:SHA1
Input = "0123456789ABCDEF1234"
Output = 3045022100b1d1cb1a577035bccdd5a86c6148c2cc7c633cd42b7234139b593076d041e15202201898cdd52b41ca502098184b409cf83a21bc945006746e3b7cea52234e043ec8


Title = DigestSign and DigestVerify

DigestVerify = SHA256
Expand All @@ -108,3 +107,85 @@ OneShotDigestVerify = SHA256
Key = P-256-PUBLIC
Input = "Hello World"
Output = 3046022100e7515177ec3817b77a4a94066ab3070817b7aa9d44a8a09f040da250116e8972022100ba59b0f631258e59a9026be5d84f60685f4cf22b9165a0c2736d5c21c8ec1862

PrivateKey = P-256_NAMED_CURVE_EXPLICIT
-----BEGIN PRIVATE KEY-----
MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB
AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA
///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV
AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg
9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A
AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgiUTxtr5vLVjj
0BOXUa/4r82DJ30QoupYS/wlilW4gWehRANCAATM0n3q2UaDyaQ7OxzJM3B6prhW
3ev1gTwRBduzqqlwd54AUSgI+pjttW8zrWNitO8H1sf59MPWOESKxNtZ1+Nl
-----END PRIVATE KEY-----

PrivateKey = EC_EXPLICIT
-----BEGIN PRIVATE KEY-----
MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB
AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA
///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV
AMSdNgiG5wSTamZ44ROdJreBn36QBEEE5JcIvn36opqjEm/k59Al40rBAxWM2TPG
l0L13Je51zHpfXQ9Z2o7IQicMXP4wSfJ0qCgg2bgydqoxlYrlLGuVQIhAP////8A
AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgec92jwduadCk
OjoNRI+YT5Be5TkzZXzYCyTLkMOikDmhRANCAATtECEhQbLEaiUj/Wu0qjcr81lL
46dx5zYgArz/iaSNJ3W80oO+F7v04jlQ7wxQzg96R0bwKiMeq5CcW9ZFt6xg
-----END PRIVATE KEY-----

PrivateKey = B-163
-----BEGIN PRIVATE KEY-----
MGMCAQAwEAYHKoZIzj0CAQYFK4EEAA8ETDBKAgEBBBUDnQW0mLiHVha/jqFznX/K
DnVlDgChLgMsAAQB1qZ00fPIct+QN8skv1XIHtBNp3EGLytJV0tsAUTYtGhtrzRj
e3GzYyg=
-----END PRIVATE KEY-----

PrivateKey = secp256k1
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgsLpFV9joHc0bisyV53XL
mrG6/Gu6ZaHoXtKP/VFX44ehRANCAARLYWGgp5nP4N8guypLSbYGCVN6ZPCnWW4x
srYkcpdbxr4neRT3zC62keCKgPbJf5SIHkJ2Tcaw6hVSrBOUFtix
-----END PRIVATE KEY-----

Title = FIPS tests

# Test that a nist curve with < 112 bits is allowed in fips mode for verifying
DigestVerify = SHA256
Key = B-163
Input = "Hello World"
Output = 302e0215027bb891747468b4b59ca2a2bf8f42d29d08866cf5021502cc311b25e9a2168e42240b07a6071070f687eb3b

# Test that a nist curve with SHA3 is allowed in fips mode
# The sign will get a mismatch error since the output signature changes on each run
DigestSign = SHA3-512
Key = P-256
Input = "Hello World"
Result = SIGNATURE_MISMATCH

# Test that a explicit curve that is a named curve is allowed in fips mode
DigestVerify = SHA256
Key = P-256_NAMED_CURVE_EXPLICIT
Input = "Hello World"
Output = 30450220796fcf472882ed5779226dcd0217b9d2b9acfe4fa2fb0109c8ee63c63adc1033022100e306c69f7e31b9a5d54eb12ba813cddf4de4af933e4f6cea38a0817d9d831d91

Title = FIPS Negative tests (using different curves and digests)

# Test that a explicit curve is not allowed in fips mode
Availablein = fips
DigestVerify = SHA256
Key = EC_EXPLICIT
Input = "Hello World"
Result = DIGESTVERIFYINIT_ERROR

# Test that a curve with < 112 bits is not allowed in fips mode for signing
Availablein = fips
DigestSign = SHA3-512
Key = B-163
Input = "Hello World"
Result = DIGESTSIGNINIT_ERROR

# Test that a non nist curve is not allowed in fips mode
Availablein = fips
DigestSign = SHA3-512
Key = secp256k1
Input = "Hello World"
Result = DIGESTSIGNINIT_ERROR

0 comments on commit 0645110

Please sign in to comment.