diff options
author | David Benjamin <davidben@chromium.org> | 2015-12-31 05:40:40 +0300 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2016-02-17 19:28:07 +0300 |
commit | 68772b31b07827793827e45ea81e8035269774c1 (patch) | |
tree | f9f661c29a375dfd4586e847b2a5ece92733de5f /crypto | |
parent | 2dc469e06655be3a761ee25b1d89b4f6ebfc29a4 (diff) |
Implement new SPKI parsers.
Many consumers need SPKI support (X.509, TLS, QUIC, WebCrypto), each
with different ways to set signature parameters. SPKIs themselves can
get complex with id-RSASSA-PSS keys which come with various constraints
in the key parameters. This suggests we want a common in-library
representation of an SPKI.
This adds two new functions EVP_parse_public_key and
EVP_marshal_public_key which converts EVP_PKEY to and from SPKI and
implements X509_PUBKEY functions with them. EVP_PKEY seems to have been
intended to be able to express the supported SPKI types with
full-fidelity, so these APIs will continue this.
This means future support for id-RSASSA-PSS would *not* repurpose
EVP_PKEY_RSA. I'm worried about code assuming EVP_PKEY_RSA implies
acting on the RSA* is legal. Instead, it'd add an EVP_PKEY_RSA_PSS and
the data pointer would be some (exposed, so the caller may still check
key size, etc.) RSA_PSS_KEY struct. Internally, the EVP_PKEY_CTX
implementation would enforce the key constraints. If RSA_PSS_KEY would
later need its own API, that code would move there, but that seems
unlikely.
Ideally we'd have a 1:1 correspondence with key OID, although we may
have to fudge things if mistakes happen in standardization. (Whether or
not X.509 reuses id-ecPublicKey for Ed25519, we'll give it a separate
EVP_PKEY type.)
DSA parsing hooks are still implemented, missing parameters and all for
now. This isn't any worse than before.
Decoupling from the giant crypto/obj OID table will be a later task.
BUG=522228
Change-Id: I0e3964edf20cb795a18b0991d17e5ca8bce3e28c
Reviewed-on: https://boringssl-review.googlesource.com/6861
Reviewed-by: Adam Langley <agl@google.com>
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/err/evp.errordata | 1 | ||||
-rw-r--r-- | crypto/evp/evp_asn1.c | 50 | ||||
-rw-r--r-- | crypto/evp/evp_test.cc | 66 | ||||
-rw-r--r-- | crypto/evp/evp_tests.txt | 50 | ||||
-rw-r--r-- | crypto/evp/internal.h | 15 | ||||
-rw-r--r-- | crypto/evp/p_dsa_asn1.c | 122 | ||||
-rw-r--r-- | crypto/evp/p_ec_asn1.c | 96 | ||||
-rw-r--r-- | crypto/evp/p_rsa_asn1.c | 43 | ||||
-rw-r--r-- | crypto/test/file_test.h | 2 | ||||
-rw-r--r-- | crypto/x509/x_pubkey.c | 84 |
10 files changed, 317 insertions, 212 deletions
diff --git a/crypto/err/evp.errordata b/crypto/err/evp.errordata index 8f8dd483..cfb81b15 100644 --- a/crypto/err/evp.errordata +++ b/crypto/err/evp.errordata @@ -6,6 +6,7 @@ EVP,143,DECODE_ERROR EVP,104,DIFFERENT_KEY_TYPES EVP,105,DIFFERENT_PARAMETERS EVP,147,DIGEST_AND_KEY_TYPE_NOT_SUPPORTED +EVP,155,ENCODE_ERROR EVP,107,EXPECTING_AN_EC_KEY_KEY EVP,141,EXPECTING_AN_RSA_KEY EVP,109,EXPECTING_A_DH_KEY diff --git a/crypto/evp/evp_asn1.c b/crypto/evp/evp_asn1.c index da25b99a..e90bed5f 100644 --- a/crypto/evp/evp_asn1.c +++ b/crypto/evp/evp_asn1.c @@ -57,6 +57,7 @@ #include <openssl/evp.h> #include <openssl/asn1.h> +#include <openssl/bytestring.h> #include <openssl/err.h> #include <openssl/obj.h> #include <openssl/x509.h> @@ -64,6 +65,55 @@ #include "internal.h" +EVP_PKEY *EVP_parse_public_key(CBS *cbs) { + /* Parse the SubjectPublicKeyInfo. */ + CBS spki, algorithm, oid, key; + uint8_t padding; + if (!CBS_get_asn1(cbs, &spki, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) || + !CBS_get_asn1(&spki, &key, CBS_ASN1_BITSTRING) || + CBS_len(&spki) != 0 || + /* Every key type defined encodes the key as a byte string with the same + * conversion to BIT STRING. */ + !CBS_get_u8(&key, &padding) || + padding != 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return NULL; + } + + /* Set up an |EVP_PKEY| of the appropriate type. */ + EVP_PKEY *ret = EVP_PKEY_new(); + if (ret == NULL || + !EVP_PKEY_set_type(ret, OBJ_cbs2nid(&oid))) { + goto err; + } + + /* Call into the type-specific SPKI decoding function. */ + if (ret->ameth->pub_decode == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM); + goto err; + } + if (!ret->ameth->pub_decode(ret, &algorithm, &key)) { + goto err; + } + + return ret; + +err: + EVP_PKEY_free(ret); + return NULL; +} + +int EVP_marshal_public_key(CBB *cbb, const EVP_PKEY *key) { + if (key->ameth->pub_encode == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM); + return 0; + } + + return key->ameth->pub_encode(cbb, key); +} + EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **out, const uint8_t **inp, long len) { EVP_PKEY *ret; diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc index 83906546..4e7b56e3 100644 --- a/crypto/evp/evp_test.cc +++ b/crypto/evp/evp_test.cc @@ -71,6 +71,7 @@ #endif #include <openssl/bio.h> +#include <openssl/bytestring.h> #include <openssl/crypto.h> #include <openssl/digest.h> #include <openssl/err.h> @@ -104,6 +105,20 @@ static const EVP_MD *GetDigest(FileTest *t, const std::string &name) { return nullptr; } +static int GetKeyType(FileTest *t, const std::string &name) { + if (name == "RSA") { + return EVP_PKEY_RSA; + } + if (name == "EC") { + return EVP_PKEY_EC; + } + if (name == "DSA") { + return EVP_PKEY_DSA; + } + t->PrintLine("Unknown key type: '%s'", name.c_str()); + return EVP_PKEY_NONE; +} + using KeyMap = std::map<std::string, ScopedEVP_PKEY>; // ImportPrivateKey evaluates a PrivateKey test in |t| and writes the resulting @@ -128,12 +143,63 @@ static bool ImportPrivateKey(FileTest *t, KeyMap *key_map) { return true; } +static bool ImportPublicKey(FileTest *t, KeyMap *key_map) { + std::vector<uint8_t> input; + if (!t->GetBytes(&input, "Input")) { + return false; + } + + CBS cbs; + CBS_init(&cbs, input.data(), input.size()); + ScopedEVP_PKEY pkey(EVP_parse_public_key(&cbs)); + if (!pkey) { + return false; + } + + std::string key_type; + if (!t->GetAttribute(&key_type, "Type")) { + return false; + } + if (EVP_PKEY_id(pkey.get()) != GetKeyType(t, key_type)) { + t->PrintLine("Bad key type."); + return false; + } + + // The encoding must round-trip. + ScopedCBB cbb; + uint8_t *spki; + size_t spki_len; + if (!CBB_init(cbb.get(), 0) || + !EVP_marshal_public_key(cbb.get(), pkey.get()) || + !CBB_finish(cbb.get(), &spki, &spki_len)) { + return false; + } + ScopedOpenSSLBytes free_spki(spki); + if (!t->ExpectBytesEqual(input.data(), input.size(), spki, spki_len)) { + t->PrintLine("Re-encoding the SPKI did not match."); + return false; + } + + // Save the key for future tests. + const std::string &key_name = t->GetParameter(); + if (key_map->count(key_name) > 0) { + t->PrintLine("Duplicate key '%s'.", key_name.c_str()); + return false; + } + (*key_map)[key_name] = std::move(pkey); + return true; +} + static bool TestEVP(FileTest *t, void *arg) { KeyMap *key_map = reinterpret_cast<KeyMap*>(arg); if (t->GetType() == "PrivateKey") { return ImportPrivateKey(t, key_map); } + if (t->GetType() == "PublicKey") { + return ImportPublicKey(t, key_map); + } + int (*key_op_init)(EVP_PKEY_CTX *ctx); int (*key_op)(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len, const uint8_t *in, size_t in_len); diff --git a/crypto/evp/evp_tests.txt b/crypto/evp/evp_tests.txt index 97ddaa0e..2320125e 100644 --- a/crypto/evp/evp_tests.txt +++ b/crypto/evp/evp_tests.txt @@ -1,9 +1,8 @@ # Public key algorithm tests -# Private keys used for PKEY operations. +# Keys used for PKEY operations. # RSA 2048 bit key. - PrivateKey = RSA-2048 -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNAIHqeyrh6gbV @@ -34,8 +33,22 @@ SOamA2hu2OJWCl9q8fLCT69KqWDjghhvFe7c6aJJGucwaA3Uz3eLcPqoaCarMiNH fMkTd7GabVourqIZdgvu1Q== -----END PRIVATE KEY----- -# EC P-256 key +# The public half of the same key encoded as a SubjectPublicKeyInfo. +PublicKey = RSA-2048-SPKI +Type = RSA +Input = 30820122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001 + +# The same key but with missing parameters rather than a NULL. +PublicKey = RSA-2048-SPKI-Invalid +Input = 30820120300b06092a864886f70d0101010382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001 +Error = DECODE_ERROR +# The same key but with an incorrectly-encoded length prefix. +PublicKey = RSA-2048-SPKI-Invalid2 +Input = 3083000122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001 +Error = DECODE_ERROR + +# EC P-256 key PrivateKey = P-256 -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiocvtiiTxNH/xbnw @@ -43,6 +56,27 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiocvtiiTxNH/xbnw +JQkBywnGX14szuSDpXNtmTpkNzwz+oNlOKo5q+dDlgFbmUxBJJbn+bJ -----END PRIVATE KEY----- +# The public half of the same key encoded as a PublicKey. +PublicKey = P-256-SPKI +Type = EC +Input = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 + +# The same as above, but with the curve explicitly spelled out. +PublicKey = P-256-SPKI +Input = 3082014b3082010306072a8648ce3d02013081f7020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff305b0420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b031500c49d360886e704936a6678e1139d26b7819f7e900441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +Error = DECODE_ERROR + +# A DSA key. +PublicKey = DSA-1024 +Type = DSA +Input = 308201b73082012c06072a8648ce3804013082011f02818100b3429b8b128c9079f9b72e86857e98d265e5d91661ed8b5f4cc56e5eed1e571da30186983a9dd76297eab73ee13a1db841f8800d04a7cab478af6cde2ea4a2868531af169a24858c6268efa39ceb7ed0d4227eb5bbb01124a2a5a26038c7bcfb8cc827f68f5202345166e4718596799b65c9def82828ce44e62e38e41a0d24b1021500c5a56c81ddd87f47e676546c56d05706421624cf0281810094de40d27314fe929e47ff9b1ac65cfc73ef38c4d381c890be6217b15039ae18190e6b421af8c0bda35a5cfd050f58ae2644adce83e68c8e5ba11729df56bbb21e227a60b816cc033fa799a38fe1ba5b4aa1801b6f841ce3df99feb3b4fb96950c960af13fa2ce920aabc12dd24ad2044a35063ea0e25f67f560f4cfbdc5598303818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a + +# The same key as above, but without the parameters. +PublicKey = DSA-1024-No-Params +Type = DSA +Input = 308192300906072a8648ce38040103818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a + + # RSA tests Sign = RSA-2048 @@ -55,6 +89,11 @@ Digest = SHA1 Input = "0123456789ABCDEF1234" Output = c09d402423cbf233d26cae21f954547bc43fe80fd41360a0336cfdbe9aedad05bef6fd2eaee6cd60089a52482d4809a238149520df3bdde4cb9e23d9307b05c0a6f327052325a29adf2cc95b66523be7024e2a585c3d4db15dfbe146efe0ecdc0402e33fe5d40324ee96c5c3edd374a15cdc0f5d84aa243c0f07e188c6518fbfceae158a9943be398e31097da81b62074f626eff738be6160741d5a26957a482b3251fd85d8df78b98148459de10aa93305dbb4a5230aa1da291a9b0e481918f99b7638d72bb687f97661d304ae145d64a474437a4ef39d7b8059332ddeb07e92bf6e0e3acaf8afedc93795e4511737ec1e7aab6d5bc9466afc950c1c17b48ad +Verify = RSA-2048-SPKI +Digest = SHA1 +Input = "0123456789ABCDEF1234" +Output = c09d402423cbf233d26cae21f954547bc43fe80fd41360a0336cfdbe9aedad05bef6fd2eaee6cd60089a52482d4809a238149520df3bdde4cb9e23d9307b05c0a6f327052325a29adf2cc95b66523be7024e2a585c3d4db15dfbe146efe0ecdc0402e33fe5d40324ee96c5c3edd374a15cdc0f5d84aa243c0f07e188c6518fbfceae158a9943be398e31097da81b62074f626eff738be6160741d5a26957a482b3251fd85d8df78b98148459de10aa93305dbb4a5230aa1da291a9b0e481918f99b7638d72bb687f97661d304ae145d64a474437a4ef39d7b8059332ddeb07e92bf6e0e3acaf8afedc93795e4511737ec1e7aab6d5bc9466afc950c1c17b48ad + # Digest too long Sign = RSA-2048 Digest = SHA1 @@ -125,6 +164,11 @@ Digest = SHA1 Input = "0123456789ABCDEF1234" Output = 3045022100b1d1cb1a577035bccdd5a86c6148c2cc7c633cd42b7234139b593076d041e15202201898cdd52b41ca502098184b409cf83a21bc945006746e3b7cea52234e043ec8 +Verify = P-256-SPKI +Digest = SHA1 +Input = "0123456789ABCDEF1234" +Output = 3045022100b1d1cb1a577035bccdd5a86c6148c2cc7c633cd42b7234139b593076d041e15202201898cdd52b41ca502098184b409cf83a21bc945006746e3b7cea52234e043ec8 + # Digest too long Verify = P-256 Digest = SHA1 diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h index 67b2a659..f2bad30a 100644 --- a/crypto/evp/internal.h +++ b/crypto/evp/internal.h @@ -90,8 +90,19 @@ struct evp_pkey_asn1_method_st { const char *pem_str; - int (*pub_decode)(EVP_PKEY *pk, X509_PUBKEY *pub); - int (*pub_encode)(X509_PUBKEY *pub, const EVP_PKEY *pk); + /* pub_decode decodes |params| and |key| as a SubjectPublicKeyInfo + * and writes the result into |out|. It returns one on success and zero on + * error. |params| is the AlgorithmIdentifier after the OBJECT IDENTIFIER + * type field, and |key| is the contents of the subjectPublicKey with the + * leading padding byte checked and removed. Although X.509 uses BIT STRINGs + * to represent SubjectPublicKeyInfo, every key type defined encodes the key + * as a byte string with the same conversion to BIT STRING. */ + int (*pub_decode)(EVP_PKEY *out, CBS *params, CBS *key); + + /* pub_encode encodes |key| as a SubjectPublicKeyInfo and appends the result + * to |out|. It returns one on success and zero on error. */ + int (*pub_encode)(CBB *out, const EVP_PKEY *key); + int (*pub_cmp)(const EVP_PKEY *a, const EVP_PKEY *b); int (*pub_print)(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx); diff --git a/crypto/evp/p_dsa_asn1.c b/crypto/evp/p_dsa_asn1.c index 9f97b163..62f2ac89 100644 --- a/crypto/evp/p_dsa_asn1.c +++ b/crypto/evp/p_dsa_asn1.c @@ -55,6 +55,8 @@ #include <openssl/evp.h> +#include <limits.h> + #include <openssl/asn1.h> #include <openssl/asn1t.h> #include <openssl/digest.h> @@ -67,113 +69,63 @@ #include "internal.h" -static int dsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) { - const uint8_t *p, *pm; - int pklen, pmlen; - int ptype; - void *pval; - ASN1_STRING *pstr; - X509_ALGOR *palg; - ASN1_INTEGER *public_key = NULL; - - DSA *dsa = NULL; - - if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) { - return 0; - } - X509_ALGOR_get0(NULL, &ptype, &pval, palg); - - if (ptype == V_ASN1_SEQUENCE) { - pstr = pval; - pm = pstr->data; - pmlen = pstr->length; +static int dsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) { + /* See RFC 3279, section 2.3.2. */ - dsa = d2i_DSAparams(NULL, &pm, pmlen); - if (dsa == NULL) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); - goto err; - } - } else if (ptype == V_ASN1_NULL || ptype == V_ASN1_UNDEF) { + /* Parameters may or may not be present. */ + DSA *dsa; + if (CBS_len(params) == 0) { dsa = DSA_new(); if (dsa == NULL) { - OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); - goto err; + return 0; } } else { - OPENSSL_PUT_ERROR(EVP, EVP_R_PARAMETER_ENCODING_ERROR); - goto err; + dsa = DSA_parse_parameters(params); + if (dsa == NULL || CBS_len(params) != 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } } - public_key = d2i_ASN1_INTEGER(NULL, &p, pklen); - if (public_key == NULL) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + dsa->pub_key = BN_new(); + if (dsa->pub_key == NULL) { goto err; } - dsa->pub_key = ASN1_INTEGER_to_BN(public_key, NULL); - if (dsa->pub_key == NULL) { - OPENSSL_PUT_ERROR(EVP, EVP_R_BN_DECODE_ERROR); + if (!BN_parse_asn1_unsigned(key, dsa->pub_key) || + CBS_len(key) != 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); goto err; } - ASN1_INTEGER_free(public_key); - EVP_PKEY_assign_DSA(pkey, dsa); + EVP_PKEY_assign_DSA(out, dsa); return 1; err: - ASN1_INTEGER_free(public_key); DSA_free(dsa); return 0; } -static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) { - DSA *dsa; - ASN1_STRING *pval = NULL; - uint8_t *penc = NULL; - int penclen; - - dsa = pkey->pkey.dsa; - - int ptype; - if (dsa->p && dsa->q && dsa->g) { - pval = ASN1_STRING_new(); - if (!pval) { - OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); - goto err; - } - pval->length = i2d_DSAparams(dsa, &pval->data); - if (pval->length <= 0) { - OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); - goto err; - } - ptype = V_ASN1_SEQUENCE; - } else { - ptype = V_ASN1_UNDEF; - } - - ASN1_INTEGER *pubint = BN_to_ASN1_INTEGER(dsa->pub_key, NULL); - if (pubint == NULL) { - goto err; - } - - penclen = i2d_ASN1_INTEGER(pubint, &penc); - ASN1_INTEGER_free(pubint); - - if (penclen <= 0) { - OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); - goto err; - } - - if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA), ptype, pval, - penc, penclen)) { - return 1; +static int dsa_pub_encode(CBB *out, const EVP_PKEY *key) { + const DSA *dsa = key->pkey.dsa; + const int has_params = dsa->p != NULL && dsa->q != NULL && dsa->g != NULL; + + /* See RFC 5480, section 2. */ + CBB spki, algorithm, key_bitstring; + if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || + !OBJ_nid2cbb(&algorithm, NID_dsa) || + (has_params && + !DSA_marshal_parameters(&algorithm, dsa)) || + !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || + !CBB_add_u8(&key_bitstring, 0 /* padding */) || + !BN_marshal_asn1(&key_bitstring, dsa->pub_key) || + !CBB_flush(out)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); + return 0; } -err: - OPENSSL_free(penc); - ASN1_STRING_free(pval); - - return 0; + return 1; } static int dsa_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) { diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c index e093c183..2d8d38a2 100644 --- a/crypto/evp/p_ec_asn1.c +++ b/crypto/evp/p_ec_asn1.c @@ -57,6 +57,7 @@ #include <openssl/asn1t.h> #include <openssl/bn.h> +#include <openssl/bytestring.h> #include <openssl/ec.h> #include <openssl/err.h> #include <openssl/mem.h> @@ -86,82 +87,65 @@ static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key) { return 1; } -static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) { - EC_KEY *ec_key = pkey->pkey.ec; - void *pval = NULL; - int ptype; - uint8_t *penc = NULL, *p; - int penclen; - - if (!eckey_param2type(&ptype, &pval, ec_key)) { - OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB); +static int eckey_pub_encode(CBB *out, const EVP_PKEY *key) { + const EC_KEY *ec_key = key->pkey.ec; + const EC_GROUP *group = EC_KEY_get0_group(ec_key); + int curve_nid = EC_GROUP_get_curve_name(group); + if (curve_nid == NID_undef) { + OPENSSL_PUT_ERROR(EVP, EVP_R_NO_NID_FOR_CURVE); return 0; } - penclen = i2o_ECPublicKey(ec_key, NULL); - if (penclen <= 0) { - goto err; - } - penc = OPENSSL_malloc(penclen); - if (!penc) { - goto err; - } - p = penc; - penclen = i2o_ECPublicKey(ec_key, &p); - if (penclen <= 0) { - goto err; - } - if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_EC), ptype, pval, penc, - penclen)) { - return 1; + const EC_POINT *public_key = EC_KEY_get0_public_key(ec_key); + + /* See RFC 5480, section 2. */ + CBB spki, algorithm, key_bitstring; + if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || + !OBJ_nid2cbb(&algorithm, NID_X9_62_id_ecPublicKey) || + !OBJ_nid2cbb(&algorithm, curve_nid) || + !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || + !CBB_add_u8(&key_bitstring, 0 /* padding */) || + !EC_POINT_point2cbb(&key_bitstring, group, public_key, + POINT_CONVERSION_UNCOMPRESSED, NULL) || + !CBB_flush(out)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); + return 0; } -err: - if (ptype == V_ASN1_OBJECT) { - ASN1_OBJECT_free(pval); - } else { - ASN1_STRING_free(pval); - } - if (penc) { - OPENSSL_free(penc); - } - return 0; + return 1; } -static int eckey_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) { - const uint8_t *p = NULL; - void *pval; - int ptype, pklen; - EC_KEY *eckey = NULL; - X509_ALGOR *palg; +static int eckey_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) { + /* See RFC 5480, section 2. */ - if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) { - return 0; - } - X509_ALGOR_get0(NULL, &ptype, &pval, palg); - - if (ptype != V_ASN1_OBJECT) { + /* The parameters are a named curve. */ + CBS named_curve; + if (!CBS_get_asn1(params, &named_curve, CBS_ASN1_OBJECT) || + CBS_len(params) != 0) { OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); return 0; } - eckey = EC_KEY_new_by_curve_name(OBJ_obj2nid((ASN1_OBJECT *)pval)); + + EC_KEY *eckey = EC_KEY_new_by_curve_name(OBJ_cbs2nid(&named_curve)); if (eckey == NULL) { - OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB); return 0; } - /* We have parameters now set public key */ - if (!o2i_ECPublicKey(&eckey, &p, pklen)) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(eckey)); + if (point == NULL || + !EC_POINT_oct2point(EC_KEY_get0_group(eckey), point, CBS_data(key), + CBS_len(key), NULL) || + !EC_KEY_set_public_key(eckey, point)) { goto err; } - EVP_PKEY_assign_EC_KEY(pkey, eckey); + EC_POINT_free(point); + EVP_PKEY_assign_EC_KEY(out, eckey); return 1; err: - if (eckey) { - EC_KEY_free(eckey); - } + EC_POINT_free(point); + EC_KEY_free(eckey); return 0; } diff --git a/crypto/evp/p_rsa_asn1.c b/crypto/evp/p_rsa_asn1.c index 70f0b763..83da7df4 100644 --- a/crypto/evp/p_rsa_asn1.c +++ b/crypto/evp/p_rsa_asn1.c @@ -69,26 +69,33 @@ #include "internal.h" -static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) { - uint8_t *encoded; - size_t encoded_len; - if (!RSA_public_key_to_bytes(&encoded, &encoded_len, pkey->pkey.rsa)) { - return 0; - } - - if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_RSA), V_ASN1_NULL, NULL, - encoded, encoded_len)) { - OPENSSL_free(encoded); +static int rsa_pub_encode(CBB *out, const EVP_PKEY *key) { + /* See RFC 3279, section 2.3.1. */ + CBB spki, algorithm, null, key_bitstring; + if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || + !OBJ_nid2cbb(&algorithm, NID_rsaEncryption) || + !CBB_add_asn1(&algorithm, &null, CBS_ASN1_NULL) || + !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || + !CBB_add_u8(&key_bitstring, 0 /* padding */) || + !RSA_marshal_public_key(&key_bitstring, key->pkey.rsa) || + !CBB_flush(out)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); return 0; } return 1; } -static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) { - const uint8_t *p; - int pklen; - if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, NULL, pubkey)) { +static int rsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) { + /* See RFC 3279, section 2.3.1. */ + + /* The parameters must be NULL. */ + CBS null; + if (!CBS_get_asn1(params, &null, CBS_ASN1_NULL) || + CBS_len(&null) != 0 || + CBS_len(params) != 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); return 0; } @@ -98,16 +105,14 @@ static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) { * TODO(davidben): Switch this to the strict version in March 2016 or when * Chromium can force client certificates down a different codepath, whichever * comes first. */ - CBS cbs; - CBS_init(&cbs, p, pklen); - RSA *rsa = RSA_parse_public_key_buggy(&cbs); - if (rsa == NULL || CBS_len(&cbs) != 0) { + RSA *rsa = RSA_parse_public_key_buggy(key); + if (rsa == NULL || CBS_len(key) != 0) { OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); RSA_free(rsa); return 0; } - EVP_PKEY_assign_RSA(pkey, rsa); + EVP_PKEY_assign_RSA(out, rsa); return 1; } diff --git a/crypto/test/file_test.h b/crypto/test/file_test.h index 8fb7ed22..501375bc 100644 --- a/crypto/test/file_test.h +++ b/crypto/test/file_test.h @@ -111,7 +111,7 @@ class FileTest { bool GetAttribute(std::string *out_value, const std::string &key); // GetAttributeOrDie looks up the attribute with key |key| and aborts if it is - // missing. It only be used after a |HasAttribute| call. + // missing. It should only be used after a |HasAttribute| call. const std::string &GetAttributeOrDie(const std::string &key); // GetBytes looks up the attribute with key |key| and decodes it as a byte diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c index e8732a11..47f256c7 100644 --- a/crypto/x509/x_pubkey.c +++ b/crypto/x509/x_pubkey.c @@ -54,8 +54,11 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include <limits.h> + #include <openssl/asn1.h> #include <openssl/asn1t.h> +#include <openssl/bytestring.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/mem.h> @@ -63,7 +66,6 @@ #include <openssl/thread.h> #include <openssl/x509.h> -#include "../evp/internal.h" #include "../internal.h" /* Minor tweak to operation: free up EVP_PKEY */ @@ -87,51 +89,50 @@ IMPLEMENT_ASN1_FUNCTIONS(X509_PUBKEY) int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) { X509_PUBKEY *pk = NULL; + uint8_t *spki = NULL; + size_t spki_len; if (x == NULL) return (0); - if ((pk = X509_PUBKEY_new()) == NULL) + CBB cbb; + if (!CBB_init(&cbb, 0) || + !EVP_marshal_public_key(&cbb, pkey) || + !CBB_finish(&cbb, &spki, &spki_len) || + spki_len > LONG_MAX) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR); goto error; + } - if (pkey->ameth) { - if (pkey->ameth->pub_encode) { - if (!pkey->ameth->pub_encode(pk, pkey)) { - OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR); - goto error; - } - } else { - OPENSSL_PUT_ERROR(X509, X509_R_METHOD_NOT_SUPPORTED); - goto error; - } - } else { - OPENSSL_PUT_ERROR(X509, X509_R_UNSUPPORTED_ALGORITHM); + const uint8_t *p = spki; + pk = d2i_X509_PUBKEY(NULL, &p, (long)spki_len); + if (pk == NULL || p != spki + spki_len) { + OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR); goto error; } - if (*x != NULL) - X509_PUBKEY_free(*x); - + OPENSSL_free(spki); + X509_PUBKEY_free(*x); *x = pk; return 1; error: - if (pk != NULL) - X509_PUBKEY_free(pk); + X509_PUBKEY_free(pk); + OPENSSL_free(spki); return 0; } -/* - * g_pubkey_lock is used to protect the initialisation of the |pkey| member - * of |X509_PUBKEY| objects. Really |X509_PUBKEY| should have a - * |CRYPTO_once_t| inside it for this, but |CRYPTO_once_t| is private and - * |X509_PUBKEY| is not. - */ +/* g_pubkey_lock is used to protect the initialisation of the |pkey| member of + * |X509_PUBKEY| objects. Really |X509_PUBKEY| should have a |CRYPTO_once_t| + * inside it for this, but |CRYPTO_once_t| is private and |X509_PUBKEY| is + * not. */ static struct CRYPTO_STATIC_MUTEX g_pubkey_lock = CRYPTO_STATIC_MUTEX_INIT; EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) { EVP_PKEY *ret = NULL; + uint8_t *spki = NULL; if (key == NULL) goto error; @@ -143,26 +144,16 @@ EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) } CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock); - if (key->public_key == NULL) - goto error; - - if ((ret = EVP_PKEY_new()) == NULL) { - OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE); + /* Re-encode the |X509_PUBKEY| to DER and parse it. */ + int spki_len = i2d_X509_PUBKEY(key, &spki); + if (spki_len < 0) { goto error; } - - if (!EVP_PKEY_set_type(ret, OBJ_obj2nid(key->algor->algorithm))) { - OPENSSL_PUT_ERROR(X509, X509_R_UNSUPPORTED_ALGORITHM); - goto error; - } - - if (ret->ameth->pub_decode) { - if (!ret->ameth->pub_decode(ret, key)) { - OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR); - goto error; - } - } else { - OPENSSL_PUT_ERROR(X509, X509_R_METHOD_NOT_SUPPORTED); + CBS cbs; + CBS_init(&cbs, spki, (size_t)spki_len); + ret = EVP_parse_public_key(&cbs); + if (ret == NULL || CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR); goto error; } @@ -177,12 +168,13 @@ EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock); } + OPENSSL_free(spki); return EVP_PKEY_up_ref(ret); error: - if (ret != NULL) - EVP_PKEY_free(ret); - return (NULL); + OPENSSL_free(spki); + EVP_PKEY_free(ret); + return NULL; } /* |