From d51939f2c4b55246fc8794021e234a38e7f9dd2f Mon Sep 17 00:00:00 2001 From: Cedric Izoard Date: Mon, 28 Jun 2021 18:25:37 +0200 Subject: [PATCH] DPP: Move CSR routines to use crypto.h Add basic CSR API in crypto.h. Signed-off-by: Cedric Izoard --- src/common/dpp_crypto.c | 148 ++++++++--------------------- src/crypto/crypto.h | 102 ++++++++++++++++++++ src/crypto/crypto_openssl.c | 184 ++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 110 deletions(-) diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index b90e4db42..ceb621a0b 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -21,6 +21,7 @@ #include "crypto/random.h" #include "crypto/sha384.h" #include "crypto/sha512.h" +#include "tls/asn1.h" #include "dpp.h" #include "dpp_i.h" @@ -2156,19 +2157,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 */ @@ -2182,22 +2179,12 @@ 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, name)) + goto fail; /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len, @@ -2208,134 +2195,75 @@ struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name) cp, DPP_CP_LEN); password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len); forced_memzero(cp, DPP_CP_LEN); - if (!password) + if (!password || + crypto_csr_set_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD, + ASN1_TAG_UTF8STRING, (const u8 *) password, + password_len)) 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) - 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; } -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; + 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) { + csr = crypto_csr_verify(csrbuf); + if (!csr) { wpa_printf(MSG_DEBUG, - "DPP: CSR does not have a valid signature"); + "DPP: CSR invalid or invalid signature"); goto fail; } - loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1); - if (loc < 0) { + attr = crypto_csr_get_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD, + &attr_len, &attr_type); + if (!attr) { wpa_printf(MSG_DEBUG, "DPP: CSR does not include challengePassword"); goto fail; } - - attr = X509_REQ_get_attr(req, loc); - 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"); - 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); + attr_type); 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"); - 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"); @@ -2366,7 +2294,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 9c18971d8..a59a81262 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1173,4 +1173,106 @@ int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2); 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(void); + +/** + * crypto_csr_verify - Initialize CSR from CertificationRequest + * @req: DER encoding of ASN.1 CertificationRequest + * + * Returns: Pointer to CSR data or %NULL on failure or if signature is invalid + */ +struct crypto_csr * crypto_csr_verify(const struct wpabuf *req); + +/** + * 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 entry in CSR SubjectName + * @csr: CSR structure from @crypto_csr_init() + * @type: Name type to add into the CSR SubjectName + * @name: UTF-8 string to write in the CSR SubjectName + * Returns: 0 on success, -1 on failure + */ +int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, + const char *name); + +/** + * crypto_csr_set_attribute - Set attribute in CSR + * @csr: CSR structure from @crypto_csr_init() + * @attr: Attribute 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 attribute 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 73e5c635d..96ce493e3 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -111,6 +111,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) @@ -2896,4 +2900,184 @@ fail: return pem; } + +struct crypto_csr * crypto_csr_init() +{ + return (struct crypto_csr *)X509_REQ_new(); +} + + +struct crypto_csr * crypto_csr_verify(const struct wpabuf *req) +{ + X509_REQ *csr; + EVP_PKEY *pkey = NULL; + const u8 *der = wpabuf_head(req); + + csr = d2i_X509_REQ(NULL, &der, wpabuf_len(req)); + 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 char *name) +{ + 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, + (const unsigned char *) name, + os_strlen(name), -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); + if (!attrib_type) + return NULL; + *type = ASN1_TYPE_get(attrib_type); + data = X509_ATTRIBUTE_get0_data(attrib, 0, *type, NULL); + if (!data) + return 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; + 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 */