From 285db1ef298bb7d78ccf16cbb644a0e6b79584e6 Mon Sep 17 00:00:00 2001 From: Doug Engert Date: Mon, 21 Dec 2020 12:02:28 -0600 Subject: [PATCH] ECDSA Signatures with hashes This PR is based on discussion with @popovec in https://github.com/OpenSC/OpenSC/issues/2181 and https://github.com/OpenSC/OpenSC/pull/2187 which was cherry-picked as 5e5300816c8 This has been tested with PIV, MyEID and Smartcard-HSM. with ECDSA keys. The main fixes include : - Setting "flags" in card drivers - added code to sc_pkcs15-compute-signature for handle ECDSA with hashes - code in framework-pkcs15.c Signatures made by pkcs11-tool -sigm verify with openssl but pkcs11-tool --verify does not work with ECDSA but does with RSA I suspect it has to do with: and some then creating the wrong PKCS11 mechanisms It should work with the epass2003 which does hashes in the driver. --- src/libopensc/card-myeid.c | 4 +- src/libopensc/pkcs15-sec.c | 95 ++++++++++++++++++++--------------- src/pkcs11/framework-pkcs15.c | 83 ++++++++++++++++++++++++++---- src/pkcs11/openssl.c | 8 ++- 4 files changed, 137 insertions(+), 53 deletions(-) diff --git a/src/libopensc/card-myeid.c b/src/libopensc/card-myeid.c index cdec99cc..a9536196 100644 --- a/src/libopensc/card-myeid.c +++ b/src/libopensc/card-myeid.c @@ -245,9 +245,7 @@ static int myeid_init(struct sc_card *card) int i; flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN; - flags |= SC_ALGORITHM_ECDSA_HASH_NONE | SC_ALGORITHM_ECDSA_HASH_SHA1; - flags |= SC_ALGORITHM_ECDSA_HASH_SHA224 | SC_ALGORITHM_ECDSA_HASH_SHA256; - flags |= SC_ALGORITHM_ECDSA_HASH_SHA384 | SC_ALGORITHM_ECDSA_HASH_SHA512; + flags |= SC_ALGORITHM_ECDSA_HASH_NONE; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; for (i=0; ec_curves[i].curve_name != NULL; i++) { diff --git a/src/libopensc/pkcs15-sec.c b/src/libopensc/pkcs15-sec.c index 397e9296..c00d3e31 100644 --- a/src/libopensc/pkcs15-sec.c +++ b/src/libopensc/pkcs15-sec.c @@ -608,56 +608,69 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, /* if the card has SC_ALGORITHM_NEED_USAGE set, and the * key is for signing and decryption, we need to emulate signing */ - /* TODO: -DEE assume only RSA keys will ever use _NEED_USAGE */ sc_log(ctx, "supported algorithm flags 0x%X, private key usage 0x%X", alg_info->flags, prkey->usage); - if ((alg_info->flags & SC_ALGORITHM_NEED_USAGE) && - ((prkey->usage & USAGE_ANY_SIGN) && - (prkey->usage & USAGE_ANY_DECIPHER)) ) { - size_t tmplen = sizeof(buf); - if (flags & SC_ALGORITHM_RSA_RAW) { - r = sc_pkcs15_decipher(p15card, obj, flags, in, inlen, out, outlen); + if (obj->type == SC_ALGORITHM_RSA) { + if ((alg_info->flags & SC_ALGORITHM_NEED_USAGE) && + ((prkey->usage & USAGE_ANY_SIGN) && + (prkey->usage & USAGE_ANY_DECIPHER)) ) { + size_t tmplen = sizeof(buf); + if (flags & SC_ALGORITHM_RSA_RAW) { + r = sc_pkcs15_decipher(p15card, obj, flags, in, inlen, out, outlen); + LOG_FUNC_RETURN(ctx, r); + } + if (modlen > tmplen) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!"); + + /* XXX Assuming RSA key here */ + r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, prkey->modulus_length); + + /* no padding needed - already done */ + flags &= ~SC_ALGORITHM_RSA_PADS; + /* instead use raw rsa */ + flags |= SC_ALGORITHM_RSA_RAW; + + LOG_TEST_RET(ctx, r, "Unable to add padding"); + + r = sc_pkcs15_decipher(p15card, obj, flags, buf, modlen, out, outlen); LOG_FUNC_RETURN(ctx, r); } - if (modlen > tmplen) - LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "Buffer too small, needs recompile!"); - /* XXX Assuming RSA key here */ - r = sc_pkcs1_encode(ctx, flags, in, inlen, buf, &tmplen, prkey->modulus_length); - /* no padding needed - already done */ - flags &= ~SC_ALGORITHM_RSA_PADS; - /* instead use raw rsa */ - flags |= SC_ALGORITHM_RSA_RAW; + /* If the card doesn't support the requested algorithm, we normally add the + * padding here in software and ask the card to do a raw signature. There's + * one exception to that, where we might be able to get the signature to + * succeed by stripping padding if the card only offers higher-level + * signature operations. The only thing we can strip is the DigestInfo + * block from PKCS1 padding. */ + if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) && + !(alg_info->flags & SC_ALGORITHM_RSA_RAW) && + !(alg_info->flags & SC_ALGORITHM_RSA_HASH_NONE) && + (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1)) { + unsigned int algo; + size_t tmplen = sizeof(buf); - LOG_TEST_RET(ctx, r, "Unable to add padding"); - - r = sc_pkcs15_decipher(p15card, obj, flags, buf, modlen, out, outlen); - LOG_FUNC_RETURN(ctx, r); + r = sc_pkcs1_strip_digest_info_prefix(&algo, tmp, inlen, tmp, &tmplen); + if (r != SC_SUCCESS || algo == SC_ALGORITHM_RSA_HASH_NONE) { + sc_mem_clear(buf, sizeof(buf)); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + } + flags &= ~SC_ALGORITHM_RSA_HASH_NONE; + flags |= algo; + inlen = tmplen; + } } - /* If the card doesn't support the requested algorithm, we normally add the - * padding here in software and ask the card to do a raw signature. There's - * one exception to that, where we might be able to get the signature to - * succeed by stripping padding if the card only offers higher-level - * signature operations. The only thing we can strip is the DigestInfo - * block from PKCS1 padding. */ - if ((flags == (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) && - !(alg_info->flags & SC_ALGORITHM_RSA_RAW) && - !(alg_info->flags & SC_ALGORITHM_RSA_HASH_NONE) && - (alg_info->flags & SC_ALGORITHM_RSA_PAD_PKCS1)) { - unsigned int algo; - size_t tmplen = sizeof(buf); - - r = sc_pkcs1_strip_digest_info_prefix(&algo, tmp, inlen, tmp, &tmplen); - if (r != SC_SUCCESS || algo == SC_ALGORITHM_RSA_HASH_NONE) { - sc_mem_clear(buf, sizeof(buf)); - LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + /* ECDSA sofware hash has already been done, or is not needed, or card will do hash */ + /* if card can not do the hash, will use SC_ALGORITHM_ECDSA_RAW */ + if (obj->type == SC_PKCS15_TYPE_PRKEY_EC) { + if ((alg_info->flags & SC_ALGORITHM_ECDSA_RAW) + && !(flags & SC_ALGORITHM_ECDSA_HASHES & alg_info->flags)) { + sc_log(ctx, "ECDSA using SC_ALGORITHM_ECDSA_RAW flags before 0x%8.8lx", flags); + flags |= SC_ALGORITHM_ECDSA_RAW; + flags &= ~SC_ALGORITHM_ECDSA_HASHES; } - flags &= ~SC_ALGORITHM_RSA_HASH_NONE; - flags |= algo; - inlen = tmplen; } r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); @@ -665,6 +678,7 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, sc_mem_clear(buf, sizeof(buf)); LOG_FUNC_RETURN(ctx, r); } + /* senv now has flags card or driver will do */ senv.algorithm_flags = sec_flags; sc_log(ctx, "DEE flags:0x%8.8lx alg_info->flags:0x%8.8x pad:0x%8.8lx sec:0x%8.8lx", @@ -695,9 +709,10 @@ int sc_pkcs15_compute_signature(struct sc_pkcs15_card *p15card, * If the length of the hash value is larger than the bit length of n, only * the leftmost bits of the hash up to the length of n will be used. Any * truncation is done by the token. + * But if card is going to do the hash, pass in all the data */ else if (senv.algorithm == SC_ALGORITHM_EC && - (flags & SC_ALGORITHM_ECDSA_HASHES)) { + (senv.flags & SC_ALGORITHM_ECDSA_HASHES) == 0) { inlen = MIN(inlen, (prkey->field_length+7)/8); } diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index 7fdc888d..5da82260 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -5649,11 +5649,12 @@ static CK_RV register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, if (ext_flags & SC_ALGORITHM_EXT_EC_COMPRESS) ec_flags |= CKF_EC_COMPRESS; - mech_info.flags = CKF_HW | CKF_SIGN; /* check for more */ + mech_info.flags = CKF_HW | CKF_SIGN | CKF_VERIFY; mech_info.flags |= ec_flags; mech_info.ulMinKeySize = min_key_size; mech_info.ulMaxKeySize = max_key_size; + /* add mechs card or driver support */ if (flags & SC_ALGORITHM_ECDSA_RAW) { mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA, &mech_info, CKK_EC, NULL, NULL); if (!mt) @@ -5661,38 +5662,89 @@ static CK_RV register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, rc = sc_pkcs11_register_mechanism(p11card, mt); if (rc != CKR_OK) return rc; - + } + #ifdef ENABLE_OPENSSL - /* Hashing is always done in openssl, if the card driver requests hashes, we enable them here. */ + /* if card supports RAW add sign_and_hash using RAW for mechs card does not support */ - if (flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + if (flags & SC_ALGORITHM_ECDSA_RAW) { + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA1)) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_ECDSA_SHA1, CKM_SHA_1, mt); if (rc != CKR_OK) return rc; } - if (flags & SC_ALGORITHM_ECDSA_HASH_SHA224) { + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA224)) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_ECDSA_SHA224, CKM_SHA224, mt); if (rc != CKR_OK) return rc; } - if (flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA256)) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_ECDSA_SHA256, CKM_SHA256, mt); if (rc != CKR_OK) return rc; } - if (flags & SC_ALGORITHM_ECDSA_HASH_SHA384) { + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA384)) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_ECDSA_SHA384, CKM_SHA384, mt); if (rc != CKR_OK) return rc; } - if (flags & SC_ALGORITHM_ECDSA_HASH_SHA512) { + + if (!(flags & SC_ALGORITHM_ECDSA_HASH_SHA512)) { rc = sc_pkcs11_register_sign_and_hash_mechanism(p11card, CKM_ECDSA_SHA512, CKM_SHA512, mt); if (rc != CKR_OK) return rc; } + } #endif + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA1, &mech_info, CKK_EC, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt); + if (rc != CKR_OK) + return rc; } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA224) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA224, &mech_info, CKK_EC, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA256, &mech_info, CKK_EC, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA384) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA384, &mech_info, CKK_EC, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt); + if (rc != CKR_OK) + return rc; + } + + if (flags & SC_ALGORITHM_ECDSA_HASH_SHA512) { + mt = sc_pkcs11_new_fw_mechanism(CKM_ECDSA_SHA512, &mech_info, CKK_EC, NULL, NULL); + if (!mt) + return CKR_HOST_MEMORY; + rc = sc_pkcs11_register_mechanism(p11card, mt); + if (rc != CKR_OK) + return rc; + } + /* ADD ECDH mechanisms */ /* The PIV uses curves where CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE produce the same results */ if(flags & SC_ALGORITHM_ECDH_CDH_RAW) { @@ -5725,6 +5777,11 @@ static CK_RV register_ec_mechanisms(struct sc_pkcs11_card *p11card, int flags, return rc; } +#ifdef ENABLE_OPNSSL + /* if card does not support a HASH but supports RAW add split mech */ + +#endif + return CKR_OK; } @@ -5786,6 +5843,7 @@ register_mechanisms(struct sc_pkcs11_card *p11card) sc_pkcs11_mechanism_type_t *mt; unsigned int num; int rsa_flags = 0, ec_flags = 0, gostr_flags = 0, aes_flags = 0; + int ec_found; CK_RV rc; /* Register generic mechanisms */ @@ -5805,6 +5863,7 @@ register_mechanisms(struct sc_pkcs11_card *p11card) mech_info.ulMaxKeySize = 0; ec_min_key_size = ~0; ec_max_key_size = 0; + ec_found = 0; aes_min_key_size = ~0; aes_max_key_size = 0; @@ -5831,6 +5890,7 @@ register_mechanisms(struct sc_pkcs11_card *p11card) ec_max_key_size = alg_info->key_length; ec_flags |= alg_info->flags; ec_ext_flags |= alg_info->u._ec.ext_flags; + ec_found = 1; break; case SC_ALGORITHM_GOSTR3410: gostr_flags |= alg_info->flags; @@ -5846,7 +5906,12 @@ register_mechanisms(struct sc_pkcs11_card *p11card) alg_info++; } - if (ec_flags & SC_ALGORITHM_ECDSA_RAW) { + /* + * TODO this looked like a bug: + * if (ec_flags & SC_ALGORITHM_ECDSA_RAW) + * Card driver driver should not have to specify SC_ALGORITHM_ECDSA_RAW + */ + if (ec_found) { rc = register_ec_mechanisms(p11card, ec_flags, ec_ext_flags, ec_min_key_size, ec_max_key_size); if (rc != CKR_OK) return rc; diff --git a/src/pkcs11/openssl.c b/src/pkcs11/openssl.c index bebf3699..e7d1fb4d 100644 --- a/src/pkcs11/openssl.c +++ b/src/pkcs11/openssl.c @@ -475,7 +475,13 @@ CK_RV sc_pkcs11_verify_data(const unsigned char *pubkey, unsigned int pubkey_len || mech->mechanism == CKM_SHA224_RSA_PKCS || mech->mechanism == CKM_SHA256_RSA_PKCS || mech->mechanism == CKM_SHA384_RSA_PKCS - || mech->mechanism == CKM_SHA512_RSA_PKCS)) { + || mech->mechanism == CKM_SHA512_RSA_PKCS + || mech->mechanism == CKM_ECDSA_SHA1 + || mech->mechanism == CKM_ECDSA_SHA224 + || mech->mechanism == CKM_ECDSA_SHA256 + || mech->mechanism == CKM_ECDSA_SHA384 + || mech->mechanism == CKM_ECDSA_SHA512 + )) { EVP_MD_CTX *md_ctx = DIGEST_CTX(md); /* This does not really use the data argument, but the data