blob: 8b74f6511b96a8ede6083b550ff00f41aff8be69 [file] [log] [blame]
// 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 <openssl/span.h>
#include "../asn1/internal.h"
#include "../bytestring/internal.h"
#include "../evp/internal.h"
#include "../internal.h"
#include "../mem_internal.h"
#include "internal.h"
void x509_pubkey_init(X509_PUBKEY *key) {
OPENSSL_memset(key, 0, sizeof(X509_PUBKEY));
x509_algor_init(&key->algor);
asn1_string_init(&key->public_key, V_ASN1_BIT_STRING);
}
X509_PUBKEY *X509_PUBKEY_new(void) {
bssl::UniquePtr<X509_PUBKEY> ret = bssl::MakeUnique<X509_PUBKEY>();
if (ret == nullptr) {
return nullptr;
}
x509_pubkey_init(ret.get());
return ret.release();
}
void x509_pubkey_cleanup(X509_PUBKEY *key) {
x509_algor_cleanup(&key->algor);
asn1_string_cleanup(&key->public_key);
EVP_PKEY_free(key->pkey);
}
void X509_PUBKEY_free(X509_PUBKEY *key) {
if (key != nullptr) {
x509_pubkey_cleanup(key);
OPENSSL_free(key);
}
}
static void x509_pubkey_changed(X509_PUBKEY *pub,
bssl::Span<const EVP_PKEY_ALG *const> algs) {
EVP_PKEY_free(pub->pkey);
pub->pkey = nullptr;
// Re-encode the |X509_PUBKEY| to DER and parse it with EVP's APIs. If the
// operation fails, clear errors. An |X509_PUBKEY| whose key we cannot parse
// is still a valid SPKI. It just cannot be converted to an |EVP_PKEY|.
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 64) || !x509_marshal_public_key(cbb.get(), pub)) {
ERR_clear_error();
return;
}
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_from_subject_public_key_info(
CBB_data(cbb.get()), CBB_len(cbb.get()), algs.data(), algs.size()));
if (pkey == nullptr) {
ERR_clear_error();
return;
}
pub->pkey = pkey.release();
}
int x509_parse_public_key(CBS *cbs, X509_PUBKEY *out,
bssl::Span<const EVP_PKEY_ALG *const> algs) {
CBS seq;
if (!CBS_get_asn1(cbs, &seq, CBS_ASN1_SEQUENCE) ||
!x509_parse_algorithm(&seq, &out->algor) ||
!asn1_parse_bit_string(&seq, &out->public_key, /*tag=*/0) ||
CBS_len(&seq) != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
x509_pubkey_changed(out, algs);
return 1;
}
static int x509_parse_public_key_default(CBS *cbs, X509_PUBKEY *out) {
return x509_parse_public_key(cbs, out, bssl::GetDefaultEVPAlgorithms());
}
int x509_marshal_public_key(CBB *cbb, const X509_PUBKEY *in) {
CBB seq;
return CBB_add_asn1(cbb, &seq, CBS_ASN1_SEQUENCE) &&
x509_marshal_algorithm(&seq, &in->algor) &&
asn1_marshal_bit_string(&seq, &in->public_key, /*tag=*/0) &&
CBB_flush(cbb);
}
X509_PUBKEY *d2i_X509_PUBKEY(X509_PUBKEY **out, const uint8_t **inp, long len) {
return bssl::D2IFromCBS(
out, inp, len, [](CBS *cbs) -> bssl::UniquePtr<X509_PUBKEY> {
bssl::UniquePtr<X509_PUBKEY> ret(X509_PUBKEY_new());
if (ret == nullptr || !x509_parse_public_key_default(cbs, ret.get())) {
return nullptr;
}
return ret;
});
}
int i2d_X509_PUBKEY(const X509_PUBKEY *key, uint8_t **outp) {
return bssl::I2DFromCBB(/*initial_capacity=*/32, outp, [&](CBB *cbb) -> bool {
return x509_marshal_public_key(cbb, key);
});
}
// TODO(crbug.com/42290417): Remove this when |X509| and |X509_REQ| no longer
// depend on the tables.
IMPLEMENT_EXTERN_ASN1_SIMPLE(X509_PUBKEY, X509_PUBKEY_new, X509_PUBKEY_free,
x509_parse_public_key_default, i2d_X509_PUBKEY)
int x509_pubkey_set1(X509_PUBKEY *key, EVP_PKEY *pkey) {
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 64) ||
!EVP_marshal_public_key(cbb.get(), pkey)) {
OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
return 0;
}
CBS cbs;
CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get()));
// TODO(crbug.com/42290364): Use an |EVP_PKEY_ALG| derived from |pkey|.
// |X509_PUBKEY_get0| does not currently work when setting, say, an
// |EVP_PKEY_RSA_PSS| key.
return x509_parse_public_key(&cbs, key, bssl::GetDefaultEVPAlgorithms());
}
int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) {
bssl::UniquePtr<X509_PUBKEY> new_key(X509_PUBKEY_new());
if (new_key == nullptr || !x509_pubkey_set1(new_key.get(), pkey)) {
return 0;
}
X509_PUBKEY_free(*x);
*x = new_key.release();
return 1;
}
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, bssl::GetDefaultEVPAlgorithms());
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;
}