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>kris/onging/CECPQ3_patch15
@@ -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 | |||
@@ -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; | |||
@@ -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); | |||
@@ -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 | |||
@@ -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); | |||
@@ -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) { | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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 | |||
@@ -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; | |||
} | |||
/* | |||
@@ -188,6 +188,20 @@ OPENSSL_EXPORT int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, | |||
/* ASN.1 functions */ | |||
/* EVP_parse_public_key decodes a DER-encoded SubjectPublicKeyInfo structure | |||
* (RFC 5280) from |cbs| and advances |cbs|. It returns a newly-allocated | |||
* |EVP_PKEY| or NULL on error. | |||
* | |||
* The caller must check the type of the parsed public key to ensure it is | |||
* suitable and validate other desired key properties such as RSA modulus size | |||
* or EC curve. */ | |||
OPENSSL_EXPORT EVP_PKEY *EVP_parse_public_key(CBS *cbs); | |||
/* EVP_marshal_public_key marshals |key| as a DER-encoded SubjectPublicKeyInfo | |||
* structure (RFC 5280) and appends the result to |cbb|. It returns one on | |||
* success and zero on error. */ | |||
OPENSSL_EXPORT int EVP_marshal_public_key(CBB *cbb, const EVP_PKEY *key); | |||
/* d2i_PrivateKey parses an ASN.1, DER-encoded, private key from |len| bytes at | |||
* |*inp|. If |out| is not NULL then, on exit, a pointer to the result is in | |||
* |*out|. If |*out| is already non-NULL on entry then the result is written | |||
@@ -780,5 +794,6 @@ struct evp_pkey_st { | |||
#define EVP_R_PARAMETER_ENCODING_ERROR 152 | |||
#define EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE 153 | |||
#define EVP_R_UNSUPPORTED_SIGNATURE_TYPE 154 | |||
#define EVP_R_ENCODE_ERROR 155 | |||
#endif /* OPENSSL_HEADER_EVP_H */ |