From 6d098460d6bdd86460b5e52b1c3e051544192feb Mon Sep 17 00:00:00 2001 From: Yaroslav Isakov Date: Thu, 28 Jul 2022 22:48:20 +0200 Subject: [PATCH] Support importing ED25519 keys and signing with them, using PKCS#11 tokens What works: - Importing ED25519 Signature key from Yubikey token (OpenSC with openpgp driver, key generated via gpg) - Creating self-signed certificate for CA, for Signature key from card - Signing Certificates using new CA I haven't tested other features, e.g. storing new certificate on card, so, they're probably broken --- lib/opensc-pkcs11.h | 3 + lib/pkcs11.cpp | 131 ++++++++++++++++++++++++++++++++++++++------ lib/pki_scard.cpp | 23 ++++++++ 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/lib/opensc-pkcs11.h b/lib/opensc-pkcs11.h index 741ae754..4de29c5e 100644 --- a/lib/opensc-pkcs11.h +++ b/lib/opensc-pkcs11.h @@ -364,6 +364,8 @@ typedef unsigned long ck_key_type_t; #define CKK_AES (0x1f) #define CKK_BLOWFISH (0x20) #define CKK_TWOFISH (0x21) +#define CKK_EC_EDWARDS (0x40) +#define CKK_EC_MONTGOMERY (0x41) #define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31)) @@ -664,6 +666,7 @@ typedef unsigned long ck_mechanism_type_t; #define CKM_ECDH1_DERIVE (0x1050) #define CKM_ECDH1_COFACTOR_DERIVE (0x1051) #define CKM_ECMQV_DERIVE (0x1052) +#define CKM_EDDSA (0x1057) #define CKM_JUNIPER_KEY_GEN (0x1060) #define CKM_JUNIPER_ECB128 (0x1061) #define CKM_JUNIPER_CBC128 (0x1062) diff --git a/lib/pkcs11.cpp b/lib/pkcs11.cpp index 2358be4b..2684e23f 100644 --- a/lib/pkcs11.cpp +++ b/lib/pkcs11.cpp @@ -653,6 +653,23 @@ int pkcs11::encrypt(int flen, const unsigned char *from, return size; } +// Shared between libressl and openssl +static int eng_idx = -1; +static int eng_finish(ENGINE *e) +{ + pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(e, eng_idx); + delete p11; + ENGINE_set_ex_data(e, eng_idx, NULL); + return 1; +} + +static int eng_pmeth_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) +{ + void *p = EVP_PKEY_CTX_get_app_data(src); + EVP_PKEY_CTX_set_app_data(dst, p); + return 1; +} + #ifndef LIBRESSL_VERSION_NUMBER static int rsa_privdata_free(RSA *rsa) { @@ -719,6 +736,8 @@ out: #ifndef OPENSSL_NO_EC +static EVP_PKEY_METHOD *p11_eddsa_method; + static void ec_privdata_free(EC_KEY *ec) { pkcs11 *p11 = (pkcs11*)EC_KEY_get_ex_data(ec, 0); @@ -811,6 +830,61 @@ static EC_KEY_METHOD *setup_ec_key_meth() ec_set_private_proc, ec_set_public_proc); return ec_key_meth; } + +static int eddsa_eng_meths(ENGINE *e, EVP_PKEY_METHOD **m, const int **nids, int nid) +{ + static const int my_nids[] = {EVP_PKEY_ED25519 }; + (void)e; + if (m) { + switch (nid) { + case EVP_PKEY_ED25519: + *m = p11_eddsa_method; + return 1; + return 0; + } + } + if (nids) { + *nids = my_nids; + return ARRAY_SIZE(my_nids); + } + return -1; + +} + +static int eng_pmeth_sign_eddsa(EVP_MD_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + int len, rs_len, ret = -1; + unsigned char rs_buf[512]; + EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); + pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(EVP_PKEY_get0_engine(pkey), eng_idx); + + // siglen is unsigned and can' cope with -1 as return value + len = p11->encrypt(tbslen, tbs, rs_buf, sizeof rs_buf, CKM_EDDSA); + if (len & 0x01) // Must be even + goto out; + memcpy(sig, rs_buf, len); + *siglen = len; + ret = 1; + out: + ign_openssl_error(); + return ret; +} + +static int eng_pmeth_ctrl_eddsa(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) +{ + (void)p1; + switch (type) { + case EVP_PKEY_CTRL_MD: + if (p2 == NULL || (const EVP_MD *)p2 == EVP_md_null()) + return 1; + ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE); + return 0; + } + qWarning() << "EC Don't call me" << type; + return -2; +} #endif EVP_PKEY *pkcs11::getPrivateKey(EVP_PKEY *pub, CK_OBJECT_HANDLE obj) @@ -820,6 +894,32 @@ EVP_PKEY *pkcs11::getPrivateKey(EVP_PKEY *pub, CK_OBJECT_HANDLE obj) #ifndef OPENSSL_NO_EC static EC_KEY_METHOD *ec_key_meth = NULL; EC_KEY *ec; + static ENGINE *e = NULL; + + if (!e) { + e = ENGINE_new(); + Q_CHECK_PTR(e); + + ENGINE_set_pkey_meths(e, eddsa_eng_meths); + ENGINE_set_finish_function(e, eng_finish); + if (eng_idx == -1) + eng_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 0); + ENGINE_set_ex_data(e, eng_idx, NULL); + // Why is engine attached to pubkey? I'm commenting it, as I do + // not want it to be attached to RSA/DSA/EC + //CRYPTO_add(&pub->references, 1, CRYPTO_LOCK_EVP_PKEY); + //pub->engine = e; + + if (!p11_eddsa_method) { + p11_eddsa_method = EVP_PKEY_meth_new(EVP_PKEY_ED25519, + EVP_PKEY_FLAG_SIGCTX_CUSTOM); + EVP_PKEY_meth_set_digestsign(p11_eddsa_method, + eng_pmeth_sign_eddsa); + EVP_PKEY_meth_set_ctrl(p11_eddsa_method, + eng_pmeth_ctrl_eddsa, NULL); + EVP_PKEY_meth_set_copy(p11_eddsa_method, eng_pmeth_copy); + } + } #endif RSA *rsa; DSA *dsa; @@ -876,27 +976,33 @@ EVP_PKEY *pkcs11::getPrivateKey(EVP_PKEY *pub, CK_OBJECT_HANDLE obj) openssl_error(); EVP_PKEY_assign_EC_KEY(evp, ec); break; + case EVP_PKEY_ED25519: + size_t len; + if (ENGINE_get_ex_data(e, eng_idx)) + qWarning() << "We forgot to free the previous Card key."; + ENGINE_set_ex_data(e, eng_idx, this); + p11obj = obj; + EVP_PKEY_get_raw_public_key(pub, NULL, &len); + unsigned char *pubkey = (unsigned char *)OPENSSL_malloc(len); + Q_CHECK_PTR(pubkey); + EVP_PKEY_get_raw_public_key(pub, pubkey, &len); + evp = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, e, pubkey, len); + openssl_error(); + OPENSSL_free(pubkey); + //EVP_PKEY_set1_engine(evp, e); + break; #endif } return evp; } #else -static int eng_idx = -1; static EVP_PKEY_METHOD *p11_rsa_method; static EVP_PKEY_METHOD *p11_dsa_method; #ifndef OPENSSL_NO_EC static EVP_PKEY_METHOD *p11_ec_method; #endif -static int eng_finish(ENGINE *e) -{ - pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(e, eng_idx); - delete p11; - ENGINE_set_ex_data(e, eng_idx, NULL); - return 1; -} - static int eng_pmeth_ctrl_rsa(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) { switch (type) { @@ -1109,13 +1215,6 @@ out: } #endif -static int eng_pmeth_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) -{ - void *p = EVP_PKEY_CTX_get_app_data(src); - EVP_PKEY_CTX_set_app_data(dst, p); - return 1; -} - static int eng_meths(ENGINE *e, EVP_PKEY_METHOD **m, const int **nids, int nid) { static const int my_nids[] = { EVP_PKEY_EC, EVP_PKEY_RSA }; diff --git a/lib/pki_scard.cpp b/lib/pki_scard.cpp index 81e5af27..9863547a 100644 --- a/lib/pki_scard.cpp +++ b/lib/pki_scard.cpp @@ -206,6 +206,25 @@ EVP_PKEY *pki_scard::load_pubkey(pkcs11 &p11, CK_OBJECT_HANDLE object) const EVP_PKEY_assign_EC_KEY(pkey, ec); break; } + case CKK_EC_EDWARDS: { + QByteArray ba; + ASN1_OCTET_STRING *os; + pk11_attr_data grp(CKA_EC_PARAMS); + p11.loadAttribute(grp, object); + pk11_attr_data pt(CKA_EC_POINT); + p11.loadAttribute(pt, object); + ba = pt.getData(); + os = (ASN1_OCTET_STRING *) + d2i_bytearray(D2I_VOID(d2i_ASN1_OCTET_STRING), ba); + pki_openssl_error(); + pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, + (const uint8_t *)os->data, + os->length); + pki_openssl_error(); + ASN1_OCTET_STRING_free(os); + pki_openssl_error(); + break; + } #endif default: throw errorEx(QString("Unsupported CKA_KEY_TYPE: %1\n").arg(keytype)); @@ -328,6 +347,10 @@ pk11_attlist pki_scard::objectAttributesNoId(EVP_PKEY *pk, bool priv) const attrs << pk11_attr_ulong(CKA_KEY_TYPE, CKK_EC) << pk11_attr_data(CKA_EC_PARAMS, ba); break; + case EVP_PKEY_ED25519: + attrs << pk11_attr_ulong(CKA_KEY_TYPE, CKK_EC_EDWARDS); + // should it also return params, somehow? + break; #endif default: throw errorEx(QString("Unknown Keytype %d")