diff --git a/.reuse/dep5 b/.reuse/dep5 index 305d6ded..8fc6cc89 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -30,6 +30,7 @@ Files: .github/* tests/*.pem tests/cert.json.in tests/cert.json.rsa.in + tests/cert.json.rsapss.in tests/cert.json.ecdsa.in tests/cert.json.ed25519.in tests/cert.json.ed448.in diff --git a/src/encoder.c b/src/encoder.c index f17dda6b..05d6e675 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -251,12 +251,212 @@ static int p11prov_rsa_pubkey_to_der(P11PROV_OBJ *key, unsigned char **der, return RET_OSSL_OK; } +/* allocate and set algorithm ID from given ptype */ +/* taken from OpenSSL */ +static X509_ALGOR *ossl_X509_ALGOR_from_nid(int nid, int ptype, void *pval) +{ + ASN1_OBJECT *algo = OBJ_nid2obj(nid); + X509_ALGOR *alg = NULL; + + if (algo == NULL) { + return NULL; + } + if ((alg = X509_ALGOR_new()) == NULL) { + goto err; + } + if (X509_ALGOR_set0(alg, algo, ptype, pval)) { + return alg; + } + alg->algorithm = NULL; /* precaution to prevent double free */ + +err: + X509_ALGOR_free(alg); + /* ASN1_OBJECT_free(algo) is not needed due to OBJ_nid2obj() */ + return NULL; +} + +/* allocate and set algorithm ID from EVP_MD, default SHA1 */ +/* taken from OpenSSL */ +static int ossl_x509_algor_new_from_md(X509_ALGOR **palg, const EVP_MD *md) +{ + X509_ALGOR *alg; + + /* Default is SHA1 so no need to create it - still success */ + if (md == NULL || EVP_MD_is_a(md, "SHA1")) { + return 1; + } + if ((alg = X509_ALGOR_new()) == NULL) { + return 0; + } + X509_ALGOR_set_md(alg, md); + *palg = alg; + return 1; +} + +/* taken from OpenSSL */ +static int ossl_x509_algor_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md) +{ + X509_ALGOR *algtmp = NULL; + ASN1_STRING *stmp = NULL; + + *palg = NULL; + if (mgf1md == NULL || EVP_MD_is_a(mgf1md, "SHA1")) { + return 1; + } + /* need to embed algorithm ID inside another */ + if (!ossl_x509_algor_new_from_md(&algtmp, mgf1md)) { + goto err; + } + if (ASN1_item_pack(algtmp, ASN1_ITEM_rptr(X509_ALGOR), &stmp) == NULL) { + goto err; + } + *palg = ossl_X509_ALGOR_from_nid(NID_mgf1, V_ASN1_SEQUENCE, stmp); + if (*palg == NULL) { + goto err; + } + stmp = NULL; +err: + ASN1_STRING_free(stmp); + X509_ALGOR_free(algtmp); + return *palg != NULL; +} + +RSA_PSS_PARAMS *p11prov_encode_rsa_pss_params(const EVP_MD *md, + const EVP_MD *mgf1_md, + int saltlen) +{ + RSA_PSS_PARAMS *pss = NULL; + + pss = RSA_PSS_PARAMS_new(); + if (pss == NULL) { + return NULL; + } + + pss->saltLength = ASN1_INTEGER_new(); + if (pss->saltLength == NULL) { + goto err; + } + if (!ASN1_INTEGER_set(pss->saltLength, saltlen)) { + goto err; + } + + if (!ossl_x509_algor_new_from_md(&pss->hashAlgorithm, md)) { + goto err; + } + + if (!ossl_x509_algor_md_to_mgf1(&pss->maskGenAlgorithm, mgf1_md)) { + goto err; + } + + if (!ossl_x509_algor_new_from_md(&pss->maskHash, mgf1_md)) { + goto err; + } + /* the pss->trailerField has a default value 1 so it is optional and we do + * not need to include it in the generated ASN1 for public key restrictions + */ + return pss; + +err: + RSA_PSS_PARAMS_free(pss); + return NULL; +} + +/* RSA-PSS mechanism map, sorted by priority */ +struct rsa_pss_map { + CK_MECHANISM_TYPE pkcs11_mechanism; + const EVP_MD *(*hash_md)(void); +} rsa_pss_map[P11PROV_N_RSAPSS_MECHS] = { + { CKM_RSA_PKCS_PSS, EVP_sha512 }, + { CKM_SHA512_RSA_PKCS_PSS, EVP_sha512 }, + { CKM_SHA384_RSA_PKCS_PSS, EVP_sha384 }, + { CKM_SHA256_RSA_PKCS_PSS, EVP_sha256 }, + { CKM_SHA224_RSA_PKCS_PSS, EVP_sha224 }, + /* Technically OpenSSL will not encode the SHA3 these days so if we will get + * these on PKCS#11 level, we will just fall back to non-PSS Key encoding: + * https://tools.ietf.org/html/rfc8017#appendix-A.2.1 + */ + { CKM_SHA3_512_RSA_PKCS_PSS, EVP_sha3_512 }, + { CKM_SHA3_384_RSA_PKCS_PSS, EVP_sha3_384 }, + { CKM_SHA3_256_RSA_PKCS_PSS, EVP_sha3_256 }, + { CKM_SHA3_224_RSA_PKCS_PSS, EVP_sha3_224 }, + { CKM_SHA1_RSA_PKCS_PSS, EVP_sha1 }, +}; + +/* The PKCS#11 defines CKA_ALLOWED_MECHANISMS listing all allowed mechanisms on + * given key. + * OTOH X509 define parameters that either allow the key operation with any + * parameters or only with one particular parameter combination + * (including hash, mgf, mgf1 hash, salt length and trailer field). + * + * In case we have only one hash algorithm, such as CKM_SHA256_RSA_PKCS_PSS, we + * can derive hash and default parameters. + * + * If there are more or there is a generic CKM_RSA_PKCS_PSS or all the PSS + * mechanisms, we need to generate unrestricted RSA-PSS mechanism + * (V_ASN1_UNDEF). Note, that this is possible to do only on the key itself and + * not on the signature! + */ +static ASN1_STRING *p11prov_encode_rsa_pss(P11PROV_OBJ *obj) +{ + CK_ATTRIBUTE *am = p11prov_obj_get_attr(obj, CKA_ALLOWED_MECHANISMS); + CK_MECHANISM_TYPE *allowed; + int am_nmechs, i, saltlen, nfound, first = -1; + RSA_PSS_PARAMS *pss = NULL; + const EVP_MD *md; + ASN1_STRING *pstr = NULL; + + if (am == NULL || am->ulValueLen == 0) { + /* no limitations or no support for allowed mechs. Should not be + * reached. + * TODO we can try also certificate restrictions */ + return NULL; + } + allowed = (CK_MECHANISM_TYPE *)am->pValue; + am_nmechs = am->ulValueLen / sizeof(CK_MECHANISM_TYPE); + for (i = 0; i < P11PROV_N_RSAPSS_MECHS; i++) { + bool found = false; + for (int j = 0; j < am_nmechs; j++) { + if (rsa_pss_map[i].pkcs11_mechanism == allowed[j]) { + found = true; + break; + } + } + if (found) { + nfound++; + if (first == -1) { + first = i; + } + } + } + if (i == P11PROV_N_RSAPSS_MECHS) { + /* no RSA-PSS mechanism -- should not be reached */ + return NULL; + } + + if (nfound > 1 || rsa_pss_map[first].pkcs11_mechanism == CKM_RSA_PKCS_PSS) { + /* no restrictions -- we can not better express limitation on multiple + * mechanisms or this generic PSS mechanism */ + return NULL; + } + + md = rsa_pss_map[i].hash_md(); + saltlen = EVP_MD_get_size(md); + pss = p11prov_encode_rsa_pss_params(md, md, saltlen); + if (ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), &pstr) == NULL) { + RSA_PSS_PARAMS_free(pss); + return NULL; + } + RSA_PSS_PARAMS_free(pss); + return pstr; +} + static X509_PUBKEY *p11prov_rsa_pubkey_to_x509(P11PROV_OBJ *key) { X509_PUBKEY *pubkey; unsigned char *der = NULL; int derlen = 0; - int ret; + int ret, nid, ptype; + ASN1_STRING *pval = NULL; ret = p11prov_rsa_pubkey_to_der(key, &der, &derlen); if (ret != RET_OSSL_OK) { @@ -269,8 +469,23 @@ static X509_PUBKEY *p11prov_rsa_pubkey_to_x509(P11PROV_OBJ *key) return NULL; } - ret = X509_PUBKEY_set0_param(pubkey, OBJ_nid2obj(NID_rsaEncryption), - V_ASN1_NULL, NULL, der, derlen); + if (p11prov_obj_is_rsa_pss(key)) { + nid = NID_rsassaPss; + pval = p11prov_encode_rsa_pss(key); + if (pval != NULL) { + /* This is RSA-PSS key with restrictions */ + ptype = V_ASN1_SEQUENCE; + } else { + /* This is RSA-PSS key without additional restrictions */ + ptype = V_ASN1_UNDEF; + } + } else { + /* this is generic RSA key without restrictions */ + nid = NID_rsaEncryption; + ptype = V_ASN1_NULL; + } + ret = X509_PUBKEY_set0_param(pubkey, OBJ_nid2obj(nid), ptype, pval, der, + derlen); if (ret != RET_OSSL_OK) { OPENSSL_free(der); X509_PUBKEY_free(pubkey); diff --git a/src/encoder.h b/src/encoder.h index 1635923c..dd4a3327 100644 --- a/src/encoder.h +++ b/src/encoder.h @@ -42,4 +42,7 @@ extern const OSSL_DISPATCH p11prov_ec_edwards_encoder_priv_key_info_pem_functions[]; extern const OSSL_DISPATCH p11prov_ec_edwards_encoder_text_functions[]; +RSA_PSS_PARAMS *p11prov_encode_rsa_pss_params(const EVP_MD *md, + const EVP_MD *mgf1_md, + int saltlen); #endif /* _ENCODER_H */ diff --git a/src/keymgmt.c b/src/keymgmt.c index 68b451b4..08b378ff 100644 --- a/src/keymgmt.c +++ b/src/keymgmt.c @@ -131,6 +131,14 @@ struct key_generator { void *cb_arg; }; +const CK_MECHANISM_TYPE p11prov_rsapss_mechs[P11PROV_N_RSAPSS_MECHS] = { + CKM_SHA1_RSA_PKCS_PSS, CKM_SHA224_RSA_PKCS_PSS, + CKM_SHA256_RSA_PKCS_PSS, CKM_SHA384_RSA_PKCS_PSS, + CKM_SHA512_RSA_PKCS_PSS, CKM_SHA3_224_RSA_PKCS_PSS, + CKM_SHA3_256_RSA_PKCS_PSS, CKM_SHA3_384_RSA_PKCS_PSS, + CKM_SHA3_512_RSA_PKCS_PSS, CKM_RSA_PKCS_PSS +}; + static int p11prov_common_gen_set_params(void *genctx, const OSSL_PARAM params[]) { @@ -784,7 +792,7 @@ static int p11prov_rsa_export(void *keydata, int selection, P11PROV_CTX *ctx = p11prov_obj_get_prov_ctx(key); CK_OBJECT_CLASS class = p11prov_obj_get_class(key); - P11PROV_debug("rsa export %p", keydata); + P11PROV_debug("rsa export %p, selection= %d", keydata, selection); if (key == NULL) { return RET_OSSL_ERR; @@ -998,21 +1006,14 @@ DISPATCH_KEYMGMT_FN(rsa, gen_settable_params); static CK_RV set_default_rsapss_mechanisms(struct key_generator *ctx) { - CK_MECHANISM_TYPE rsapss_mechs[] = { - CKM_SHA1_RSA_PKCS_PSS, CKM_SHA224_RSA_PKCS_PSS, - CKM_SHA256_RSA_PKCS_PSS, CKM_SHA384_RSA_PKCS_PSS, - CKM_SHA512_RSA_PKCS_PSS, CKM_SHA3_224_RSA_PKCS_PSS, - CKM_SHA3_256_RSA_PKCS_PSS, CKM_SHA3_384_RSA_PKCS_PSS, - CKM_SHA3_512_RSA_PKCS_PSS, CKM_RSA_PKCS_PSS - }; - - ctx->data.rsa.allowed_types = OPENSSL_malloc(sizeof(rsapss_mechs)); + size_t mechs_size = sizeof(CK_MECHANISM_TYPE) * P11PROV_N_RSAPSS_MECHS; + ctx->data.rsa.allowed_types = OPENSSL_malloc(mechs_size); if (ctx->data.rsa.allowed_types == NULL) { P11PROV_raise(ctx->provctx, CKR_HOST_MEMORY, "Allocating data"); return CKR_HOST_MEMORY; } - memcpy(ctx->data.rsa.allowed_types, rsapss_mechs, sizeof(rsapss_mechs)); - ctx->data.rsa.allowed_types_size = sizeof(rsapss_mechs); + memcpy(ctx->data.rsa.allowed_types, p11prov_rsapss_mechs, mechs_size); + ctx->data.rsa.allowed_types_size = mechs_size; return CKR_OK; } @@ -1194,6 +1195,7 @@ const OSSL_DISPATCH p11prov_rsapss_keymgmt_functions[] = { DISPATCH_KEYMGMT_ELEM(rsa, LOAD, load), DISPATCH_KEYMGMT_ELEM(rsa, FREE, free), DISPATCH_KEYMGMT_ELEM(rsa, HAS, has), + DISPATCH_KEYMGMT_ELEM(rsa, MATCH, match), DISPATCH_KEYMGMT_ELEM(rsa, IMPORT, import), DISPATCH_KEYMGMT_ELEM(rsa, IMPORT_TYPES, import_types), DISPATCH_KEYMGMT_ELEM(rsa, EXPORT, export), diff --git a/src/keymgmt.h b/src/keymgmt.h index 0e78d717..0a538f17 100644 --- a/src/keymgmt.h +++ b/src/keymgmt.h @@ -5,6 +5,10 @@ #define _KEYMGMT_H /* keymgmt */ + +#define P11PROV_N_RSAPSS_MECHS 10 +extern const CK_MECHANISM_TYPE p11prov_rsapss_mechs[P11PROV_N_RSAPSS_MECHS]; + #define DISPATCH_KEYMGMT_FN(type, name) \ DECL_DISPATCH_FUNC(keymgmt, p11prov_##type, name) #define DISPATCH_KEYMGMT_ELEM(type, NAME, name) \ diff --git a/src/objects.c b/src/objects.c index 84d2eb88..80b76d3a 100644 --- a/src/objects.c +++ b/src/objects.c @@ -558,6 +558,69 @@ CK_KEY_TYPE p11prov_obj_get_key_type(P11PROV_OBJ *obj) return CK_UNAVAILABLE_INFORMATION; } +bool p11prov_obj_is_rsa_pss(P11PROV_OBJ *obj) +{ + CK_ATTRIBUTE *am = p11prov_obj_get_attr(obj, CKA_ALLOWED_MECHANISMS); + bool non_pss_found = false; + CK_MECHANISM_TYPE *allowed; + int am_nmechs; + + if (am == NULL || am->ulValueLen == 0) { + /* no limitations or no support for allowed mechs + * TODO we can try also certificate restrictions */ + return false; + } + allowed = (CK_MECHANISM_TYPE *)am->pValue; + am_nmechs = am->ulValueLen / sizeof(CK_MECHANISM_TYPE); + for (int i = 0; i < am_nmechs; i++) { + bool found = false; + for (int j = 0; j < P11PROV_N_RSAPSS_MECHS; j++) { + if (allowed[i] == p11prov_rsapss_mechs[j]) { + found = true; + break; + } + } + if (!found) { + non_pss_found = true; + } + } + if (non_pss_found) { + return false; + } + return true; +} + +char *p11prov_obj_get_ossl_key_type(P11PROV_OBJ *obj) +{ + CK_KEY_TYPE type = p11prov_obj_get_key_type(obj); + switch (type) { + case CKK_RSA: + if (p11prov_obj_is_rsa_pss(obj)) { + return (char *)P11PROV_NAME_RSAPSS; + } else { + return (char *)P11PROV_NAME_RSA; + } + break; + case CKK_EC: + return (char *)P11PROV_NAME_EC; + break; + case CKK_EC_EDWARDS: + switch (p11prov_obj_get_key_bit_size(obj)) { + case ED448_BIT_SIZE: + return (char *)ED448; + break; + case ED25519_BIT_SIZE: + return (char *)ED25519; + break; + default: + return NULL; + } + break; + default: + return NULL; + } +} + CK_ULONG p11prov_obj_get_key_bit_size(P11PROV_OBJ *obj) { if (obj) { @@ -1723,9 +1786,9 @@ static int p11prov_obj_export_public_rsa_key(P11PROV_OBJ *obj, OSSL_CALLBACK *cb_fn, void *cb_arg) { CK_ATTRIBUTE attrs[RSA_PUB_ATTRS] = { 0 }; - OSSL_PARAM params[RSA_PUB_ATTRS + 1]; + OSSL_PARAM params[RSA_PUB_ATTRS + 2]; CK_RV rv; - int ret; + int ret, n = 0; if (p11prov_obj_get_key_type(obj) != CKK_RSA) { return RET_OSSL_ERR; @@ -1741,12 +1804,21 @@ static int p11prov_obj_export_public_rsa_key(P11PROV_OBJ *obj, } byteswap_buf(attrs[0].pValue, attrs[0].pValue, attrs[0].ulValueLen); - params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, attrs[0].pValue, - attrs[0].ulValueLen); + params[n++] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, + attrs[0].pValue, attrs[0].ulValueLen); byteswap_buf(attrs[1].pValue, attrs[1].pValue, attrs[1].ulValueLen); - params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, attrs[1].pValue, - attrs[1].ulValueLen); - params[RSA_PUB_ATTRS] = OSSL_PARAM_construct_end(); + params[n++] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, + attrs[1].pValue, attrs[1].ulValueLen); + /* If this is an RSA-PSS limited key, OpenSSL need some more signs here. + * The PKCS#11 specification is not compatible with what OpenSSL expects + * (unless we would have just one mechanisms specified in + * ALLOWED_MECHANISMS) */ + if (p11prov_obj_is_rsa_pss(obj)) { + params[n++] = OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_RSA_MASKGENFUNC, (char *)SN_mgf1, strlen(SN_mgf1)); + /* TODO add the other params if restricted? */ + } + params[n++] = OSSL_PARAM_construct_end(); ret = cb_fn(params, cb_arg); diff --git a/src/objects.h b/src/objects.h index c6ee04ff..ffbb4405 100644 --- a/src/objects.h +++ b/src/objects.h @@ -26,6 +26,7 @@ CK_OBJECT_CLASS p11prov_obj_get_class(P11PROV_OBJ *obj); CK_ATTRIBUTE *p11prov_obj_get_attr(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type); bool p11prov_obj_get_bool(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type, bool def); CK_KEY_TYPE p11prov_obj_get_key_type(P11PROV_OBJ *obj); +char *p11prov_obj_get_ossl_key_type(P11PROV_OBJ *obj); CK_ULONG p11prov_obj_get_key_bit_size(P11PROV_OBJ *obj); CK_ULONG p11prov_obj_get_key_size(P11PROV_OBJ *obj); void p11prov_obj_to_store_reference(P11PROV_OBJ *obj, void **reference, @@ -62,6 +63,7 @@ int p11prov_obj_get_ed_pub_key(P11PROV_OBJ *obj, CK_ATTRIBUTE **pub); CK_ATTRIBUTE *p11prov_obj_get_ec_public_raw(P11PROV_OBJ *key); P11PROV_OBJ *mock_pub_ec_key(P11PROV_CTX *ctx, CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE *ec_params); +bool p11prov_obj_is_rsa_pss(P11PROV_OBJ *obj); #define OBJ_CMP_KEY_TYPE 0x00 #define OBJ_CMP_KEY_PUBLIC 0x01 diff --git a/src/signature.c b/src/signature.c index f1a4c8e0..30dd319a 100644 --- a/src/signature.c +++ b/src/signature.c @@ -268,6 +268,16 @@ static void p11prov_sig_freectx(void *ctx) }; \ DEFINE_DER_DIGESTINFO(sha3_##bits, digestinfo_algid, bits/8) +/* ... pkcs(1) 10 (id-RSASSA-PSS) */ +#define DER_RSASSA_PSS DER_RSADSI_PKCS1, 0x0A +#define DER_RSASSA_PSS_LEN (DER_RSADSI_PKCS1_LEN + 1) + +static const unsigned char der_rsa_pss[] = { + DER_SEQUENCE, 0, /* length will be adjusted */ + DER_OBJECT, DER_RSASSA_PSS_LEN, DER_RSASSA_PSS, + /* the params added separately as they are quite complex */ +}; + static const unsigned char der_rsa_sha1[] = { DER_SEQUENCE, DER_RSADSI_PKCS1_LEN+5, DER_OBJECT, DER_RSADSI_PKCS1_LEN+1, DER_RSADSI_PKCS1, 0x05, @@ -299,27 +309,33 @@ DEFINE_DER_SEQ_SHA3(224, 0x0D, 0x09, 0x07); #define DM_ELEM_SHA(bits) \ { \ - .digest = CKM_SHA##bits, .pkcs_mech = CKM_SHA##bits##_RSA_PKCS, \ + .digest = CKM_SHA##bits, \ + .pkcs_mech = CKM_SHA##bits##_RSA_PKCS, \ .pkcs_pss = CKM_SHA##bits##_RSA_PKCS_PSS, \ - .ecdsa_mech = CKM_ECDSA_SHA##bits, .mgf = CKG_MGF1_SHA##bits, \ + .ecdsa_mech = CKM_ECDSA_SHA##bits, \ + .mgf = CKG_MGF1_SHA##bits, \ .der_rsa_algorithm_id = der_rsa_sha##bits, \ .der_rsa_algorithm_id_len = sizeof(der_rsa_sha##bits), \ .der_ecdsa_algorithm_id = der_ecdsa_sha##bits, \ .der_ecdsa_algorithm_id_len = sizeof(der_ecdsa_sha##bits), \ .der_digestinfo = der_digestinfo_sha##bits, \ .der_digestinfo_len = sizeof(der_digestinfo_sha##bits), \ + .hash_md = EVP_sha##bits, \ } #define DM_ELEM_SHA3(bits) \ { \ - .digest = CKM_SHA3_##bits, .pkcs_mech = CKM_SHA3_##bits##_RSA_PKCS, \ + .digest = CKM_SHA3_##bits, \ + .pkcs_mech = CKM_SHA3_##bits##_RSA_PKCS, \ .pkcs_pss = CKM_SHA3_##bits##_RSA_PKCS_PSS, \ - .ecdsa_mech = CKM_ECDSA_SHA3_##bits, .mgf = CKG_MGF1_SHA3_##bits, \ + .ecdsa_mech = CKM_ECDSA_SHA3_##bits, \ + .mgf = CKG_MGF1_SHA3_##bits, \ .der_rsa_algorithm_id = der_rsa_sha3_##bits, \ .der_rsa_algorithm_id_len = sizeof(der_rsa_sha3_##bits), \ .der_ecdsa_algorithm_id = der_ecdsa_sha3_##bits, \ .der_ecdsa_algorithm_id_len = sizeof(der_ecdsa_sha3_##bits), \ .der_digestinfo = der_digestinfo_sha3_##bits, \ .der_digestinfo_len = sizeof(der_digestinfo_sha3_##bits), \ + .hash_md = EVP_sha3_##bits, \ } /* only the ones we can support */ @@ -335,6 +351,7 @@ struct p11prov_mech { int der_ecdsa_algorithm_id_len; const unsigned char *der_digestinfo; int der_digestinfo_len; + const EVP_MD *(*hash_md)(void); }; typedef struct p11prov_mech P11PROV_MECH; @@ -349,8 +366,8 @@ static const P11PROV_MECH mech_map[] = { DM_ELEM_SHA(224), { CKM_SHA_1, CKM_SHA1_RSA_PKCS, CKM_SHA1_RSA_PKCS_PSS, CKM_ECDSA_SHA1, CKG_MGF1_SHA1, der_rsa_sha1, sizeof(der_rsa_sha1), der_ecdsa_sha1, - sizeof(der_ecdsa_sha1), der_digestinfo_sha1, - sizeof(der_digestinfo_sha1) }, + sizeof(der_ecdsa_sha1), der_digestinfo_sha1, sizeof(der_digestinfo_sha1), + EVP_sha1 }, { CK_UNAVAILABLE_INFORMATION, 0, 0, 0, 0, 0, 0, 0, 0 }, }; @@ -507,11 +524,13 @@ static CK_RV pss_defaults(P11PROV_SIG_CTX *sigctx, CK_MECHANISM *mechanism, sigctx->pss_params.sLen = size; } - mechanism->pParameter = &sigctx->pss_params; - mechanism->ulParameterLen = sizeof(sigctx->pss_params); + if (mechanism) { + mechanism->pParameter = &sigctx->pss_params; + mechanism->ulParameterLen = sizeof(sigctx->pss_params); - if (set_mech) { - mechanism->mechanism = mech->pkcs_pss; + if (set_mech) { + mechanism->mechanism = mech->pkcs_pss; + } } return CKR_OK; @@ -1330,16 +1349,73 @@ static struct { { CK_UNAVAILABLE_INFORMATION, 0, NULL }, }; +static unsigned char * +p11prov_rsapss_encode_algorithm_id(P11PROV_SIG_CTX *sigctx, size_t *outlen) +{ + RSA_PSS_PARAMS *pss = NULL; + unsigned char *buffer = NULL, *p; + const P11PROV_MECH *mech; + const EVP_MD *md, *mgf1_md; + int ret, len; + + /* when OpenSSL calls this before it makes the signature, we still might not + * have the defaults set so do it now */ + ret = pss_defaults(sigctx, NULL, false); + if (ret != CKR_OK) { + P11PROV_raise(sigctx->provctx, ret, "Failed to set PSS defaults"); + goto err; + } + + ret = p11prov_mech_by_mechanism(sigctx->pss_params.hashAlg, &mech); + if (ret != CKR_OK) { + P11PROV_raise(sigctx->provctx, ret, "Failed to get mech for digest %lx", + sigctx->pss_params.hashAlg); + goto err; + } + md = mech->hash_md(); + + ret = p11prov_mech_by_mgf(sigctx->pss_params.mgf, &mech); + if (ret != CKR_OK) { + P11PROV_raise(sigctx->provctx, ret, "Failed to get mech for mgf %lx", + sigctx->pss_params.mgf); + goto err; + } + mgf1_md = mech->hash_md(); + + pss = p11prov_encode_rsa_pss_params(md, mgf1_md, sigctx->pss_params.sLen); + + len = i2d_RSA_PSS_PARAMS(pss, NULL); + if (len < 0) { + P11PROV_raise(sigctx->provctx, ret, + "Failed to get RSA_PSS_PARAMS size"); + goto err; + } + buffer = OPENSSL_malloc(len + sizeof(der_rsa_pss)); + if (buffer == NULL) { + P11PROV_raise(sigctx->provctx, ret, "Failed to encode RSA_PSS_PARAMS"); + goto err; + } + memcpy(buffer, der_rsa_pss, sizeof(der_rsa_pss)); + p = buffer + sizeof(der_rsa_pss); + len = i2d_RSA_PSS_PARAMS(pss, &p); + buffer[1] = len + sizeof(der_rsa_pss) - 2; /* sneak in the length */ + *outlen = len + sizeof(der_rsa_pss); + RSA_PSS_PARAMS_free(pss); + return buffer; + +err: + RSA_PSS_PARAMS_free(pss); + return NULL; +} + static int p11prov_rsasig_get_ctx_params(void *ctx, OSSL_PARAM *params) { P11PROV_SIG_CTX *sigctx = (P11PROV_SIG_CTX *)ctx; OSSL_PARAM *p; + size_t len; + unsigned char *algorithm_id = NULL; int ret; - /* todo sig params: - OSSL_SIGNATURE_PARAM_ALGORITHM_ID - */ - P11PROV_debug("rsasig get ctx params (ctx=%p, params=%p)", ctx, params); if (params == NULL) { @@ -1369,8 +1445,21 @@ static int p11prov_rsasig_get_ctx_params(void *ctx, OSSL_PARAM *params) case CKM_RSA_X_509: return RET_OSSL_ERR; case CKM_RSA_PKCS_PSS: - /* TODO */ - return RET_OSSL_ERR; + /* The AlgorithmIdentifier here needs to contain also the + * information about the RSA-PSS parameters as defined in + * https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.3 */ + algorithm_id = p11prov_rsapss_encode_algorithm_id(sigctx, &len); + if (algorithm_id == NULL) { + P11PROV_raise(sigctx->provctx, CKR_GENERAL_ERROR, + "Failed to get encode algorithm ID for RSA-PSS"); + return RET_OSSL_ERR; + } + ret = OSSL_PARAM_set_octet_string(p, algorithm_id, len); + OPENSSL_free(algorithm_id); + if (ret != RET_OSSL_OK) { + return ret; + } + break; } } diff --git a/src/store.c b/src/store.c index 3d9d9bc7..9140f914 100644 --- a/src/store.c +++ b/src/store.c @@ -244,7 +244,6 @@ static int p11prov_store_load(void *pctx, OSSL_CALLBACK *object_cb, P11PROV_OBJ *obj = NULL; OSSL_PARAM params[4]; int object_type; - CK_KEY_TYPE type; char *data_type; bool found = false; @@ -357,27 +356,8 @@ static int p11prov_store_load(void *pctx, OSSL_CALLBACK *object_cb, case CKO_PUBLIC_KEY: case CKO_PRIVATE_KEY: object_type = OSSL_OBJECT_PKEY; - type = p11prov_obj_get_key_type(obj); - switch (type) { - case CKK_RSA: - data_type = (char *)P11PROV_NAME_RSA; - break; - case CKK_EC: - data_type = (char *)P11PROV_NAME_EC; - break; - case CKK_EC_EDWARDS: - switch (p11prov_obj_get_key_bit_size(obj)) { - case ED448_BIT_SIZE: - data_type = (char *)ED448; - break; - case ED25519_BIT_SIZE: - data_type = (char *)ED25519; - break; - default: - return RET_OSSL_ERR; - } - break; - default: + data_type = p11prov_obj_get_ossl_key_type(obj); + if (data_type == NULL) { return RET_OSSL_ERR; } p11prov_obj_to_store_reference(obj, &reference, &reference_sz); diff --git a/tests/cert.json.rsapss.in b/tests/cert.json.rsapss.in new file mode 100644 index 00000000..e265358b --- /dev/null +++ b/tests/cert.json.rsapss.in @@ -0,0 +1,27 @@ +, + {"server_command": [ + @CHECKER@"openssl", "s_server", @PROPQ@"-www", "-port", "@PORT@", + "-key", "@PRIURI@", "-cert", "@CRTURI@"], + "comment": "Run test with RSA-PSS key without certificate verify", + "environment": {"PYTHONPATH" : "."}, + "server_hostname": "localhost", + "server_port": @PORT@, + "common_arguments": ["-p", "@PORT@"], + "tests" : [ + {"name" : "test-tls13-conversation.py"}, + {"name" : "test-tls13-rsapss-signatures.py"}, + {"name" : "test-conversation.py", + "arguments" : ["-d"]}, + {"name" : "test-sig-algs.py", + "comment" : "the server has just one certificate installed", + "arguments" : [ + "-n", "0", + "-x", "rsa_pss_rsae_sha256 only", + "-x", "rsa_pss_rsae_sha384 only", + "-x", "rsa_pss_rsae_sha512 only"] + }, + {"name" : "test-tls13-signature-algorithms.py", + "arguments" : ["-n", "0"]} + ] + } + diff --git a/tests/kryoptic-init.sh b/tests/kryoptic-init.sh index 705ba8a7..93b31af5 100755 --- a/tests/kryoptic-init.sh +++ b/tests/kryoptic-init.sh @@ -46,3 +46,5 @@ export TESTPORT="34000" # Older versions of certtool do not support non-DER encoded CKA_EC_POINT # so set the kryoptic env var to enforce compatibility for the setup phase export KRYOPTIC_EC_POINT_ENCODING="DER" + +export SUPPORT_ALLOWED_MECHANISMS=1 diff --git a/tests/kryoptic.nss-init.sh b/tests/kryoptic.nss-init.sh index 9fd87b8b..6fb4eb75 100644 --- a/tests/kryoptic.nss-init.sh +++ b/tests/kryoptic.nss-init.sh @@ -25,3 +25,7 @@ source "${TESTSSRCDIR}/kryoptic-init.sh" export TOKENCONFIGVARS="export KRYOPTIC_CONF=${TMPPDIR}/kryoptic.conf" export TOKENOPTIONS="${TOKENOPTIONS}\npkcs11-module-quirks = no-allowed-mechanisms" export TESTPORT="36000" + +# While this works with the default DB, the NSS DB does not support this +# attribute +export SUPPORT_ALLOWED_MECHANISMS=0 diff --git a/tests/setup.sh b/tests/setup.sh index 3887d5d0..7837e63d 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -17,6 +17,7 @@ SUPPORT_ED448=1 SUPPORT_RSA_PKCS1_ENCRYPTION=1 SUPPORT_RSA_KEYGEN_PUBLIC_EXPONENT=1 SUPPORT_TLSFUZZER=1 +SUPPORT_ALLOWED_MECHANISMS=0 # Ed448 requires OpenSC 0.26.0 OPENSC_VERSION=$(opensc-tool -i | grep OpenSC | sed -e "s/OpenSC 0\.\([0-9]*\).*/\1/") @@ -141,6 +142,23 @@ CACRT_PEM="${TMPPDIR}/${CACRTN}.pem" CACRT="${TMPPDIR}/${CACRTN}.crt" openssl x509 -inform DER -in "$CACRT" -outform PEM -out "$CACRT_PEM" +CABASEURIWITHPINVALUE="pkcs11:id=${URIKEYID}?pin-value=${PINVALUE}" +CABASEURIWITHPINSOURCE="pkcs11:id=${URIKEYID}?pin-source=file:${PINFILE}" +CABASEURI="pkcs11:id=${URIKEYID}" +CAPUBURI="pkcs11:type=public;id=${URIKEYID}" +CAPRIURI="pkcs11:type=private;id=${URIKEYID}" +CACRTURI="pkcs11:type=cert;object=${CACRTN}" + +title LINE "RSA PKCS11 URIS" +echo "${CABASEURIWITHPINVALUE}" +echo "${CABASEURIWITHPINSOURCE}" +echo "${CABASEURI}" +echo "${CAPUBURI}" +echo "${CAPRIURI}" +echo "${CACRTURI}" +echo "" + + cat "${TMPPDIR}/cacert.cfg" > "${TMPPDIR}/cert.cfg" # the organization identification is not in the CA echo 'organization = "PKCS11 Provider"' >> "${TMPPDIR}/cert.cfg" @@ -151,6 +169,7 @@ ca_sign() { LABEL=$1 CN=$2 KEYID=$3 + shift 3 ((SERIAL+=1)) sed -e "s|cn = .*|cn = $CN|g" \ -e "s|serial = .*|serial = $SERIAL|g" \ @@ -162,7 +181,8 @@ ca_sign() { --load-privkey "pkcs11:object=$LABEL;token=$TOKENLABELURI;type=private" \ --load-pubkey "pkcs11:object=$LABEL;token=$TOKENLABELURI;type=public" --outder \ --load-ca-certificate "${CACRT}" --inder \ - --load-ca-privkey="pkcs11:object=$CACRTN;token=$TOKENLABELURI;type=private" + --load-ca-privkey="pkcs11:object=$CACRTN;token=$TOKENLABELURI;type=private" \ + "$@" pkcs11-tool "${P11DEFARGS[@]}" --write-object "${TMPPDIR}/${LABEL}.crt" --type=cert \ --id="$KEYID" --label="$LABEL" 2>&1 } @@ -394,6 +414,35 @@ echo "${ECPRI3URI}" echo "${ECCRT3URI}" echo "" +if [ "${SUPPORT_ALLOWED_MECHANISMS}" -eq 1 ]; then + # generate RSA-PSS key pair and self-signed RSA-PSS certificate + KEYID='0010' + URIKEYID="%00%10" + TSTCRTN="testRsaPssCert" + + pkcs11-tool "${P11DEFARGS[@]}" --keypairgen --key-type="RSA:2048" \ + --label="${TSTCRTN}" --id="$KEYID" --allowed-mechanisms \ + RSA-PKCS-PSS,SHA1-RSA-PKCS-PSS,SHA224-RSA-PKCS-PSS,SHA256-RSA-PKCS-PSS,SHA384-RSA-PKCS-PSS,SHA512-RSA-PKCS-PSS + ca_sign "${TSTCRTN}" "My RsaPss Cert" $KEYID "--sign-params=RSA-PSS" + + RSAPSSBASEURIWITHPINVALUE="pkcs11:id=${URIKEYID}?pin-value=${PINVALUE}" + RSAPSSBASEURIWITHPINSOURCE="pkcs11:id=${URIKEYID}?pin-source=file:${PINFILE}" + RSAPSSBASEURI="pkcs11:id=${URIKEYID}" + RSAPSSPUBURI="pkcs11:type=public;id=${URIKEYID}" + RSAPSSPRIURI="pkcs11:type=private;id=${URIKEYID}" + RSAPSSCRTURI="pkcs11:type=cert;object=${TSTCRTN}" + + title LINE "RSA-PSS PKCS11 URIS" + echo "${RSAPSSBASEURIWITHPINVALUE}" + echo "${RSAPSSBASEURIWITHPINSOURCE}" + echo "${RSAPSSBASEURI}" + echo "${RSAPSSPUBURI}" + echo "${RSAPSSPRIURI}" + echo "${RSAPSSCRTURI}" + echo "" +fi + + title PARA "Show contents of ${TOKENTYPE} token" echo " ----------------------------------------------------------------------------------------------------" pkcs11-tool "${P11DEFARGS[@]}" -O @@ -428,17 +477,24 @@ export SUPPORT_ED448="${SUPPORT_ED448}" export SUPPORT_RSA_PKCS1_ENCRYPTION="${SUPPORT_RSA_PKCS1_ENCRYPTION}" export SUPPORT_RSA_KEYGEN_PUBLIC_EXPONENT="${SUPPORT_RSA_KEYGEN_PUBLIC_EXPONENT}" export SUPPORT_TLSFUZZER="${SUPPORT_TLSFUZZER}" +export SUPPORT_ALLOWED_MECHANISMS="${SUPPORT_ALLOWED_MECHANISMS}" export TESTPORT="${TESTPORT}" -export CACRT="${CACRT_PEM}" - export TOKDIR="${TOKDIR}" export TMPPDIR="${TMPPDIR}" export PINVALUE="${PINVALUE}" export SEEDFILE="${TMPPDIR}/noisefile.bin" export RAND64FILE="${TMPPDIR}/64krandom.bin" +export CACRT="${CACRT_PEM}" +export CABASEURIWITHPINVALUE="${CABASEURIWITHPINVALUE}" +export CABASEURIWITHPINSOURCE="${CABASEURIWITHPINSOURCE}" +export CABASEURI="${CABASEURI}" +export CAPUBURI="${CAPUBURI}" +export CAPRIURI="${CAPRIURI}" +export CACRTURI="${CACRTURI}" + export BASEURIWITHPINVALUE="${BASEURIWITHPINVALUE}" export BASEURIWITHPINSOURCE="${BASEURIWITHPINSOURCE}" export BASEURI="${BASEURI}" @@ -515,6 +571,18 @@ export ECXPRIURI="${ECXPRIURI}" DBGSCRIPT fi +if [ -n "${RSAPSSBASEURI}" ]; then + cat >> "${TMPPDIR}/testvars" <> "${TMPPDIR}/testvars" < "${OPENSSL_CONF}.tmpca" + export OPENSSL_CONF=${OPENSSL_CONF}.tmpca + + mkdir -p "${TMPCA}/newcerts" "${TMPCA}/private" + if [ ! -e "${TMPCA}/serial" ]; then + echo "01" > "${TMPCA}/serial" + fi + touch "${TMPCA}/index.txt" + + title PARA "Generating a new CSR with existing RSA-PSS key in token" + ossl 'req -batch -noenc -x509 -new -key ${RSAPSSPRIURI} + -sigopt rsa_padding_mode:pss + -sigopt digest:sha256 + -sigopt rsa_pss_saltlen:-1 + -out ${TMPCA}/rsapss-cacert.pem' + OPENSSL_CONF=${TMP_OPENSSL_CONF} + + run_test "$RSAPSSPRIURI" "${TMPCA}/rsapss-cacert.pem" + fi + title PARA "Run sanity test with default values (ECDSA)" run_test "$ECPRIURI" "$ECCRTURI" diff --git a/tests/ttlsfuzzer b/tests/ttlsfuzzer index 4788278b..e1807247 100755 --- a/tests/ttlsfuzzer +++ b/tests/ttlsfuzzer @@ -30,7 +30,7 @@ prepare_test() { KEY="$2" CERT="$3" # Prepare the tlsfuzzer configuration - sed -e "s|@PRIURI@|$KEY|g" -e "s/@CRTURI@/$CERT/g" \ + sed -e "s|@PRIURI@|$KEY|g" -e "s|@CRTURI@|$CERT|g" \ -e "s/@PORT@/$PORT/g" \ -e "s/@PROPQ@/$PROPQ/g" \ -e "s/@SIGALGS@/$SIGALGS/g" "${TESTSSRCDIR}/${TEMPLATE}" >>"${TMPFILE}" @@ -62,6 +62,35 @@ run_tests() { title PARA "Prepare test for RSA" prepare_test cert.json.rsa.in "$PRIURI" "$CRTURI" + if [[ -n "$RSAPSSBASEURI" ]]; then + title PARA "Prepare test for RSA-PSS" + + # do selfsign now using openssl as certool can not create SPKI using + # RSA-PSS + TMPCA=${TMPPDIR}/tmpca + TMP_OPENSSL_CONF=${OPENSSL_CONF} + sed -e "s|^dir .*|dir = ${TMPCA}|" \ + "${OPENSSL_CONF}" > "${OPENSSL_CONF}.tmpca" + export OPENSSL_CONF=${OPENSSL_CONF}.tmpca + + mkdir -p "${TMPCA}/newcerts" "${TMPCA}/private" + if [ ! -e "${TMPCA}/serial" ]; then + echo "01" > "${TMPCA}/serial" + fi + touch "${TMPCA}/index.txt" + + title PARA "Generating a new CSR with existing RSA-PSS key in token" + ossl 'req -batch -noenc -x509 -new -key ${RSAPSSPRIURI} + -sigopt rsa_padding_mode:pss + -sigopt digest:sha256 + -sigopt rsa_pss_saltlen:-1 + -out ${TMPCA}/rsapss-cacert.pem' + OPENSSL_CONF=${TMP_OPENSSL_CONF} + + prepare_test cert.json.rsapss.in \ + "$RSAPSSPRIURI" "${TMPCA}/rsapss-cacert.pem" + fi + title PARA "Prepare test for ECDSA" # Note, that tlsfuzzer expects the homogeneous CA and server keys # so we are using here the self-signed peer EC Key, instead of