From 4a847677cc3bf81988d046673bf0612d6a0eb228 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Fri, 20 Oct 2017 16:35:09 +0200 Subject: [PATCH] WIP:Use RSA/EC_KEY METHOD to provide X509 signatures using high-level OpenSSL API --- tool/yubico-piv-tool.c | 77 ++++++++++++++++++++++++++++++++++++++++++ ykcs11/openssl_utils.c | 2 ++ 2 files changed, 79 insertions(+) diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index ca50fa7..313b9e5 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -116,6 +116,62 @@ static bool sign_data(ykpiv_state *state, const unsigned char *in, size_t len, u return false; } +static int ec_key_ex_data_idx = -1; + +struct internal_key { + ykpiv_state *state; + int algorithm; + int key; +}; + +int +yk_rsa_meth_sign(int dtype, const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa) +{ + const RSA_METHOD *meth = RSA_get_method(rsa); + const struct internal_key *key = RSA_meth_get0_app_data(meth); + if (sign_data(key->state, m, m_length, sigret, (size_t *)siglen, key->algorithm, key->key)) + return 0; + + return 1; +} + +int +yk_ec_meth_sign(int type, const unsigned char *dgst, int dlen, + unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv, + const BIGNUM *r, EC_KEY *ec) +{ + const struct internal_key *key = EC_KEY_get_ex_data(ec, ec_key_ex_data_idx); + if (sign_data(key->state, dgst, dlen, sig, (size_t *)siglen, key->algorithm, key->key)) + return 0; + + return 1; +} + +static int +wrap_public_key(ykpiv_state *state, int algorithm, EVP_PKEY *public_key, + int key) +{ + if(YKPIV_IS_RSA(algorithm)) { + RSA_METHOD *meth = RSA_meth_dup(RSA_get_default_method()); + RSA *rsa = EVP_PKEY_get0_RSA(public_key); + struct internal_key int_key = {state, algorithm, key}; + RSA_meth_set0_app_data(meth, &int_key); + RSA_meth_set_sign(meth, yk_rsa_meth_sign); + RSA_set_method(rsa, meth); + } else { + EC_KEY *ec = EVP_PKEY_get0_EC_KEY(public_key); + EC_KEY_METHOD *meth = EC_KEY_METHOD_new(EC_KEY_get_method(ec)); + struct internal_key int_key = {state, algorithm, key}; + if (ec_key_ex_data_idx == -1) + ec_key_ex_data_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); + EC_KEY_set_ex_data(ec, ec_key_ex_data_idx, &int_key); + EC_KEY_METHOD_set_sign(meth, yk_ec_meth_sign, NULL, NULL); /* XXX ?? */ + EC_KEY_set_method(ec, meth); + } + return 0; +} + static bool generate_key(ykpiv_state *state, const char *slot, enum enum_algorithm algorithm, const char *output_file_name, enum enum_key_format key_format, enum enum_pin_policy pin_policy, @@ -743,6 +799,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for goto request_out; } +#if OPENSSL_VERSION_NUMBER < 10100000L memcpy(digest, oid, oid_len); /* XXX: this should probably use X509_REQ_digest() but that's buggy */ if(!ASN1_item_digest(ASN1_ITEM_rptr(X509_REQ_INFO), md, req->req_info, @@ -756,6 +813,7 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for fprintf(stderr, "Unsupported algorithm %x or hash %x\n", algorithm, hash); goto request_out; } + if(YKPIV_IS_RSA(algorithm)) { signinput = digest; len = oid_len + digest_len; @@ -778,6 +836,13 @@ static bool request_certificate(ykpiv_state *state, enum enum_key_format key_for /* mark that all bits should be used. */ req->signature->flags = ASN1_STRING_FLAG_BITS_LEFT; } +#else + /* With opaque structures we can not touch whatever we want, but we need + * to embed the sign_data function in the RSA/EC key structures */ + wrap_public_key(state, algorithm, public_key, key); + + X509_REQ_sign(req, public_key, md); +#endif if(key_format == key_format_arg_PEM) { PEM_write_X509_REQ(output_file, req); @@ -797,9 +862,11 @@ request_out: EVP_PKEY_free(public_key); } if(req) { +#if OPENSSL_VERSION_NUMBER < 10100000L if(req->sig_alg->parameter) { req->sig_alg->parameter = NULL; } +#endif X509_REQ_free(req); } if(name) { @@ -928,6 +995,7 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo if(nid == 0) { goto selfsign_out; } +#if OPENSSL_VERSION_NUMBER < 10100000L if(YKPIV_IS_RSA(algorithm)) { signinput = digest; len = oid_len + md_len; @@ -961,6 +1029,13 @@ static bool selfsign_certificate(ykpiv_state *state, enum enum_key_format key_fo * certificate can be validated. */ x509->signature->flags = ASN1_STRING_FLAG_BITS_LEFT; } +#else + /* With opaque structures we can not touch whatever we want, but we need + * to embed the sign_data function in the RSA/EC key structures */ + wrap_public_key(state, algorithm, public_key, key); + + X509_sign(x509, public_key, md); +#endif if(key_format == key_format_arg_PEM) { PEM_write_X509(output_file, x509); @@ -977,10 +1052,12 @@ selfsign_out: fclose(output_file); } if(x509) { +#if OPENSSL_VERSION_NUMBER < 10100000L if(x509->sig_alg->parameter) { x509->sig_alg->parameter = NULL; x509->cert_info->signature->parameter = NULL; } +#endif X509_free(x509); } if(public_key) { diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index b1899df..ee9e229 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -165,6 +165,7 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, X509_set_notBefore(cert, tm); X509_set_notAfter(cert, tm); +#if OPENSSL_VERSION_NUMBER < 10100000L // Manually set the signature algorithms. // OpenSSL 1.0.1i complains about empty DER fields // 8 => md5WithRsaEncryption @@ -174,6 +175,7 @@ CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, // Manually set a signature (same reason as before) ASN1_BIT_STRING_set_bit(cert->signature, 8, 1); ASN1_BIT_STRING_set(cert->signature, "\x00", 1); +#endif len = i2d_X509(cert, NULL); if (len < 0)