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>
This commit is contained in:
parent
2dc469e066
commit
68772b31b0
@ -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;
|
||||
static int dsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
|
||||
/* See RFC 3279, section 2.3.2. */
|
||||
|
||||
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;
|
||||
|
||||
dsa = d2i_DSAparams(NULL, &pm, pmlen);
|
||||
/* Parameters may or may not be present. */
|
||||
DSA *dsa;
|
||||
if (CBS_len(params) == 0) {
|
||||
dsa = DSA_new();
|
||||
if (dsa == NULL) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
dsa = DSA_parse_parameters(params);
|
||||
if (dsa == NULL || CBS_len(params) != 0) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
||||
goto err;
|
||||
}
|
||||
} else if (ptype == V_ASN1_NULL || ptype == V_ASN1_UNDEF) {
|
||||
dsa = DSA_new();
|
||||
if (dsa == NULL) {
|
||||
OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_PARAMETER_ENCODING_ERROR);
|
||||
}
|
||||
|
||||
dsa->pub_key = BN_new();
|
||||
if (dsa->pub_key == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
public_key = d2i_ASN1_INTEGER(NULL, &p, pklen);
|
||||
if (public_key == NULL) {
|
||||
if (!BN_parse_asn1_unsigned(key, dsa->pub_key) ||
|
||||
CBS_len(key) != 0) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
||||
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);
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
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 (*x != NULL)
|
||||
X509_PUBKEY_free(*x);
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user