/** * ECDH key derivation method (replaces ossl_ecdh_compute_key) * Implementation for OpenSSL 1.1.0-pre4 and later * * @param out derived key * @param outlen derived key length * @param peer_point public key point * @param ecdh private key * @return 1 on success or 0 on error */ static int pkcs11_ec_ckey(unsigned char **out, size_t *outlen, const EC_POINT *peer_point, const EC_KEY *ecdh) { PKCS11_KEY *key; CK_ECDH1_DERIVE_PARAMS *parms; unsigned char *buf = NULL; size_t buflen; int rv; key = (PKCS11_KEY *)EC_KEY_get_ex_data(ecdh, ec_ex_index); if (key == NULL) /* The private key is not handled by PKCS#11 */ return ossl_ecdh_compute_key(out, outlen, peer_point, ecdh); /* TODO: Add an atfork check */ /* both peer and ecdh use same group parameters */ parms = pkcs11_ecdh_params_alloc(EC_KEY_get0_group(ecdh), peer_point); if (parms == NULL) return 0; rv = pkcs11_ecdh_derive(&buf, &buflen, CKM_ECDH1_DERIVE, parms, NULL, key); pkcs11_ecdh_params_free(parms); if (rv < 0) return 0; *out = buf; *outlen = buflen; return 1; }
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_data *pkd = NULL; CK_MECHANISM mech = { CKM_ECDSA, NULL_PTR, 0 }; CK_ULONG tlen = 0; CK_RV rv; #if OPENSSL_VERSION_NUMBER < 0x10100000L pkd = ECDSA_get_ex_data(ecdsa, pkcs11_ecdsa_key_idx); #else pkd = EC_KEY_get_ex_data(ecdsa, pkcs11_ecdsa_key_idx); #endif if((pkd != NULL) && ((rv = pkd->funcs->C_SignInit(pkd->session, &mech, pkd->key)) == CKR_OK)) { CK_BYTE_PTR buf = NULL; ECDSA_SIG *rval; BIGNUM *r, *s; int nlen; /* Make a call to C_Sign to find out the size of the signature */ rv = pkd->funcs->C_Sign(pkd->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen); if (rv != CKR_OK) { return NULL; } if ((buf = malloc(tlen)) == NULL) { return NULL; } rv = pkd->funcs->C_Sign(pkd->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen); if (rv != CKR_OK) { free(buf); return NULL; } if ((rval = ECDSA_SIG_new()) != NULL) { /* * 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; #if OPENSSL_VERSION_NUMBER >= 0x10100000L ECDSA_SIG_get0(&r, &s, rval); #else r = rval->r; s = rval->s; #endif BN_bin2bn(&buf[0], nlen, r); BN_bin2bn(&buf[nlen], nlen, s); } free(buf); return rval; } else { return NULL; } }
PKCS11_KEY *pkcs11_get_ex_data_ec(const EC_KEY *ec) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) return EC_KEY_get_ex_data(ec, ec_ex_index); #else return ECDSA_get_ex_data((EC_KEY *)ec, ec_ex_index); #endif }
static ECDSA_SIG * pkcs11_ecdsa_do_sign(const unsigned char *dgst, int dlen, const BIGNUM *inv, const BIGNUM *r, EC_KEY * ec) { unsigned char sigret[512]; /* HACK for now */ ECDSA_SIG * sig = NULL; PKCS11_KEY * key = NULL; unsigned int siglen; int nLen = 48; /* HACK */ int rv; #if OPENSSL_VERSION_NUMBER >= 0x10100000L key = (PKCS11_KEY *) EC_KEY_get_ex_data(ec, ec_key_ex_index); #else key = (PKCS11_KEY *) ECDSA_get_ex_data(ec, ecdsa_ex_index); #endif if (key == NULL) return NULL; siglen = sizeof(sigret); rv = PKCS11_ecdsa_sign(dgst, dlen, sigret, &siglen, key); nLen = siglen / 2; if (rv > 0) { sig = ECDSA_SIG_new(); if (sig) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* * OpenSSL 1.1 does not have a way to allocate r and s * in ECDSA_SIG as it is now hidden. * Will us dummy ASN1 so r and s are allocated then * use ECDSA_SIG_get0 to get access to r and s * can then update r annd s */ const unsigned char *a; unsigned char dasn1[8] = {0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00}; BIGNUM *r; BIGNUM *s; a = dasn1; d2i_ECDSA_SIG(&sig, &a, 8); ECDSA_SIG_get0(&r, &s, sig); BN_bin2bn(&sigret[0], nLen, r); BN_bin2bn(&sigret[nLen], nLen, s); #else BN_bin2bn(&sigret[0], nLen, sig->r); BN_bin2bn(&sigret[nLen], nLen, sig->s); #endif } } return sig; }
/** * ECDSA signing method (replaces ossl_ecdsa_sign_sig) * * @param dgst hash value to sign * @param dlen length of the hash value * @param kinv precomputed inverse k (from the sign_setup method) * @param rp precomputed rp (from the sign_setup method) * @param ec private EC signing key * @return pointer to a ECDSA_SIG structure or NULL if an error occurred */ static ECDSA_SIG *pkcs11_ecdsa_sign_sig(const unsigned char *dgst, int dlen, const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *ec) { unsigned char sigret[512]; /* HACK for now */ ECDSA_SIG *sig; PKCS11_KEY *key; unsigned int siglen; BIGNUM *r, *s, *order; (void)kinv; /* Precomputed values are not used for PKCS#11 */ (void)rp; /* Precomputed values are not used for PKCS#11 */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L key = (PKCS11_KEY *)EC_KEY_get_ex_data(ec, ec_ex_index); #else key = (PKCS11_KEY *)ECDSA_get_ex_data(ec, ec_ex_index); #endif if (key == NULL) { PKCS11err(PKCS11_F_PKCS11_EC_KEY_SIGN, PKCS11_ALIEN_KEY); return NULL; } /* TODO: Add an atfork check */ /* Truncate digest if its byte size is longer than needed */ order = BN_new(); if (order) { const EC_GROUP *group = EC_KEY_get0_group(ec); if (group && EC_GROUP_get_order(group, order, NULL)) { int klen = BN_num_bits(order); if (klen < 8*dlen) dlen = (klen+7)/8; } BN_free(order); } siglen = sizeof sigret; if (pkcs11_ecdsa_sign(dgst, dlen, sigret, &siglen, key) <= 0) return NULL; sig = ECDSA_SIG_new(); if (sig == NULL) return NULL; #if OPENSSL_VERSION_NUMBER >= 0x10100000L ECDSA_SIG_get0(&r, &s, sig); #else r = sig->r; s = sig->s; #endif BN_bin2bn(sigret, siglen/2, r); BN_bin2bn(sigret + siglen/2, siglen/2, s); return sig; }
/** * ECDH key derivation method (replaces ossl_ecdh_compute_key) * Implementation for OpenSSL 1.1.0-pre3 and earlier * * @param out derived key * @param outlen derived key length * @param peer_point public key point * @param ecdh private key * @param KCF key derivation function * @return the length of the derived key or -1 if an error occurred */ static int pkcs11_ec_ckey(void *out, size_t outlen, const EC_POINT *peer_point, const EC_KEY *ecdh, void *(*KDF)(const void *, size_t, void *, size_t *)) { PKCS11_KEY *key; CK_ECDH1_DERIVE_PARAMS *parms; unsigned char *buf = NULL; size_t buflen; int rv; #if OPENSSL_VERSION_NUMBER >= 0x10100000L key = (PKCS11_KEY *)EC_KEY_get_ex_data(ecdh, ec_ex_index); #else key = (PKCS11_KEY *)ECDSA_get_ex_data((EC_KEY *)ecdh, ec_ex_index); #endif if (key == NULL) /* The private key is not handled by PKCS#11 */ return ossl_ecdh_compute_key(out, outlen, peer_point, ecdh, KDF); /* TODO: Add an atfork check */ /* both peer and ecdh use same group parameters */ parms = pkcs11_ecdh_params_alloc(EC_KEY_get0_group(ecdh), peer_point); if (parms == NULL) return -1; rv = pkcs11_ecdh_derive(&buf, &buflen, CKM_ECDH1_DERIVE, parms, NULL, key); pkcs11_ecdh_params_free(parms); if (rv < 0) return -1; if (KDF) { if (KDF(buf, buflen, out, &outlen) == NULL) { OPENSSL_free(buf); return -1; } } else { if (outlen > buflen) outlen = buflen; memcpy(out, buf, outlen); } OPENSSL_free(buf); return outlen; }
/* Our version of the ossl_ecdh_compute_key replaced in the EC_KEY_METHOD */ static int pkcs11_ec_ckey(void *out, size_t outlen, const EC_POINT *ecpointpeer, const EC_KEY *ecdh, void *(*KDF) (const void *in, size_t inlen, void *out, size_t *outlen)) { int ret = -1; size_t buflen; unsigned char *buf = NULL; size_t peerbuflen; unsigned char *peerbuf = NULL; const EC_GROUP *ecgroup = NULL; const EC_POINT *ecpoint = NULL; CK_ECDH1_DERIVE_PARAMS ecdh_parms; PKCS11_KEY * key = NULL; key = (PKCS11_KEY *) EC_KEY_get_ex_data(ecdh, ec_key_ex_index); if (key == NULL) { ret -1; goto err; } /* both peer and ecdh use same group parameters */ ecgroup = EC_KEY_get0_group(ecdh); buflen = (EC_GROUP_get_degree(ecgroup) + 7) / 8; peerbuflen = 2*buflen + 1; peerbuf = OPENSSL_malloc(peerbuflen); if (peerbuf == NULL) { ret = -1; goto err; } ecdh_parms.kdf = CKD_NULL; ecdh_parms.ulSharedDataLen = 0; ecdh_parms.pSharedData = NULL; ecdh_parms.ulPublicDataLen = peerbuflen; ret = EC_POINT_point2oct(ecgroup, ecpointpeer, POINT_CONVERSION_UNCOMPRESSED, peerbuf, peerbuflen,NULL); ecdh_parms.ulPublicDataLen = peerbuflen; ecdh_parms.pPublicData = peerbuf; ret = pkcs11_ecdh_derive_internal(&buf, &buflen, CKM_ECDH1_DERIVE, (const void *)&ecdh_parms, NULL, key); if (KDF != 0) { if (KDF(buf, buflen, out, &outlen) == NULL) { ret -1; goto err; } ret = outlen; } else { if (outlen > buflen) outlen = buflen; memcpy(out, buf, outlen); ret = outlen; } err: OPENSSL_free(buf); return (ret); }