|  | // Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     https://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include <openssl/x509.h> | 
|  |  | 
|  | #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> | 
|  | #include <openssl/obj.h> | 
|  |  | 
|  | #include "../internal.h" | 
|  | #include "internal.h" | 
|  |  | 
|  |  | 
|  | static void x509_pubkey_changed(X509_PUBKEY *pub) { | 
|  | EVP_PKEY_free(pub->pkey); | 
|  | pub->pkey = NULL; | 
|  |  | 
|  | // Re-encode the |X509_PUBKEY| to DER and parse it with EVP's APIs. | 
|  | uint8_t *spki = NULL; | 
|  | int spki_len = i2d_X509_PUBKEY(pub, &spki); | 
|  | EVP_PKEY *pkey; | 
|  | if (spki_len < 0) { | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | CBS cbs; | 
|  | CBS_init(&cbs, spki, (size_t)spki_len); | 
|  | pkey = EVP_parse_public_key(&cbs); | 
|  | if (pkey == NULL || CBS_len(&cbs) != 0) { | 
|  | EVP_PKEY_free(pkey); | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | pub->pkey = pkey; | 
|  |  | 
|  | err: | 
|  | OPENSSL_free(spki); | 
|  | // If the operation failed, clear errors. An |X509_PUBKEY| whose key we cannot | 
|  | // parse is still a valid SPKI. It just cannot be converted to an |EVP_PKEY|. | 
|  | ERR_clear_error(); | 
|  | } | 
|  |  | 
|  | static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, | 
|  | void *exarg) { | 
|  | X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; | 
|  | if (operation == ASN1_OP_FREE_POST) { | 
|  | EVP_PKEY_free(pubkey->pkey); | 
|  | } else if (operation == ASN1_OP_D2I_POST) { | 
|  | x509_pubkey_changed(pubkey); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | ASN1_SEQUENCE_cb(X509_PUBKEY, pubkey_cb) = { | 
|  | ASN1_SIMPLE(X509_PUBKEY, algor, X509_ALGOR), | 
|  | ASN1_SIMPLE(X509_PUBKEY, public_key, ASN1_BIT_STRING), | 
|  | } ASN1_SEQUENCE_END_cb(X509_PUBKEY, X509_PUBKEY) | 
|  |  | 
|  | IMPLEMENT_ASN1_FUNCTIONS_const(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; | 
|  | } | 
|  |  | 
|  | CBB cbb; | 
|  | const uint8_t *p; | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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: | 
|  | X509_PUBKEY_free(pk); | 
|  | OPENSSL_free(spki); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key) { | 
|  | if (key == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (key->pkey == NULL) { | 
|  | OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return key->pkey; | 
|  | } | 
|  |  | 
|  | EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key) { | 
|  | EVP_PKEY *pkey = X509_PUBKEY_get0(key); | 
|  | if (pkey != NULL) { | 
|  | EVP_PKEY_up_ref(pkey); | 
|  | } | 
|  | return pkey; | 
|  | } | 
|  |  | 
|  | int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *obj, int param_type, | 
|  | void *param_value, uint8_t *key, int key_len) { | 
|  | if (!X509_ALGOR_set0(pub->algor, obj, param_type, param_value)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ASN1_STRING_set0(pub->public_key, key, key_len); | 
|  | // Set the number of unused bits to zero. | 
|  | pub->public_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); | 
|  | pub->public_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; | 
|  |  | 
|  | x509_pubkey_changed(pub); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int X509_PUBKEY_get0_param(ASN1_OBJECT **out_obj, const uint8_t **out_key, | 
|  | int *out_key_len, X509_ALGOR **out_alg, | 
|  | X509_PUBKEY *pub) { | 
|  | if (out_obj != NULL) { | 
|  | *out_obj = pub->algor->algorithm; | 
|  | } | 
|  | if (out_key != NULL) { | 
|  | *out_key = pub->public_key->data; | 
|  | *out_key_len = pub->public_key->length; | 
|  | } | 
|  | if (out_alg != NULL) { | 
|  | *out_alg = pub->algor; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | const ASN1_BIT_STRING *X509_PUBKEY_get0_public_key(const X509_PUBKEY *pub) { | 
|  | return pub->public_key; | 
|  | } |