diff --git a/crypto/evp_extra/p_dsa_asn1.c b/crypto/evp_extra/p_dsa_asn1.c index 9d927897b13..63e5ea22c23 100644 --- a/crypto/evp_extra/p_dsa_asn1.c +++ b/crypto/evp_extra/p_dsa_asn1.c @@ -211,6 +211,21 @@ static int dsa_bits(const EVP_PKEY *pkey) { return BN_num_bits(pkey->pkey.dsa->p); } +static int dsa_param_decode(EVP_PKEY *pkey, const uint8_t **pder, long derlen) { + DSA *dsa; + dsa = d2i_DSAparams(NULL, pder, derlen); + if (dsa == NULL) { + OPENSSL_PUT_ERROR(EVP, ERR_R_DSA_LIB); + return 0; + } + EVP_PKEY_assign_DSA(pkey, dsa); + return 1; +} + +static int dsa_param_encode(const EVP_PKEY *pkey, uint8_t **pder) { + return i2d_DSAparams(pkey->pkey.dsa, pder); +} + static int dsa_missing_parameters(const EVP_PKEY *pkey) { DSA *dsa; dsa = pkey->pkey.dsa; @@ -281,6 +296,8 @@ const EVP_PKEY_ASN1_METHOD dsa_asn1_meth = { int_dsa_size, dsa_bits, + dsa_param_decode, + dsa_param_encode, dsa_missing_parameters, dsa_copy_parameters, dsa_cmp_parameters, diff --git a/crypto/evp_extra/p_ec_asn1.c b/crypto/evp_extra/p_ec_asn1.c index de364a6f4f7..efbbe992eb5 100644 --- a/crypto/evp_extra/p_ec_asn1.c +++ b/crypto/evp_extra/p_ec_asn1.c @@ -203,6 +203,21 @@ static int ec_bits(const EVP_PKEY *pkey) { return EC_GROUP_order_bits(group); } +static int eckey_param_decode(EVP_PKEY *pkey, const uint8_t **pder, + long derlen) { + EC_KEY *eckey = d2i_ECParameters(NULL, pder, derlen); + if (eckey == NULL) { + OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB); + return 0; + } + EVP_PKEY_assign_EC_KEY(pkey, eckey); + return 1; +} + +static int eckey_param_encode(const EVP_PKEY *pkey, uint8_t **pder) { + return i2d_ECParameters(pkey->pkey.ec, pder); +} + static int ec_missing_parameters(const EVP_PKEY *pkey) { return pkey->pkey.ec == NULL || EC_KEY_get0_group(pkey->pkey.ec) == NULL; } @@ -274,6 +289,8 @@ const EVP_PKEY_ASN1_METHOD ec_asn1_meth = { int_ec_size, ec_bits, + eckey_param_decode, + eckey_param_encode, ec_missing_parameters, ec_copy_parameters, ec_cmp_parameters, diff --git a/crypto/evp_extra/p_ed25519_asn1.c b/crypto/evp_extra/p_ed25519_asn1.c index 14c4cfdf8e3..5c5abbe7bd4 100644 --- a/crypto/evp_extra/p_ed25519_asn1.c +++ b/crypto/evp_extra/p_ed25519_asn1.c @@ -276,6 +276,8 @@ const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = { NULL /* pkey_opaque */, ed25519_size, ed25519_bits, + NULL /* param_decode */, + NULL /* param_encode */, NULL /* param_missing */, NULL /* param_copy */, NULL /* param_cmp */, diff --git a/crypto/evp_extra/p_hmac_asn1.c b/crypto/evp_extra/p_hmac_asn1.c index 153ced0b680..3041128f40b 100644 --- a/crypto/evp_extra/p_hmac_asn1.c +++ b/crypto/evp_extra/p_hmac_asn1.c @@ -119,27 +119,29 @@ static int hmac_get_key(const EVP_PKEY *pkey, uint8_t *priv, size_t *len) { const EVP_PKEY_ASN1_METHOD hmac_asn1_meth = { EVP_PKEY_HMAC, - {0xff} /* placeholder oid */, - 0 /* oid_len */, + {0xff}, // placeholder oid + 0, // oid_len "HMAC", "OpenSSL HMAC method", - NULL /* pub_decode */, - NULL /* pub_encode */, - NULL /* pub_cmp */, - NULL /*priv_decode */, - NULL /* priv_encode */, - NULL /* priv_encode_v2 */, - hmac_set_key /* set_priv_raw */, - NULL /* set_pub_raw */, - hmac_get_key /* get_priv_raw */, - NULL /* get_pub_raw */, - NULL /* pkey_opaque */, - hmac_size /* pkey_size */, - NULL /* pkey_bits */, - NULL /* param_missing */, - NULL /* param_copy */, - NULL /* param_cmp */, - hmac_key_free /* pkey_free */ + NULL, // pub_decode + NULL, // pub_encode + NULL, // pub_cmp + NULL, // priv_decode + NULL, // priv_encode + NULL, // priv_encode_v2 + hmac_set_key, // set_priv_raw + NULL, // set_pub_raw + hmac_get_key, // get_priv_raw + NULL, // get_pub_raw + NULL, // pkey_opaque + hmac_size, // pkey_size + NULL, // pkey_bits + NULL, // param_decode + NULL, // param_encode + NULL, // param_missing + NULL, // param_copy + NULL, // param_cmp + hmac_key_free, // pkey_free }; diff --git a/crypto/evp_extra/p_kem_asn1.c b/crypto/evp_extra/p_kem_asn1.c index d6ca05676e1..a73554cf7b4 100644 --- a/crypto/evp_extra/p_kem_asn1.c +++ b/crypto/evp_extra/p_kem_asn1.c @@ -136,6 +136,8 @@ const EVP_PKEY_ASN1_METHOD kem_asn1_meth = { NULL, // pkey_opaque NULL, // kem_size NULL, // kem_bits + NULL, // param_decode + NULL, // param_encode NULL, // missing_parameters NULL, // param_copy kem_cmp_parameters, diff --git a/crypto/evp_extra/p_rsa_asn1.c b/crypto/evp_extra/p_rsa_asn1.c index 04235950cc9..ca3dca162e0 100644 --- a/crypto/evp_extra/p_rsa_asn1.c +++ b/crypto/evp_extra/p_rsa_asn1.c @@ -245,7 +245,7 @@ const EVP_PKEY_ASN1_METHOD rsa_asn1_meth = { int_rsa_size, rsa_bits, - 0,0,0, + 0,0,0,0,0, int_rsa_free, }; @@ -276,7 +276,7 @@ const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth = { int_rsa_size, rsa_bits, - 0,0,0, + 0,0,0,0,0, int_rsa_free, }; diff --git a/crypto/evp_extra/p_x25519_asn1.c b/crypto/evp_extra/p_x25519_asn1.c index b6963a82281..d8728f255d6 100644 --- a/crypto/evp_extra/p_x25519_asn1.c +++ b/crypto/evp_extra/p_x25519_asn1.c @@ -269,6 +269,8 @@ const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = { NULL /* pkey_opaque */, x25519_size, x25519_bits, + NULL /* param_decode */, + NULL /* param_encode */, NULL /* param_missing */, NULL /* param_copy */, NULL /* param_cmp */, diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index 0465d6cd66c..85f325550da 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -129,6 +129,11 @@ struct evp_pkey_asn1_method_st { int (*pkey_size)(const EVP_PKEY *pk); int (*pkey_bits)(const EVP_PKEY *pk); + // TODO: Do we even need function pointers if only 3 types of `EVP_PKEY`s + // consume these? Considering if we can just pull the logic into the API + // instead of adding a function pointer for each `EVP_PKEY`. + int (*param_decode)(EVP_PKEY *pkey, const uint8_t **pder, long derlen); + int (*param_encode)(const EVP_PKEY *pkey, uint8_t **pder); int (*param_missing)(const EVP_PKEY *pk); int (*param_copy)(EVP_PKEY *to, const EVP_PKEY *from); int (*param_cmp)(const EVP_PKEY *a, const EVP_PKEY *b); diff --git a/crypto/pem/pem_lib.c b/crypto/pem/pem_lib.c index 01c6d6bca7d..2c9a347ba47 100644 --- a/crypto/pem/pem_lib.c +++ b/crypto/pem/pem_lib.c @@ -71,6 +71,7 @@ #include #include "../internal.h" +#include "../fipsmodule/evp/internal.h" #define MIN_LENGTH 4 @@ -146,6 +147,13 @@ static int check_pem(const char *nm, const char *name) { !strcmp(nm, PEM_STRING_DSA); } + // These correspond with the PEM strings that have "PARAMETERS". + if (!strcmp(name, PEM_STRING_PARAMETERS)) { + return !strcmp(nm, PEM_STRING_ECPARAMETERS) || + !strcmp(nm, PEM_STRING_DSAPARAMS) || + !strcmp(nm, PEM_STRING_DHPARAMS); + } + // Permit older strings if (!strcmp(nm, PEM_STRING_X509_OLD) && !strcmp(name, PEM_STRING_X509)) { diff --git a/crypto/pem/pem_pkey.c b/crypto/pem/pem_pkey.c index 2d28d6c0217..342d60950cc 100644 --- a/crypto/pem/pem_pkey.c +++ b/crypto/pem/pem_pkey.c @@ -67,6 +67,7 @@ #include #include #include +#include "../fipsmodule/evp/internal.h" EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u) { @@ -156,6 +157,66 @@ int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc, return PEM_write_bio_PKCS8PrivateKey(bp, x, enc, (char *)kstr, klen, cb, u); } +EVP_PKEY *PEM_read_bio_Parameters(BIO *bio, EVP_PKEY **pkey) { + char *nm = NULL; + unsigned char *data = NULL; + long len; + if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_PARAMETERS, bio, 0, + NULL)) { + return NULL; + } + const unsigned char *p = data; + + EVP_PKEY *ret = EVP_PKEY_new(); + if (strcmp(nm, PEM_STRING_ECPARAMETERS) == 0 || + !EVP_PKEY_set_type(ret, EVP_PKEY_EC)) { + goto err; + } else if (strcmp(nm, PEM_STRING_DSAPARAMS) == 0 || + !EVP_PKEY_set_type(ret, EVP_PKEY_DSA)) { + // TODO: This should work, we only support serializing DSA? + goto err; + } else if (strcmp(nm, PEM_STRING_DHPARAMS) == 0 || + !EVP_PKEY_set_type(ret, EVP_PKEY_DH)) { + // TODO: EVP_PKEY_DH ASN1 support is missing. Work on this once we have it. + goto err; + } + + if (!ret->ameth->param_decode || !ret->ameth->param_decode(ret, &p, len)) { + EVP_PKEY_free(ret); + OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB); + goto err; + } + + + if (pkey != NULL) { + if (*pkey != NULL) { + EVP_PKEY_free(*pkey); + } + *pkey = ret; + } + + OPENSSL_free(nm); + OPENSSL_free(data); + return ret; + +err: + EVP_PKEY_free(ret); + OPENSSL_free(nm); + OPENSSL_free(data); + return NULL; +} + +int PEM_write_bio_Parameters(BIO *bio, EVP_PKEY *pkey) { + char pem_str[80]; + if (!pkey->ameth || !pkey->ameth->param_encode) { + return 0; + } + + BIO_snprintf(pem_str, 80, "%s PARAMETERS", pkey->ameth->pem_str); + return PEM_ASN1_write_bio((i2d_of_void *)pkey->ameth->param_encode, pem_str, + bio, pkey, NULL, NULL, 0, 0, NULL); +} + EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u) { BIO *b = BIO_new_fp(fp, BIO_NOCLOSE); diff --git a/include/openssl/pem.h b/include/openssl/pem.h index 0420abf9bcf..4018c632ccb 100644 --- a/include/openssl/pem.h +++ b/include/openssl/pem.h @@ -107,6 +107,7 @@ extern "C" { #define PEM_STRING_ECDSA_PUBLIC "ECDSA PUBLIC KEY" #define PEM_STRING_ECPARAMETERS "EC PARAMETERS" #define PEM_STRING_ECPRIVATEKEY "EC PRIVATE KEY" +#define PEM_STRING_PARAMETERS "PARAMETERS" #define PEM_STRING_CMS "CMS" // enc_type is one off @@ -473,6 +474,9 @@ OPENSSL_EXPORT int PEM_write_PKCS8PrivateKey(FILE *fp, const EVP_PKEY *x, int klen, pem_password_cb *cd, void *u); +OPENSSL_EXPORT EVP_PKEY *PEM_read_bio_Parameters(BIO *bio, EVP_PKEY **pkey); +OPENSSL_EXPORT int PEM_write_bio_Parameters(BIO *bio, EVP_PKEY *pkey); + // PEM_read_bio_ECPKParameters deserializes the PEM file written in |bio| // according to |ECPKParameters| in RFC 3279. It returns the |EC_GROUP| // corresponding to deserialized output and also writes it to |out_group|. Only