Add basic CSR API in crypto.h. Signed-off-by: Cedric Izoard <cedric.izoard@xxxxxxxxxxxx> --- Probably not the best place to add this. --- src/common/dpp_crypto.c | 143 +++++++--------------------- src/crypto/crypto.h | 101 ++++++++++++++++++++ src/crypto/crypto_openssl.c | 180 ++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 108 deletions(-) diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index 4b387f6b0..5092e98e3 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -19,6 +19,7 @@ #include "crypto/random.h" #include "crypto/sha384.h" #include "crypto/sha512.h" +#include "tls/asn1.h" #include "dpp.h" #include "dpp_i.h" @@ -2030,19 +2031,15 @@ void dpp_pfs_free(struct dpp_pfs *pfs) struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name) { - X509_REQ *req = NULL; + struct crypto_csr *csr = NULL; struct wpabuf *buf = NULL; - unsigned char *der; - int der_len; struct crypto_ec_key *key; - const EVP_MD *sign_md; unsigned int hash_len = auth->curve->hash_len; struct wpabuf * priv_key; - BIO *out = NULL; u8 cp[DPP_CP_LEN]; - char *password; + char *password = NULL; size_t password_len; - int res; + int hash_sign_algo; /* TODO: use auth->csrattrs */ @@ -2056,22 +2053,13 @@ struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name) wpabuf_free(auth->priv_key); auth->priv_key = priv_key; - req = X509_REQ_new(); - if (!req || !X509_REQ_set_pubkey(req, (EVP_PKEY *)key)) + csr = crypto_csr_init(); + if (!csr || crypto_csr_set_ec_public_key(csr, key)) goto fail; - if (name) { - X509_NAME *n; - - n = X509_REQ_get_subject_name(req); - if (!n) - goto fail; - - if (X509_NAME_add_entry_by_txt( - n, "CN", MBSTRING_UTF8, - (const unsigned char *) name, -1, -1, 0) != 1) - goto fail; - } + if (name && crypto_csr_set_name(csr, CSR_NAME_CN, (const u8 *)name, + os_strlen(name))) + goto fail; /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len, @@ -2085,43 +2073,31 @@ struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name) if (!password) goto fail; - res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword, - V_ASN1_UTF8STRING, - (const unsigned char *) password, - password_len); - bin_clear_free(password, password_len); - if (!res) + if (crypto_csr_set_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD, + ASN1_TAG_UTF8STRING, (const u8*) password, + password_len)) goto fail; - /* TODO */ - /* TODO: hash func selection based on csrAttrs */ if (hash_len == SHA256_MAC_LEN) { - sign_md = EVP_sha256(); + hash_sign_algo = CRYPTO_HASH_ALG_SHA256; } else if (hash_len == SHA384_MAC_LEN) { - sign_md = EVP_sha384(); + hash_sign_algo = CRYPTO_HASH_ALG_SHA384; } else if (hash_len == SHA512_MAC_LEN) { - sign_md = EVP_sha512(); + hash_sign_algo = CRYPTO_HASH_ALG_SHA512; } else { wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); goto fail; } - if (!X509_REQ_sign(req, (EVP_PKEY *)key, sign_md)) + buf = crypto_csr_sign(csr, key, hash_sign_algo); + if (!buf) goto fail; - - der = NULL; - der_len = i2d_X509_REQ(req, &der); - if (der_len < 0) - goto fail; - buf = wpabuf_alloc_copy(der, der_len); - OPENSSL_free(der); - wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf); fail: - BIO_free_all(out); - X509_REQ_free(req); + bin_clear_free(password, password_len); + crypto_csr_deinit(csr); return buf; } @@ -2214,90 +2190,41 @@ fail: } -int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr) +int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csrbuf) { - X509_REQ *req; - const unsigned char *pos; - EVP_PKEY *pkey; - int res, loc, ret = -1; - X509_ATTRIBUTE *attr; - ASN1_TYPE *type; - ASN1_STRING *str; - unsigned char *utf8 = NULL; + struct crypto_csr *csr = NULL; + const u8 *attr; + size_t attr_len; + int attr_type; unsigned char *cp = NULL; size_t cp_len; u8 exp_cp[DPP_CP_LEN]; unsigned int hash_len = auth->curve->hash_len; + int ret = -1; - pos = wpabuf_head(csr); - req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr)); - if (!req) { - wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR"); - return -1; - } - - pkey = X509_REQ_get_pubkey(req); - if (!pkey) { - wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR"); - goto fail; - } - - res = X509_REQ_verify(req, pkey); - EVP_PKEY_free(pkey); - if (res != 1) { - wpa_printf(MSG_DEBUG, - "DPP: CSR does not have a valid signature"); - goto fail; - } - - loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1); - if (loc < 0) { + csr = crypto_csr_verify(wpabuf_head(csrbuf), wpabuf_len(csrbuf)); + if (!csr) { wpa_printf(MSG_DEBUG, - "DPP: CSR does not include challengePassword"); + "DPP: CSR invalid or invalid signature"); goto fail; } - attr = X509_REQ_get_attr(req, loc); + attr = crypto_csr_get_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD, &attr_len, &attr_type); if (!attr) { - wpa_printf(MSG_DEBUG, - "DPP: Could not get challengePassword attribute"); - goto fail; - } - - type = X509_ATTRIBUTE_get0_type(attr, 0); - if (!type) { - wpa_printf(MSG_DEBUG, - "DPP: Could not get challengePassword attribute type"); + wpa_printf(MSG_DEBUG, "DPP: CSR does not include challengePassword"); goto fail; } - - res = ASN1_TYPE_get(type); /* This is supposed to be UTF8String, but allow other strings as well * since challengePassword is using ASCII (base64 encoded). */ - if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING && - res != V_ASN1_IA5STRING) { + if (attr_type != ASN1_TAG_UTF8STRING && attr_type != ASN1_TAG_PRINTABLESTRING && + attr_type != ASN1_TAG_IA5STRING) { wpa_printf(MSG_DEBUG, "DPP: Unexpected challengePassword attribute type %d", - res); - goto fail; - } - - str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL); - if (!str) { - wpa_printf(MSG_DEBUG, - "DPP: Could not get ASN.1 string for challengePassword"); - goto fail; - } - - res = ASN1_STRING_to_UTF8(&utf8, str); - if (res < 0) { - wpa_printf(MSG_DEBUG, - "DPP: Could not get UTF8 version of challengePassword"); + attr_type); goto fail; } - cp = base64_decode((const char *) utf8, res, &cp_len); - OPENSSL_free(utf8); + cp = base64_decode((const char *)attr, attr_len, &cp_len); if (!cp) { wpa_printf(MSG_DEBUG, "DPP: Could not base64 decode challengePassword"); @@ -2328,7 +2255,7 @@ int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr) ret = 0; fail: os_free(cp); - X509_REQ_free(req); + crypto_csr_deinit(csr); return ret; } diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 78e7045ca..b5c5f0c36 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1169,4 +1169,105 @@ void crypto_ec_key_debug_print(const struct crypto_ec_key *key, const char *title); +/** + * struct crypto_csr - Certification Signing Request + * + * Internal data structure for CSR. The contents is specific to the used + * crypto library. + * For now it is assumed that only an EC public key can be used + */ +struct crypto_csr; + +/** + * enum crypto_csr_name - CSR name type + */ +enum crypto_csr_name { + CSR_NAME_CN, + CSR_NAME_SN, + CSR_NAME_C, + CSR_NAME_O, + CSR_NAME_OU, +}; + +/** + * enum crypto_csr_attr - CSR attribute + */ +enum crypto_csr_attr { + CSR_ATTR_CHALLENGE_PASSWORD, +}; + +/** + * crypto_csr_init - Initialize empty CSR + * Returns: Pointer to CSR data or %NULL on failure + */ +struct crypto_csr * crypto_csr_init(); + +/** + * crypto_csr_verify - Initialize CSR from CertificationRequest + * @req: DER encoding of ASN.1 CertificationRequest + * @len: Length of @req buffer + * + * Returns: Pointer to CSR data or %NULL on failure or if signature is invalid + */ +struct crypto_csr * crypto_csr_verify(const u8 *req, size_t len); + +/** + * crypto_csr_deinit - Free CSR structure + * @csr: CSR structure from @crypto_csr_init() or crypto_csr_verify() + */ +void crypto_csr_deinit(struct crypto_csr *csr); + +/** + * crypto_csr_set_ec_public_key - Set public Key in CSR + * @csr: CSR structure from @crypto_csr_init() + * @key: EC Public key to set as Public key in the CSR + * Returns: 0 on success, -1 on failure + */ +int crypto_csr_set_ec_public_key(struct crypto_csr *csr, struct crypto_ec_key *key); + +/** + * crypto_csr_set_name - Set name in CSR + * @csr: CSR structure from @crypto_csr_init() + * @type: Name to add in the EC Public key to set as Public key in the CSR + * @name: UTF8 string to write in the CSR + * @len: length of @name buffer + * Returns: 0 on success, -1 on failure + */ +int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, + const u8 *name, size_t len); + +/** + * crypto_csr_set_attribute - Set attribute in CSR + * @csr: CSR structure from @crypto_csr_init() + * @attr: Atribute identifier + * @attr_type: ASN.1 type of @value buffer + * @value: Attribute value + * @len: length of @value buffer + * Returns: 0 on success, -1 on failure + */ +int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr, + int attr_type, const u8 *value, size_t len); + +/** + * crypto_csr_get_attribute - Get attribute from CSR + * @csr: CSR structure from @crypto_csr_verify() + * @attr: Updated with atribute identifier + * @len: Updated with length of returned buffer + * @type: ASN.1 type of the attribute buffer + * Returns: Type, length and Pointer on atrtibute value or %NULL on failure + */ +const u8 *crypto_csr_get_attribute(struct crypto_csr *csr, + enum crypto_csr_attr attr, + size_t *len, int *type); + +/** + * crypto_csr_sign - Sign CSR and return ASN.1 CertificationRequest + * @csr: CSR structure from @crypto_csr_init() + * @key: Private key to sign the CSR (for now ony EC key are supported) + * @algo: Hash algorithm to use for the signature + * Returns: DER encoding of ASN.1 CertificationRequest for the CSR or %NULL on failure + */ +struct wpabuf *crypto_csr_sign(struct crypto_csr *csr, struct crypto_ec_key *key, + enum crypto_hash_alg algo); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 26705ff50..abcfeef38 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -108,6 +108,10 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, } #endif /* CONFIG_ECC */ +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} #endif /* OpenSSL version < 1.1.0 */ static BIGNUM * get_group5_prime(void) @@ -2829,4 +2833,180 @@ void crypto_ec_key_debug_print(const struct crypto_ec_key *key, BIO_free(out); } + +struct crypto_csr * crypto_csr_init() +{ + return (struct crypto_csr *)X509_REQ_new(); +} + + +struct crypto_csr * crypto_csr_verify(const u8 *req, size_t len) +{ + X509_REQ *csr; + EVP_PKEY *pkey = NULL; + const u8 *der = req; + + csr = d2i_X509_REQ(NULL, &der, len); + if (!csr) + return NULL; + + pkey = X509_REQ_get_pubkey((X509_REQ *)csr); + if (!pkey) + goto fail; + + if (X509_REQ_verify((X509_REQ *)csr, pkey) != 1) + goto fail; + + return (struct crypto_csr *)csr; +fail: + X509_REQ_free(csr); + return NULL; +} + + +void crypto_csr_deinit(struct crypto_csr *csr) +{ + X509_REQ_free((X509_REQ *)csr); +} + + +int crypto_csr_set_ec_public_key(struct crypto_csr *csr, struct crypto_ec_key *key) +{ + if (!X509_REQ_set_pubkey((X509_REQ *)csr, (EVP_PKEY *)key)) + return -1; + + return 0; +} + + +int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, + const u8 *name, size_t len) +{ + X509_NAME *n; + int nid; + + switch (type) { + case CSR_NAME_CN: + nid = NID_commonName; + break; + case CSR_NAME_SN: + nid = NID_surname; + break; + case CSR_NAME_C: + nid = NID_countryName; + break; + case CSR_NAME_O: + nid = NID_organizationName; + break; + case CSR_NAME_OU: + nid = NID_organizationalUnitName; + break; + default: + return -1; + } + + n = X509_REQ_get_subject_name((X509_REQ *)csr); + if (!n) + return -1; + + if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8, + name, len, -1, 0)) + return -1; + + return 0; +} + + +int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr, + int attr_type, const u8 *value, size_t len) +{ + int nid; + + switch(attr) { + case CSR_ATTR_CHALLENGE_PASSWORD: + nid = NID_pkcs9_challengePassword; + break; + default: + return -1; + } + + if (!X509_REQ_add1_attr_by_NID((X509_REQ *)csr, nid, attr_type, value, len)) + return -1; + + return 0; +} + + +const u8 *crypto_csr_get_attribute(struct crypto_csr *csr, + enum crypto_csr_attr attr, + size_t *len, int *type) +{ + X509_ATTRIBUTE *attrib; + ASN1_TYPE *attrib_type; + ASN1_STRING *data; + int loc; + int nid; + + switch(attr) { + case CSR_ATTR_CHALLENGE_PASSWORD: + nid = NID_pkcs9_challengePassword; + break; + default: + return NULL; + } + + loc = X509_REQ_get_attr_by_NID((X509_REQ *)csr, nid, -1); + if (loc < 0) { + return NULL; + } + + attrib = X509_REQ_get_attr((X509_REQ *)csr, loc); + if (!attrib) { + return NULL; + } + + + attrib_type = X509_ATTRIBUTE_get0_type(attrib, 0); + *type = ASN1_TYPE_get(attrib_type); + data = X509_ATTRIBUTE_get0_data(attrib, 0, *type, NULL); + *len = ASN1_STRING_length(data); + return ASN1_STRING_get0_data(data); +} + + +struct wpabuf * crypto_csr_sign(struct crypto_csr *csr, struct crypto_ec_key *key, + enum crypto_hash_alg algo) +{ + const EVP_MD *sign_md; + struct wpabuf *buf = NULL; + unsigned char *der = NULL; + int der_len; + + switch(algo) { + case CRYPTO_HASH_ALG_SHA256: + sign_md = EVP_sha256(); + break; + case CRYPTO_HASH_ALG_SHA384: + sign_md = EVP_sha384(); + break; + case CRYPTO_HASH_ALG_SHA512: + sign_md = EVP_sha512(); + break; + default: + return NULL; + } + + if (!X509_REQ_sign((X509_REQ *)csr, (EVP_PKEY *)key, sign_md)) + return NULL; + + der_len = i2d_X509_REQ((X509_REQ *)csr, &der); + if (der_len < 0) + return NULL; + + buf = wpabuf_alloc_copy(der, der_len); + OPENSSL_free(der); + + return buf; +} + #endif /* CONFIG_ECC */ -- 2.17.0 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap