diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-client.c --- openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa 2018-02-16 13:25:59.426469253 +0100 +++ openssh-7.6p1/ssh-pkcs11-client.c 2018-02-16 13:25:59.428469265 +0100 @@ -31,6 +31,15 @@ #include #include +#ifdef OPENSSL_HAS_ECC +#include +#if ((defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ + (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ + (OPENSSL_VERSION_NUMBER >= 0x00010100L) +#define ENABLE_PKCS11_ECDSA 1 +#endif +#endif #include "pathnames.h" #include "xmalloc.h" @@ -139,9 +147,9 @@ pkcs11_rsa_private_encrypt(int flen, con return (ret); } -/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ +/* redirect the RSA private key encrypt operation to the ssh-pkcs11-helper */ static int -wrap_key(RSA *rsa) +wrap_rsa_key(RSA *rsa) { static RSA_METHOD helper_rsa; @@ -152,6 +160,88 @@ wrap_key(RSA *rsa) return (0); } +#ifdef ENABLE_PKCS11_ECDSA +static ECDSA_SIG * +pkcs11_ecdsa_private_sign(const unsigned char *from, int flen, + const BIGNUM *inv, const BIGNUM *rp, EC_KEY * ecdsa) +{ + struct sshkey *key = NULL; + u_char *blob, *signature = NULL; + size_t blen, slen = 0; + struct sshbuf *msg = NULL; + ECDSA_SIG *ret = NULL; + BIGNUM *r = NULL, *s = NULL; + int rv; + + if ((key = sshkey_new(KEY_ECDSA)) == NULL) + fatal("%s: sshkey_new failed", __func__); + key->ecdsa = ecdsa; + key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); + if (sshkey_to_blob(key, &blob, &blen) == 0) + goto out; + if ((msg = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((rv = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || + (rv = sshbuf_put_string(msg, blob, blen)) != 0 || + (rv = sshbuf_put_string(msg, from, flen)) != 0 || + (rv = sshbuf_put_u32(msg, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(rv)); + free(blob); + send_msg(msg); + sshbuf_reset(msg); + + if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { + if ((rv = sshbuf_get_string(msg, &signature, &slen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(rv)); + if (slen <= (size_t)ECDSA_size(ecdsa)) { + int nlen = slen / 2; + ret = ECDSA_SIG_new(); + r = BN_new(); + s = BN_new(); + BN_bin2bn(&signature[0], nlen, r); + BN_bin2bn(&signature[nlen], nlen, s); + ECDSA_SIG_set0(ret, r, s); + } + free(signature); + } +out: + sshkey_free(key); + sshbuf_free(msg); + return (ret); +} + +/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */ +static int +wrap_ecdsa_key(EC_KEY *ecdsa) { +#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) + static EC_KEY_METHOD *helper_ecdsa = NULL; + if (helper_ecdsa == NULL) { + const EC_KEY_METHOD *def = EC_KEY_get_default_method(); + helper_ecdsa = EC_KEY_METHOD_new(def); + EC_KEY_METHOD_set_sign(helper_ecdsa, NULL, NULL, pkcs11_ecdsa_private_sign); + } + EC_KEY_set_method(ecdsa, helper_ecdsa); +#else + static ECDSA_METHOD *helper_ecdsa = NULL; + if(helper_ecdsa == NULL) { + const ECDSA_METHOD *def = ECDSA_get_default_method(); +# ifdef ECDSA_F_ECDSA_METHOD_NEW + helper_ecdsa = ECDSA_METHOD_new((ECDSA_METHOD *)def); + ECDSA_METHOD_set_name(helper_ecdsa, "ssh-pkcs11-helper-ecdsa"); + ECDSA_METHOD_set_sign(helper_ecdsa, pkcs11_ecdsa_private_sign); +# else + helper_ecdsa = xcalloc(1, sizeof(*helper_ecdsa)); + memcpy(helper_ecdsa, def, sizeof(*helper_ecdsa)); + helper_ecdsa->name = "ssh-pkcs11-helper-ecdsa"; + helper_ecdsa->ecdsa_do_sign = pkcs11_ecdsa_private_sign; +# endif + } + ECDSA_set_method(ecdsa, helper_ecdsa); +#endif + return (0); +} +#endif + static int pkcs11_start_helper(void) { @@ -212,7 +281,15 @@ pkcs11_add_provider(char *name, char *pi __func__, ssh_err(r)); if ((r = sshkey_from_blob(blob, blen, &k)) != 0) fatal("%s: bad key: %s", __func__, ssh_err(r)); - wrap_key(k->rsa); + if(k->type == KEY_RSA) { + wrap_rsa_key(k->rsa); +#ifdef ENABLE_PKCS11_ECDSA + } else if(k->type == KEY_ECDSA) { + wrap_ecdsa_key(k->ecdsa); +#endif /* ENABLE_PKCS11_ECDSA */ + } else { + /* Unsupported type */ + } (*keysp)[i] = k; free(blob); } diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c --- openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa 2018-02-16 13:25:59.427469259 +0100 +++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 13:44:51.270554797 +0100 @@ -32,6 +32,16 @@ #include "openbsd-compat/sys-queue.h" #include +#include +#ifdef OPENSSL_HAS_ECC +#include +#if ((defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ + (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ + (OPENSSL_VERSION_NUMBER >= 0x00010100L) +#define ENABLE_PKCS11_ECDSA 1 +#endif +#endif #define CRYPTOKI_COMPAT #include "pkcs11.h" @@ -67,6 +76,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_pro struct pkcs11_key { struct pkcs11_provider *provider; CK_ULONG slotidx; + CK_ULONG key_type; int (*orig_finish)(RSA *rsa); RSA_METHOD rsa_method; char *keyid; @@ -75,6 +85,9 @@ struct pkcs11_key { }; int pkcs11_interactive = 0; +#ifdef ENABLE_PKCS11_ECDSA +static int pkcs11_key_idx = -1; +#endif /* ENABLE_PKCS11_ECDSA */ /* * This can't be in the ssh-pkcs11-uri, becase we can not depend on @@ -289,6 +302,40 @@ pkcs11_find(struct pkcs11_provider *p, C return (ret); } +int pkcs11_login(struct pkcs11_key *k11, CK_FUNCTION_LIST *f, struct pkcs11_slotinfo *si) { + char *pin = NULL, prompt[1024]; + CK_RV rv; + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (!pkcs11_interactive) { + error("need pin entry%s", (si->token.flags & + CKF_PROTECTED_AUTHENTICATION_PATH) ? + " on reader keypad" : ""); + return (-1); + } + if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) + verbose("Deferring PIN entry to reader keypad."); + else { + snprintf(prompt, sizeof(prompt), + "Enter PIN for '%s': ", si->token.label); + pin = read_passphrase(prompt, RP_ALLOW_EOF); + if (pin == NULL) + return (-1); /* bail out */ + } + rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, + (pin != NULL) ? strlen(pin) : 0); + if (pin != NULL) { + explicit_bzero(pin, strlen(pin)); + free(pin); + } + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { + error("C_Login failed: %lu", rv); + return (-1); + } + si->logged_in = 1; + } + return 0; +} + /* openssl callback doing the actual signing operation */ static int pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, @@ -310,7 +357,6 @@ pkcs11_rsa_private_encrypt(int flen, con {CKA_ID, NULL, 0}, {CKA_SIGN, NULL, sizeof(true_val) } }; - char *pin = NULL, prompt[1024]; int rval = -1; key_filter[0].pValue = &private_key_class; @@ -326,33 +372,8 @@ pkcs11_rsa_private_encrypt(int flen, con } f = k11->provider->module->function_list; si = &k11->provider->module->slotinfo[k11->slotidx]; - if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { - if (!pkcs11_interactive) { - error("need pin entry%s", (si->token.flags & - CKF_PROTECTED_AUTHENTICATION_PATH) ? - " on reader keypad" : ""); - return (-1); - } - if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) - verbose("Deferring PIN entry to reader keypad."); - else { - snprintf(prompt, sizeof(prompt), - "Enter PIN for '%s': ", si->token.label); - pin = read_passphrase(prompt, RP_ALLOW_EOF); - if (pin == NULL) - return (-1); /* bail out */ - } - rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, - (pin != NULL) ? strlen(pin) : 0); - if (pin != NULL) { - explicit_bzero(pin, strlen(pin)); - free(pin); - } - if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { - error("C_Login failed: %lu", rv); - return (-1); - } - si->logged_in = 1; + if(pkcs11_login(k11, f, si)) { + return (-1); } key_filter[1].pValue = k11->keyid; key_filter[1].ulValueLen = k11->keyid_len; @@ -390,6 +411,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider * const RSA_METHOD *def = RSA_get_default_method(); k11 = xcalloc(1, sizeof(*k11)); + k11->key_type = CKK_RSA; k11->provider = provider; provider->refcount++; /* provider referenced by RSA key */ k11->slotidx = slotidx; @@ -415,6 +437,184 @@ pkcs11_rsa_wrap(struct pkcs11_provider * return (0); } +#ifdef ENABLE_PKCS11_ECDSA +static ECDSA_SIG *pkcs11_ecdsa_sign(const unsigned char *dgst, int dgst_len, + const BIGNUM *inv, const BIGNUM *rp, + EC_KEY *ecdsa) { + struct pkcs11_key *k11; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_OBJECT_HANDLE obj; + CK_ULONG tlen = 0; + CK_RV rv; + CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; + CK_BBOOL true_val = CK_TRUE; + CK_MECHANISM mech = { + CKM_ECDSA, NULL_PTR, 0 + }; + CK_ATTRIBUTE key_filter[] = { + {CKA_CLASS, NULL, sizeof(private_key_class) }, + {CKA_ID, NULL, 0}, + {CKA_SIGN, NULL, sizeof(true_val) } + }; + ECDSA_SIG *rval = NULL; + key_filter[0].pValue = &private_key_class; + key_filter[2].pValue = &true_val; + + #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) + if ((k11 = (struct pkcs11_key *)EC_KEY_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { + error("EC_KEY_get_ex_data failed for ecdsa %p", ecdsa); + #else + if ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { + error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa); + #endif + return NULL; + } + if (!k11->provider || !k11->provider->valid) { + error("no pkcs11 (valid) provider for ecdsa %p", ecdsa); + return NULL; + } + f = k11->provider->module->function_list; + si = &k11->provider->module->slotinfo[k11->slotidx]; + if(pkcs11_login(k11, f, si)) { + return NULL; + } + key_filter[1].pValue = k11->keyid; + key_filter[1].ulValueLen = k11->keyid_len; + /* try to find object w/CKA_SIGN first, retry w/o */ + if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && + pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { + error("cannot find private key"); + } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { + error("C_SignInit failed: %lu", rv); + } else { + CK_BYTE_PTR buf = NULL; + BIGNUM *r = NULL, *s = NULL; + int nlen; + /* Make a call to C_Sign to find out the size of the signature */ + rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen); + if (rv != CKR_OK) { + error("C_Sign failed: %lu", rv); + return NULL; + } + if ((buf = xmalloc(tlen)) == NULL) { + error("failure to allocate signature buffer"); + return NULL; + } + rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen); + if (rv != CKR_OK) { + error("C_Sign failed: %lu", rv); + } + + if ((rval = ECDSA_SIG_new()) == NULL || + (r = BN_new()) == NULL || + (s = BN_new()) == NULL) { + error("failure to allocate ECDSA signature"); + } else { + /* + * ECDSA signature is 2 large integers of same size returned + * concatenated by PKCS#11, we separate them to create an + * ECDSA_SIG for OpenSSL. + */ + nlen = tlen / 2; + BN_bin2bn(&buf[0], nlen, r); + BN_bin2bn(&buf[nlen], nlen, s); + ECDSA_SIG_set0(rval, r, s); + } + free(buf); + } + return (rval); +} + +#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) +static EC_KEY_METHOD *get_pkcs11_ecdsa_method(void) { + static EC_KEY_METHOD *pkcs11_ecdsa_method = NULL; + if(pkcs11_key_idx == -1) { + pkcs11_key_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); + } + if (pkcs11_ecdsa_method == NULL) { + const EC_KEY_METHOD *def = EC_KEY_get_default_method(); + pkcs11_ecdsa_method = EC_KEY_METHOD_new(def); + EC_KEY_METHOD_set_sign(pkcs11_ecdsa_method, NULL, NULL, pkcs11_ecdsa_sign); + } +#else +static ECDSA_METHOD *get_pkcs11_ecdsa_method(void) { + static ECDSA_METHOD *pkcs11_ecdsa_method = NULL; + if(pkcs11_key_idx == -1) { + pkcs11_key_idx = ECDSA_get_ex_new_index(0, NULL, NULL, NULL, 0); + } + if(pkcs11_ecdsa_method == NULL) { + const ECDSA_METHOD *def = ECDSA_get_default_method(); + #ifdef ECDSA_F_ECDSA_METHOD_NEW + pkcs11_ecdsa_method = ECDSA_METHOD_new((ECDSA_METHOD *)def); + ECDSA_METHOD_set_name(pkcs11_ecdsa_method, "pkcs11"); + ECDSA_METHOD_set_sign(pkcs11_ecdsa_method, pkcs11_ecdsa_sign); + #else + pkcs11_ecdsa_method = xcalloc(1, sizeof(*pkcs11_ecdsa_method)); + memcpy(pkcs11_ecdsa_method, def, sizeof(*pkcs11_ecdsa_method)); + pkcs11_ecdsa_method->name = "pkcs11"; + pkcs11_ecdsa_method->ecdsa_do_sign = pkcs11_ecdsa_sign; + #endif + } +#endif + return pkcs11_ecdsa_method; +} + +static int +pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ecdsa) +{ + struct pkcs11_key *k11; + k11 = xcalloc(1, sizeof(*k11)); + k11->key_type = CKK_EC; + k11->provider = provider; + provider->refcount++; /* provider referenced by ECDSA key */ + k11->slotidx = slotidx; + /* identify key object on smartcard */ + k11->keyid_len = keyid_attrib->ulValueLen; + if (k11->keyid_len > 0) { + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } + if (label_attrib->ulValueLen > 0 ) { + k11->label = xmalloc(label_attrib->ulValueLen+1); + memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); + k11->label[label_attrib->ulValueLen] = 0; + } + #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) + EC_KEY_set_method(ecdsa, get_pkcs11_ecdsa_method()); + EC_KEY_set_ex_data(ecdsa, pkcs11_key_idx, k11); + #else + ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method()); + ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11); + #endif + return (0); +} +#endif /* ENABLE_PKCS11_ECDSA */ + +int pkcs11_del_key(struct sshkey *key) { +#ifdef ENABLE_PKCS11_ECDSA + if(key->type == KEY_ECDSA) { + struct pkcs11_key *k11 = (struct pkcs11_key *) + #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) + EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx); + #else + ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx); + #endif + if (k11 == NULL) { + error("EC_KEY_get_ex_data failed for ecdsa %p", key->ecdsa); + } else { + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); + free(k11); + } + } +#endif /* ENABLE_PKCS11_ECDSA */ + sshkey_free(key); + return (0); +} + /* remove trailing spaces */ static void rmspace(u_char *buf, size_t len) @@ -482,11 +646,13 @@ static int pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) { - size_t filter_size = 1; + size_t filter_size = 2; + CK_KEY_TYPE pubkey_type = CKK_RSA; CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; CK_ATTRIBUTE pubkey_filter[] = { { CKA_CLASS, NULL, sizeof(pubkey_class) }, + { CKA_KEY_TYPE, NULL, sizeof(pubkey_type) }, { CKA_ID, NULL, 0 }, { CKA_LABEL, NULL, 0 } }; @@ -507,29 +673,60 @@ pkcs11_fetch_keys(struct pkcs11_provider { CKA_SUBJECT, NULL, 0 }, { CKA_VALUE, NULL, 0 } }; +#ifdef ENABLE_PKCS11_ECDSA + CK_KEY_TYPE ecdsa_type = CKK_EC; + CK_ATTRIBUTE ecdsa_filter[] = { + { CKA_CLASS, NULL, sizeof(pubkey_class) }, + { CKA_KEY_TYPE, NULL, sizeof(ecdsa_type) }, + { CKA_ID, NULL, 0 }, + { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE ecdsa_attribs[] = { + { CKA_ID, NULL, 0 }, + { CKA_LABEL, NULL, 0 }, + { CKA_EC_PARAMS, NULL, 0 }, + { CKA_EC_POINT, NULL, 0 } + }; + ecdsa_filter[0].pValue = &pubkey_class; + ecdsa_filter[1].pValue = &ecdsa_type; +#endif /* ENABLE_PKCS11_ECDSA */ pubkey_filter[0].pValue = &pubkey_class; + pubkey_filter[1].pValue = &pubkey_type; cert_filter[0].pValue = &cert_class; if (uri->id != NULL) { pubkey_filter[filter_size].pValue = uri->id; pubkey_filter[filter_size].ulValueLen = uri->id_len; - cert_filter[filter_size].pValue = uri->id; - cert_filter[filter_size].ulValueLen = uri->id_len; +#ifdef ENABLE_PKCS11_ECDSA + ecdsa_filter[filter_size].pValue = uri->id; + ecdsa_filter[filter_size].ulValueLen = uri->id_len; +#endif /* ENABLE_PKCS11_ECDSA */ + cert_filter[filter_size-1].pValue = uri->id; + cert_filter[filter_size-1].ulValueLen = uri->id_len; filter_size++; } if (uri->object != NULL) { pubkey_filter[filter_size].pValue = uri->object; pubkey_filter[filter_size].ulValueLen = strlen(uri->object); pubkey_filter[filter_size].type = CKA_LABEL; - cert_filter[filter_size].pValue = uri->object; - cert_filter[filter_size].ulValueLen = strlen(uri->object); - cert_filter[filter_size].type = CKA_LABEL; +#ifdef ENABLE_PKCS11_ECDSA + ecdsa_filter[filter_size].pValue = uri->object; + ecdsa_filter[filter_size].ulValueLen = strlen(uri->object); + ecdsa_filter[filter_size].type = CKA_LABEL; +#endif /* ENABLE_PKCS11_ECDSA */ + cert_filter[filter_size-1].pValue = uri->object; + cert_filter[filter_size-1].ulValueLen = strlen(uri->object); + cert_filter[filter_size-1].type = CKA_LABEL; filter_size++; } if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, pubkey_attribs, keysp, nkeys) < 0 || - pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, +#ifdef ENABLE_PKCS11_ECDSA + pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size, + ecdsa_attribs, keysp, nkeys) < 0|| +#endif /* ENABLE_PKCS11_ECDSA */ + pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1, cert_attribs, keysp, nkeys) < 0) return (-1); return (0); @@ -553,6 +746,11 @@ pkcs11_fetch_keys_filter(struct pkcs11_p { struct sshkey *key; RSA *rsa; +#ifdef ENABLE_PKCS11_ECDSA + EC_KEY *ecdsa; +#else + void *ecdsa; +#endif /* ENABLE_PKCS11_ECDSA */ X509 *x509; EVP_PKEY *evp = NULL; int i; @@ -608,6 +806,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p * or ID, label, subject and value for certificates. */ rsa = NULL; +#ifdef ENABLE_PKCS11_ECDSA + ecdsa = NULL; +#endif /* ENABLE_PKCS11_ECDSA */ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) != CKR_OK) { error("C_GetAttributeValue failed: %lu", rv); @@ -620,6 +821,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_p rsa->e = BN_bin2bn(attribs[3].pValue, attribs[3].ulValueLen, NULL); } +#ifdef ENABLE_PKCS11_ECDSA + } else if (attribs[2].type == CKA_EC_PARAMS ) { + if ((ecdsa = EC_KEY_new()) == NULL) { + error("EC_KEY_new failed"); + } else { + const unsigned char *ptr1 = attribs[2].pValue; + const unsigned char *ptr2 = attribs[3].pValue; + CK_ULONG len1 = attribs[2].ulValueLen; + CK_ULONG len2 = attribs[3].ulValueLen; + ASN1_OCTET_STRING *point = NULL; + + /* + * CKA_EC_PARAMS contains the curve parameters of the key + * either referenced as an OID or directly with all values. + * CKA_EC_POINT contains the point (public key) on the curve. + * The point is should be returned inside a DER-encoded + * ASN.1 OCTET STRING value (but some implementation). + */ + if ((point = d2i_ASN1_OCTET_STRING(NULL, &ptr2, len2))) { + /* Pointing to OCTET STRING content */ + ptr2 = point->data; + len2 = point->length; + } else { + /* No OCTET STRING */ + ptr2 = attribs[3].pValue; + } + + if((d2i_ECParameters(&ecdsa, &ptr1, len1) == NULL) || + (o2i_ECPublicKey(&ecdsa, &ptr2, len2) == NULL)) { + EC_KEY_free(ecdsa); + ecdsa = NULL; + error("EC public key parsing failed"); + } + + if(point) { + ASN1_OCTET_STRING_free(point); + } + } +#endif /* ENABLE_PKCS11_ECDSA */ } else { cp = attribs[3].pValue; if ((x509 = X509_new()) == NULL) { @@ -639,13 +879,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p X509_free(x509); EVP_PKEY_free(evp); } - if (rsa && rsa->n && rsa->e && - pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); - key->rsa = rsa; - key->type = KEY_RSA; - key->flags |= SSHKEY_FLAG_EXT; + key = NULL; + if (rsa || ecdsa) { + if (rsa && rsa->n && rsa->e && + pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + key->rsa = rsa; + key->type = KEY_RSA; + key->flags |= SSHKEY_FLAG_EXT; +#ifdef ENABLE_PKCS11_ECDSA + } else if(ecdsa && pkcs11_ecdsa_wrap(p, slotidx, &attribs[0], &attribs[1], ecdsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + key->ecdsa = ecdsa; + key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); + key->type = KEY_ECDSA; + key->flags |= SSHKEY_FLAG_EXT; +#endif /* ENABLE_PKCS11_ECDSA */ + } + } + + if(key) { if (pkcs11_key_included(keysp, nkeys, key)) { sshkey_free(key); } else { @@ -658,6 +913,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p } } else if (rsa) { RSA_free(rsa); +#ifdef ENABLE_PKCS11_ECDSA + } else if (ecdsa) { + EC_KEY_free(ecdsa); +#endif /* ENABLE_PKCS11_ECDSA */ } for (i = 0; i < nattribs; i++) free(attribs[i].pValue); diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-helper.c --- openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa 2017-10-02 21:34:26.000000000 +0200 +++ openssh-7.6p1/ssh-pkcs11-helper.c 2018-02-16 13:25:59.428469265 +0100 @@ -24,6 +24,17 @@ #include "openbsd-compat/sys-queue.h" +#include +#ifdef OPENSSL_HAS_ECC +#include +#if ((defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ + (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ + (OPENSSL_VERSION_NUMBER >= 0x00010100L) +#define ENABLE_PKCS11_ECDSA 1 +#endif +#endif + #include #include #include @@ -80,7 +90,7 @@ del_keys_by_name(char *name) if (!strcmp(ki->providername, name)) { TAILQ_REMOVE(&pkcs11_keylist, ki, next); free(ki->providername); - sshkey_free(ki->key); + pkcs11_del_key(ki->key); free(ki); } } @@ -164,6 +174,20 @@ process_del(void) sshbuf_free(msg); } +#ifdef ENABLE_PKCS11_ECDSA +static u_int EC_KEY_order_size(EC_KEY *key) +{ + const EC_GROUP *group = EC_KEY_get0_group(key); + BIGNUM *order = BN_new(); + u_int nbytes = 0; + if ((group != NULL) && (order != NULL) && EC_GROUP_get_order(group, order, NULL)) { + nbytes = BN_num_bytes(order); + } + BN_clear_free(order); + return nbytes; +} +#endif /* ENABLE_PKCS11_ECDSA */ + static void process_sign(void) { @@ -180,14 +204,38 @@ process_sign(void) else { if ((found = lookup_key(key)) != NULL) { #ifdef WITH_OPENSSL - int ret; - - slen = RSA_size(key->rsa); - signature = xmalloc(slen); - if ((ret = RSA_private_encrypt(dlen, data, signature, - found->rsa, RSA_PKCS1_PADDING)) != -1) { - slen = ret; - ok = 0; + if(found->type == KEY_RSA) { + int ret; + slen = RSA_size(key->rsa); + signature = xmalloc(slen); + if ((ret = RSA_private_encrypt(dlen, data, signature, + found->rsa, RSA_PKCS1_PADDING)) != -1) { + slen = ret; + ok = 0; + } +#ifdef ENABLE_PKCS11_ECDSA + } else if(found->type == KEY_ECDSA) { + ECDSA_SIG *sig; + const BIGNUM *r = NULL, *s = NULL; + if ((sig = ECDSA_do_sign(data, dlen, found->ecdsa)) != NULL) { + /* PKCS11 2.3.1 recommends both r and s to have the order size for + backward compatiblity */ + ECDSA_SIG_get0(sig, &r, &s); + u_int o_len = EC_KEY_order_size(found->ecdsa); + u_int r_len = BN_num_bytes(r); + u_int s_len = BN_num_bytes(s); + if (o_len > 0 && r_len <= o_len && s_len <= o_len) { + signature = xcalloc(2, o_len); + BN_bn2bin(r, signature + o_len - r_len); + BN_bn2bin(s, signature + (2 * o_len) - s_len); + slen = 2 * o_len; + ok = 0; + } + ECDSA_SIG_free(sig); + } +#endif /* ENABLE_PKCS11_ECDSA */ + } else { + /* Unsupported type */ } #endif /* WITH_OPENSSL */ } diff -up openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.h --- openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa 2018-02-16 13:25:59.429469272 +0100 +++ openssh-7.6p1/ssh-pkcs11.h 2018-02-16 13:45:29.623800048 +0100 @@ -20,6 +20,7 @@ int pkcs11_init(int); void pkcs11_terminate(void); int pkcs11_add_provider(char *, char *, struct sshkey ***); +int pkcs11_del_key(struct sshkey *); int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); int pkcs11_del_provider(char *); int pkcs11_uri_write(const struct sshkey *, FILE *); diff -up openssh-7.6p1/ssh-pkcs11.c.old openssh-7.6p1/ssh-pkcs11.c --- openssh-7.6p1/ssh-pkcs11.c.old 2018-02-16 16:43:08.861520255 +0100 +++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 16:56:35.312601451 +0100 @@ -917,13 +917,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) == NULL) { error("d2i_X509 failed"); - } else if ((evp = X509_get_pubkey(x509)) == NULL || - evp->type != EVP_PKEY_RSA || - evp->pkey.rsa == NULL) { - debug("X509_get_pubkey failed or no rsa"); - } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) - == NULL) { - error("RSAPublicKey_dup"); + } else if ((evp = X509_get_pubkey(x509)) == NULL) { + debug("X509_get_pubkey failed"); + } else { + switch (evp->type) { + case EVP_PKEY_RSA: + if (evp->pkey.rsa == NULL) + debug("Missing RSA key"); + else if ((rsa = RSAPublicKey_dup( + evp->pkey.rsa)) == NULL) + error("RSAPublicKey_dup failed"); + break; + case EVP_PKEY_EC: + if (evp->pkey.ecdsa == NULL) + debug("Missing ECDSA key"); + else if ((ecdsa = EC_KEY_dup( + evp->pkey.ecdsa)) == NULL) + error("EC_KEY_dup failed"); + break; + default: + debug("not a RSA or ECDSA key"); + break; + } } X509_free(x509); EVP_PKEY_free(evp);