core/asn1/x509_mbedtls.c (843 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "platform_api.h"
#include "x509_mbedtls.h"
#include "common/unused.h"
#include "crypto/crypto_logging.h"
#include "logging/debug_log.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/bignum.h"
#include "mbedtls/oid.h"
#include "mbedtls/pk.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509_csr.h"
/**
* mbedTLS data for managing CA certificates.
*/
struct x509_mbedtls_ca_store_context {
mbedtls_x509_crt *root_ca; /**< The chain of trusted root certificates. */
mbedtls_x509_crt *intermediate; /**< The chain of intermediate CAs. */
};
/**
* Create a new mbedTLS certificate instance.
*
* @return The allocated certificate or null.
*/
static mbedtls_x509_crt* x509_mbedtls_new_cert ()
{
mbedtls_x509_crt *x509 = platform_malloc (sizeof (mbedtls_x509_crt));
if (x509 != NULL) {
mbedtls_x509_crt_init (x509);
}
return x509;
}
/**
* Free an mbedTLS certificate instance.
*
* @param cert The certificate to free.
*/
static void x509_mbedtls_free_cert (void *cert)
{
mbedtls_x509_crt *x509 = (mbedtls_x509_crt*) cert;
if (x509) {
mbedtls_x509_crt_free (x509);
platform_free (x509);
}
}
#ifdef X509_ENABLE_CREATE_CERTIFICATES
/**
* Initialize a key context from a DER formatted key.
*
* @param key The key instance that will be initialized.
* @param der The DER formatted key to load.
* @param length The length of the DER key.
* @param require_priv Flag indicating if the key must be a private a key.
*
* @return 0 if the key was successfully loaded or an error code.
*/
static int x509_mbedtls_load_key (mbedtls_pk_context *key, const uint8_t *der, size_t length,
bool require_priv)
{
int status;
mbedtls_pk_init (key);
status = mbedtls_pk_parse_key (key, der, length, NULL, 0);
if (status != 0) {
if (require_priv) {
goto exit;
}
status = mbedtls_pk_parse_public_key (key, der, length);
}
exit:
if (status != 0) {
mbedtls_pk_free (key);
}
return status;
}
/**
* Write the ASN.1 header to an object.
*
* @param pos The current buffer position.
* @param start The start of the buffer.
* @param tag The object tag to write.
* @param length The length of the object, updated with the header length.
*
* @return 0 if the object header was written or an error code.
*/
int x509_mbedtls_close_asn1_object (uint8_t **pos, uint8_t *start, uint8_t tag, int *length)
{
int ret;
MBEDTLS_ASN1_CHK_ADD (*length, mbedtls_asn1_write_len (pos, start, *length));
MBEDTLS_ASN1_CHK_ADD (*length, mbedtls_asn1_write_tag (pos, start, tag));
return 0;
}
/**
* Create and add the key usage extension to a certificate or CSR.
*
* @param extensions The list of extensions to update.
* @param usage The key usage to advertise in the extension.
*
* @return 0 if the extension was successfully added or an error code.
*/
static int x509_mbedtls_add_key_usage_extension (mbedtls_asn1_named_data **extensions, int usage)
{
uint8_t ext[4];
uint8_t *pos;
int length;
pos = ext + sizeof (ext);
length = mbedtls_asn1_write_bitstring (&pos, ext, (uint8_t*) &usage, (usage & 0x04) ? 6 : 5);
if (length < 0) {
return length;
}
return mbedtls_x509_set_extension (extensions, MBEDTLS_OID_KEY_USAGE,
MBEDTLS_OID_SIZE (MBEDTLS_OID_KEY_USAGE), 1, pos, length);
}
/**
* Create and add the extended key usage extension to a certificate or CSR.
*
* @param extensions The list of extensions to update.
* @param oid The encoded OID string to add.
* @param oid_length Length of the OID string.
* @param critical Critical flag for the extension.
*
* @return 0 if the extension was successfully added or an error code.
*/
static int x509_mbedtls_add_extended_key_usage_extension (mbedtls_asn1_named_data **extensions,
const char *oid, int oid_length, bool critical)
{
uint8_t ext[20];
uint8_t *pos;
int status;
int length;
pos = ext + sizeof (ext);
length = mbedtls_asn1_write_oid (&pos, ext, oid, oid_length);
if (length < 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_ASN1_WRITE_OID_EC, length, 0);
return length;
}
status = x509_mbedtls_close_asn1_object (&pos, ext,
(MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE), &length);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_ASN1_CLOSE_EC, length, 0);
return status;
}
return mbedtls_x509_set_extension (extensions, MBEDTLS_OID_EXTENDED_KEY_USAGE,
MBEDTLS_OID_SIZE (MBEDTLS_OID_EXTENDED_KEY_USAGE), critical, pos, length);
}
/**
* Create and add the basic constraints extension to a certificate or CSR.
*
* @param extensions The list of extensions to update.
* @param ca Flag indicating if the cA flag should be set.
* @param pathlen The value of the pathLengthConstraint. Greater that the max path length will omit
* the constraint.
*
* @return 0 if the extension was successfully added or an error code.
*/
static int x509_mbedtls_add_basic_constraints_extension (mbedtls_asn1_named_data **extensions,
bool ca, int pathlen)
{
uint8_t ext[9];
uint8_t *pos;
int length = 0;
int ret;
pos = ext + sizeof (ext);
if (ca) {
if (pathlen <= X509_CERT_MAX_PATHLEN) {
MBEDTLS_ASN1_CHK_ADD (length, mbedtls_asn1_write_int (&pos, ext, pathlen));
}
MBEDTLS_ASN1_CHK_ADD (length, mbedtls_asn1_write_bool (&pos, ext, 1));
}
ret = x509_mbedtls_close_asn1_object (&pos, ext,
(MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE), &length);
if (ret != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_ASN1_CLOSE_EC, ret, 0);
return ret;
}
return mbedtls_x509_set_extension (extensions, MBEDTLS_OID_BASIC_CONSTRAINTS,
MBEDTLS_OID_SIZE (MBEDTLS_OID_BASIC_CONSTRAINTS), 1, pos, length);
}
/**
* Add a custom extensions to a certificate.
*
* @param builder The extension to add.
* @param extensions List of extensions for the certificate that will be updated with the new
* extension.
*
* @return 0 if the extension was add successfully or an error code.
*/
static int x509_mbedtls_add_custom_extension (const struct x509_extension_builder *builder,
mbedtls_asn1_named_data **extensions)
{
struct x509_extension extension = {0};
int status;
if (builder == NULL) {
/* Silently skip null extension builders. */
return 0;
}
status = builder->build (builder, &extension);
if (status != 0) {
return status;
}
status = mbedtls_x509_set_extension (extensions, (char*) extension.oid, extension.oid_length,
extension.critical, extension.data, extension.data_length);
builder->free (builder, &extension);
return status;
}
int x509_mbedtls_create_csr (const struct x509_engine *engine, const uint8_t *priv_key,
size_t key_length, enum hash_type sig_hash, const char *name, int type, const uint8_t *eku,
size_t eku_length, const struct x509_extension_builder *const *extra_extensions,
size_t ext_count, uint8_t **csr, size_t *csr_length)
{
const struct x509_engine_mbedtls *mbedtls = (const struct x509_engine_mbedtls*) engine;
mbedtls_x509write_csr x509;
mbedtls_pk_context key;
mbedtls_md_type_t md_alg;
char *subject;
size_t i;
int status;
if (csr == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
*csr = NULL;
if ((mbedtls == NULL) || (priv_key == NULL) || (name == NULL) || (csr_length == NULL) ||
(key_length == 0)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if ((eku_length != 0) && (eku == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if ((ext_count != 0) && (extra_extensions == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if ((type == X509_CERT_END_ENTITY) && (eku != NULL)) {
return X509_ENGINE_NOT_CA_CERT;
}
switch (sig_hash) {
case HASH_TYPE_SHA256:
md_alg = MBEDTLS_MD_SHA256;
break;
case HASH_TYPE_SHA384:
md_alg = MBEDTLS_MD_SHA384;
break;
case HASH_TYPE_SHA512:
md_alg = MBEDTLS_MD_SHA512;
break;
default:
return X509_ENGINE_UNSUPPORTED_SIG_HASH;
}
mbedtls_x509write_csr_init (&x509);
status = x509_mbedtls_load_key (&key, priv_key, key_length, true);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_LOAD_KEY_EC, status, 0);
goto err_free_csr;
}
mbedtls_x509write_csr_set_key (&x509, &key);
subject = platform_malloc (strlen (name) + 4);
if (subject == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_key;
}
strcpy (subject, "CN=");
strcpy (&subject[3], name);
status = mbedtls_x509write_csr_set_subject_name (&x509, subject);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_CSR_SET_SUBJECT_EC, status, 0);
goto err_free_subject;
}
if (type) {
status = x509_mbedtls_add_key_usage_extension (&x509.extensions,
MBEDTLS_X509_KU_KEY_CERT_SIGN);
}
else {
status = x509_mbedtls_add_key_usage_extension (&x509.extensions,
(MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT));
}
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_KEY_USAGE_EC, status, 0);
goto err_free_subject;
}
if (type == X509_CERT_END_ENTITY) {
status = x509_mbedtls_add_extended_key_usage_extension (&x509.extensions,
MBEDTLS_OID_CLIENT_AUTH, MBEDTLS_OID_SIZE (MBEDTLS_OID_CLIENT_AUTH), true);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_EXT_KEY_USAGE_EC, status, 0);
goto err_free_subject;
}
}
if (eku != NULL) {
status = x509_mbedtls_add_extended_key_usage_extension (&x509.extensions, (char*) eku,
eku_length, false);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_EXT_KEY_USAGE_EC, status, 0);
goto err_free_subject;
}
}
if (type) {
status = x509_mbedtls_add_basic_constraints_extension (&x509.extensions, true,
X509_CERT_PATHLEN (type));
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_BASIC_CONSTRAINTS_EC, status, 0);
goto err_free_subject;
}
}
for (i = 0; i < ext_count; i++) {
status = x509_mbedtls_add_custom_extension (extra_extensions[i], &x509.extensions);
if (status != 0) {
goto err_free_subject;
}
}
mbedtls_x509write_csr_set_md_alg (&x509, md_alg);
status = mbedtls_x509write_csr_der (&x509, mbedtls->state->der_buf, X509_MAX_SIZE,
mbedtls_ctr_drbg_random, &mbedtls->state->ctr_drbg);
if (status < 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_CSR_DER_WRITE_EC, status, 0);
goto err_free_subject;
}
*csr = platform_malloc (status);
if (*csr == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_subject;
}
memcpy (*csr, &mbedtls->state->der_buf[X509_MAX_SIZE - status], status);
*csr_length = status;
status = 0;
err_free_subject:
platform_free (subject);
err_free_key:
mbedtls_pk_free (&key);
err_free_csr:
mbedtls_x509write_csr_free (&x509);
return status;
}
/**
* Create a new certificate. This can be self-signed, or signed by a CA.
*
* @param mbedtls The mbedTLS X.509 instance to use for creating the certificate.
* @param cert The instance to initialize with the new certificate.
* @param cert_key The key to use to create the certificate. For self-signed certificates, this
* must be a private key.
* @param sig_hash The hash algorithm to use for signing the certificate.
* @param serial_num The serial number to assign to the certificate.
* @param serial_length The length of the serial number.
* @param name The common name for the certificate subject.
* @param type The type of certificate to create.
* @param ca_key The private key of the CA to use for certificate signing. Set this to null for a
* self-signed certificate.
* @param ca_cert The certificate for the CA key. This is unused for a self-signed certificate and
* can be set to null.
* @param extra_extensions List of custom extensions that should be added to the certificate.
* @param ext_count The number of custom extensions to add.
*
* @return 0 if the certificate was successfully created or an error code.
*/
static int x509_mbedtls_create_certificate (const struct x509_engine_mbedtls *mbedtls,
struct x509_certificate *cert, mbedtls_pk_context *cert_key, enum hash_type sig_hash,
const uint8_t *serial_num, size_t serial_length, const char *name, int type,
mbedtls_pk_context *ca_key, const struct x509_certificate *ca_cert,
const struct x509_extension_builder *const *extra_extensions, size_t ext_count)
{
mbedtls_x509_crt *x509;
mbedtls_x509_crt *ca_x509;
mbedtls_x509write_cert x509_build;
mbedtls_pk_context *signing_key;
mbedtls_md_type_t md_alg;
char *subject;
size_t i;
int status;
switch (sig_hash) {
case HASH_TYPE_SHA256:
md_alg = MBEDTLS_MD_SHA256;
break;
case HASH_TYPE_SHA384:
md_alg = MBEDTLS_MD_SHA384;
break;
case HASH_TYPE_SHA512:
md_alg = MBEDTLS_MD_SHA512;
break;
default:
return X509_ENGINE_UNSUPPORTED_SIG_HASH;
}
if (ca_key) {
signing_key = ca_key;
ca_x509 = (mbedtls_x509_crt*) ca_cert->context;
}
else {
signing_key = cert_key;
ca_x509 = NULL;
}
mbedtls_x509write_crt_init (&x509_build);
mbedtls_x509write_crt_set_version (&x509_build, MBEDTLS_X509_CRT_VERSION_3);
mbedtls_x509write_crt_set_subject_key (&x509_build, cert_key);
mbedtls_x509write_crt_set_issuer_key (&x509_build, signing_key);
status = mbedtls_mpi_read_binary (&x509_build.serial, serial_num, serial_length);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_MPI_READ_BIN_EC, status, 0);
goto err_free_crt;
}
if (mbedtls_mpi_cmp_int (&x509_build.serial, 0) == 0) {
status = X509_ENGINE_INVALID_SERIAL_NUM;
goto err_free_crt;
}
subject = platform_malloc (strlen (name) + 4);
if (subject == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_crt;
}
strcpy (subject, "CN=");
strcpy (&subject[3], name);
status = mbedtls_x509write_crt_set_subject_name (&x509_build, subject);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_SET_SUBJECT_EC, status, 0);
goto err_free_subject;
}
if (!ca_key) {
status = mbedtls_x509write_crt_set_issuer_name (&x509_build, subject);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_SET_ISSUER_EC, status, 0);
}
}
else {
mbedtls_asn1_store_named_data (&x509_build.issuer, (char*) ca_x509->subject.oid.p,
ca_x509->subject.oid.len, ca_x509->subject.val.p, ca_x509->subject.val.len);
if (x509_build.issuer == NULL) {
status = X509_ENGINE_NO_MEMORY;
}
x509_build.issuer->val.tag = MBEDTLS_ASN1_UTF8_STRING;
}
if (status != 0) {
goto err_free_subject;
}
status = mbedtls_x509write_crt_set_validity (&x509_build, "20180101000000", "99991231235959");
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_SET_VALIDITY_EC, status, 0);
goto err_free_subject;
}
status = mbedtls_x509write_crt_set_subject_key_identifier (&x509_build);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_SET_SUBJECT_EC, status, 0);
goto err_free_subject;
}
status = mbedtls_x509write_crt_set_authority_key_identifier (&x509_build);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_SET_AUTHORITY_EC, status, 0);
goto err_free_subject;
}
if (type) {
status = x509_mbedtls_add_key_usage_extension (&x509_build.extensions,
MBEDTLS_X509_KU_KEY_CERT_SIGN);
}
else {
status = x509_mbedtls_add_key_usage_extension (&x509_build.extensions,
(MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT));
}
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_KEY_USAGE_EC, status, 0);
goto err_free_subject;
}
if (type == X509_CERT_END_ENTITY) {
status = x509_mbedtls_add_extended_key_usage_extension (&x509_build.extensions,
MBEDTLS_OID_CLIENT_AUTH, MBEDTLS_OID_SIZE (MBEDTLS_OID_CLIENT_AUTH), true);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_EXT_KEY_USAGE_EC, status, 0);
goto err_free_subject;
}
}
if (type) {
status = x509_mbedtls_add_basic_constraints_extension (&x509_build.extensions, true,
X509_CERT_PATHLEN (type));
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_ADD_BASIC_CONSTRAINTS_EC, status, 0);
goto err_free_subject;
}
}
for (i = 0; i < ext_count; i++) {
status = x509_mbedtls_add_custom_extension (extra_extensions[i], &x509_build.extensions);
if (status != 0) {
goto err_free_subject;
}
}
mbedtls_x509write_crt_set_md_alg (&x509_build, md_alg);
status = mbedtls_x509write_crt_der (&x509_build, mbedtls->state->der_buf, X509_MAX_SIZE,
mbedtls_ctr_drbg_random, &mbedtls->state->ctr_drbg);
if (status < 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_WRITE_DER_EC, status, 0);
goto err_free_subject;
}
x509 = x509_mbedtls_new_cert ();
if (x509 == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_subject;
}
status = mbedtls_x509_crt_parse_der (x509, &mbedtls->state->der_buf[X509_MAX_SIZE - status],
status);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_PARSE_DER_EC, status, 0);
x509_mbedtls_free_cert (x509);
goto err_free_subject;
}
cert->context = x509;
status = 0;
err_free_subject:
platform_free (subject);
err_free_crt:
mbedtls_x509write_crt_free (&x509_build);
return status;
}
int x509_mbedtls_create_self_signed_certificate (const struct x509_engine *engine,
struct x509_certificate *cert, const uint8_t *priv_key, size_t key_length,
enum hash_type sig_hash, const uint8_t *serial_num, size_t serial_length, const char *name,
int type, const struct x509_extension_builder *const *extra_extensions, size_t ext_count)
{
mbedtls_pk_context cert_key;
int status;
if ((engine == NULL) || (cert == NULL) || (priv_key == NULL) || (key_length == 0) ||
(serial_num == NULL) || (serial_length == 0) || (name == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if ((ext_count != 0) && (extra_extensions == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
status = x509_mbedtls_load_key (&cert_key, priv_key, key_length, true);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_LOAD_KEY_EC, status, 0);
goto err_exit;
}
status = x509_mbedtls_create_certificate ((const struct x509_engine_mbedtls*) engine, cert,
&cert_key, sig_hash, serial_num, serial_length, name, type, NULL, NULL, extra_extensions,
ext_count);
mbedtls_pk_free (&cert_key);
err_exit:
return status;
}
int x509_mbedtls_create_ca_signed_certificate (const struct x509_engine *engine,
struct x509_certificate *cert, const uint8_t *key, size_t key_length, const uint8_t *serial_num,
size_t serial_length, const char *name, int type, const uint8_t *ca_priv_key,
size_t ca_key_length, enum hash_type sig_hash, const struct x509_certificate *ca_cert,
const struct x509_extension_builder *const *extra_extensions, size_t ext_count)
{
mbedtls_pk_context cert_key;
mbedtls_pk_context ca_key;
int status;
if ((engine == NULL) || (cert == NULL) || (key == NULL) || (key_length == 0) ||
(serial_num == NULL) || (serial_length == 0) || (name == NULL) || (ca_priv_key == NULL) ||
(ca_key_length == 0) || (ca_cert == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if ((ext_count != 0) && (extra_extensions == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
status = x509_mbedtls_load_key (&cert_key, key, key_length, false);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_LOAD_KEY_EC, status, 0);
goto err_exit;
}
status = x509_mbedtls_load_key (&ca_key, ca_priv_key, ca_key_length, true);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_X509_LOAD_KEY_EC, status, 0);
goto err_free_key;
}
status = x509_mbedtls_create_certificate ((const struct x509_engine_mbedtls*) engine, cert,
&cert_key, sig_hash, serial_num, serial_length, name, type, &ca_key, ca_cert,
extra_extensions, ext_count);
mbedtls_pk_free (&ca_key);
err_free_key:
mbedtls_pk_free (&cert_key);
err_exit:
return status;
}
#endif
int x509_mbedtls_load_certificate (const struct x509_engine *engine, struct x509_certificate *cert,
const uint8_t *der, size_t length)
{
mbedtls_x509_crt *x509;
int status;
if ((engine == NULL) || (cert == NULL) || (der == NULL) || (length == 0)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
x509 = x509_mbedtls_new_cert ();
if (x509 == NULL) {
return X509_ENGINE_NO_MEMORY;
}
status = mbedtls_x509_crt_parse_der (x509, der, length);
if (status == 0) {
cert->context = x509;
}
else {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_PARSE_DER_EC, status, 0);
x509_mbedtls_free_cert (x509);
}
return status;
}
void x509_mbedtls_release_certificate (const struct x509_engine *engine,
struct x509_certificate *cert)
{
UNUSED (engine);
if (cert) {
x509_mbedtls_free_cert (cert->context);
memset (cert, 0, sizeof (struct x509_certificate));
}
}
#ifdef X509_ENABLE_CREATE_CERTIFICATES
int x509_mbedtls_get_certificate_der (const struct x509_engine *engine,
const struct x509_certificate *cert, uint8_t **der, size_t *length)
{
mbedtls_x509_crt *x509;
if (der == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
*der = NULL;
if ((engine == NULL) || (cert == NULL) || (length == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
x509 = (mbedtls_x509_crt*) cert->context;
*der = platform_malloc (x509->raw.len);
if (*der == NULL) {
return X509_ENGINE_NO_MEMORY;
}
memcpy (*der, x509->raw.p, x509->raw.len);
*length = x509->raw.len;
return 0;
}
#endif
#ifdef X509_ENABLE_AUTHENTICATION
int x509_mbedtls_get_certificate_version (const struct x509_engine *engine,
const struct x509_certificate *cert)
{
if ((engine == NULL) || (cert == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
return ((mbedtls_x509_crt*) cert->context)->version;
}
int x509_mbedtls_get_serial_number (const struct x509_engine *engine,
const struct x509_certificate *cert, uint8_t *serial_num, size_t length)
{
mbedtls_x509_crt *x509;
if ((engine == NULL) || (cert == NULL) || (serial_num == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
x509 = (mbedtls_x509_crt*) cert->context;
if (length < x509->serial.len) {
return X509_ENGINE_SMALL_SERIAL_BUFFER;
}
memcpy (serial_num, x509->serial.p, x509->serial.len);
return x509->serial.len;
}
int x509_mbedtls_get_public_key_type (const struct x509_engine *engine,
const struct x509_certificate *cert)
{
if ((engine == NULL) || (cert == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
switch (mbedtls_pk_get_type (&((mbedtls_x509_crt*) cert->context)->pk)) {
case MBEDTLS_PK_ECKEY:
return X509_PUBLIC_KEY_ECC;
case MBEDTLS_PK_RSA:
return X509_PUBLIC_KEY_RSA;
case MBEDTLS_PK_NONE:
return X509_ENGINE_UNKNOWN_KEY_TYPE;
default:
return X509_ENGINE_UNSUPPORTED_KEY_TYPE;
}
}
int x509_mbedtls_get_public_key_length (const struct x509_engine *engine,
const struct x509_certificate *cert)
{
if ((engine == NULL) || (cert == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
return mbedtls_pk_get_bitlen (&((mbedtls_x509_crt*) (cert->context))->pk);
}
int x509_mbedtls_get_public_key (const struct x509_engine *engine,
const struct x509_certificate *cert, uint8_t **key, size_t *key_length)
{
const struct x509_engine_mbedtls *mbedtls = (const struct x509_engine_mbedtls*) engine;
int status;
if (key == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
*key = NULL;
if ((mbedtls == NULL) || (cert == NULL) || (key_length == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
status = mbedtls_pk_write_pubkey_der (&((mbedtls_x509_crt*) cert->context)->pk,
mbedtls->state->der_buf, X509_MAX_SIZE);
if (status < 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_PK_WRITE_PUBKEY_DER_EC, status, 0);
return status;
}
*key = platform_malloc (status);
if (*key == NULL) {
return X509_ENGINE_NO_MEMORY;
}
memcpy (*key, &mbedtls->state->der_buf[X509_MAX_SIZE - status], status);
*key_length = status;
return 0;
}
/**
* Verify only the signature of a certificate.
*
* @param cert The certificate to verify.
* @param key The key to use for verification.
*
* @return 0 if the signature is valid or an error code.
*/
static int x509_mbedtls_verify_cert_signature (mbedtls_x509_crt *cert, mbedtls_pk_context *key)
{
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
const mbedtls_md_info_t *md_info;
int status;
md_info = mbedtls_md_info_from_type (cert->sig_md);
if (md_info == NULL) {
return X509_ENGINE_UNSUPPORTED_SIG_TYPE;
}
mbedtls_md (md_info, cert->tbs.p, cert->tbs.len, hash);
status = mbedtls_pk_verify_ext (cert->sig_pk, cert->sig_opts, key, cert->sig_md, hash,
mbedtls_md_get_size (md_info), cert->sig.p, cert->sig.len);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_PK_VERIFY_EC, status, 0);
}
return status;
}
/**
* Indicate if a certificate is self-signed.
*
* @param x509 The certificate to check.
*
* @return true if the certificate is self-signed or false if not.
*/
static bool x509_mbedtls_is_self_signed (mbedtls_x509_crt *x509)
{
if ((x509->issuer_raw.len == x509->subject_raw.len) &&
(memcmp (x509->issuer_raw.p, x509->subject_raw.p, x509->issuer_raw.len) == 0)) {
return true;
}
else {
return false;
}
}
int x509_mbedtls_init_ca_cert_store (const struct x509_engine *engine, struct x509_ca_certs *store)
{
struct x509_mbedtls_ca_store_context *store_ctx;
if ((engine == NULL) || (store == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
store_ctx = platform_malloc (sizeof (struct x509_mbedtls_ca_store_context));
if (store_ctx == NULL) {
return X509_ENGINE_NO_MEMORY;
}
memset (store_ctx, 0, sizeof (struct x509_mbedtls_ca_store_context));
store->context = store_ctx;
return 0;
}
void x509_mbedtls_release_ca_cert_store (const struct x509_engine *engine,
struct x509_ca_certs *store)
{
UNUSED (engine);
if (store && store->context) {
struct x509_mbedtls_ca_store_context *store_ctx = store->context;
mbedtls_x509_crt_free (store_ctx->root_ca);
platform_free (store_ctx->root_ca);
mbedtls_x509_crt_free (store_ctx->intermediate);
platform_free (store_ctx->intermediate);
platform_free (store_ctx);
memset (store, 0, sizeof (struct x509_ca_certs));
}
}
/**
* Load a CA certificate and add it to a certificate chain.
*
* @param engine The X.509 instance to use for loading the certificate.
* @param chain The certificate chain to update with the CA certificate.
* @param der DER encoded data for the CA certificate.
* @param length Length of the DER encoded data.
* @param is_root Flag indicating if the certificate is a root CA.
*
* @return 0 if the certificate chain was updated successfully or an error code.
*/
static int x509_mbedtls_add_ca_to_cert_chain (const struct x509_engine *engine,
mbedtls_x509_crt **chain, const uint8_t *der, size_t length, bool is_root)
{
struct x509_certificate cert;
mbedtls_x509_crt *x509;
int status;
status = x509_mbedtls_load_certificate (engine, &cert, der, length);
if (status != 0) {
goto err_exit;
}
x509 = (mbedtls_x509_crt*) cert.context;
if (!x509->ca_istrue) {
status = X509_ENGINE_NOT_CA_CERT;
goto err_free_cert;
}
if (is_root) {
/* Root CAs must be self signed and have a valid signature. */
if (!x509_mbedtls_is_self_signed (x509)) {
status = X509_ENGINE_NOT_SELF_SIGNED;
goto err_free_cert;
}
status = x509_mbedtls_verify_cert_signature (x509, &x509->pk);
if (status != 0) {
status = X509_ENGINE_BAD_SIGNATURE;
goto err_free_cert;
}
}
else {
/* ICAs must not be self signed. */
if (x509_mbedtls_is_self_signed (x509)) {
status = X509_ENGINE_IS_SELF_SIGNED;
goto err_free_cert;
}
}
x509->next = *chain;
*chain = x509;
return 0;
err_free_cert:
x509_mbedtls_release_certificate (engine, &cert);
err_exit:
return status;
}
int x509_mbedtls_add_root_ca (const struct x509_engine *engine, struct x509_ca_certs *store,
const uint8_t *der, size_t length)
{
if (store == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
return x509_mbedtls_add_ca_to_cert_chain (engine,
&((struct x509_mbedtls_ca_store_context*) store->context)->root_ca, der, length, true);
}
int x509_mbedtls_add_trusted_ca (const struct x509_engine *engine, struct x509_ca_certs *store,
const uint8_t *der, size_t length)
{
if (store == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
return x509_mbedtls_add_ca_to_cert_chain (engine,
&((struct x509_mbedtls_ca_store_context*) store->context)->root_ca, der, length, false);
}
int x509_mbedtls_add_intermediate_ca (const struct x509_engine *engine, struct x509_ca_certs *store,
const uint8_t *der, size_t length)
{
if (store == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
return x509_mbedtls_add_ca_to_cert_chain (engine,
&((struct x509_mbedtls_ca_store_context*) store->context)->intermediate, der, length,
false);
}
int x509_mbedtls_authenticate (const struct x509_engine *engine,
const struct x509_certificate *cert, const struct x509_ca_certs *store)
{
const struct x509_engine_mbedtls *mbedtls = (const struct x509_engine_mbedtls*) engine;
struct x509_mbedtls_ca_store_context *store_ctx;
mbedtls_x509_crt *x509;
int status;
uint32_t validation;
if ((mbedtls == NULL) || (cert == NULL) || (store == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
store_ctx = store->context;
x509 = (mbedtls_x509_crt*) cert->context;
x509->next = store_ctx->intermediate;
status = mbedtls_x509_crt_verify (x509, store_ctx->root_ca, NULL, NULL, &validation, NULL,
NULL);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CRT_CERT_AUTHENTICATE_EC, status, validation);
}
if (status == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
status = X509_ENGINE_CERT_NOT_VALID;
}
x509->next = NULL;
return status;
}
#endif
/**
* Initialize an instance for handling X.509 certificates using mbedTLS.
*
* @param engine The X.509 engine to initialize.
* @param state Variable context for X.509 operations. This must be uninitialized.
*
* @return 0 if the X.509 engine was successfully initialized or an error code.
*/
int x509_mbedtls_init (struct x509_engine_mbedtls *engine, struct x509_engine_mbedtls_state *state)
{
if (engine == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
memset (engine, 0, sizeof (struct x509_engine_mbedtls));
#ifdef X509_ENABLE_CREATE_CERTIFICATES
engine->base.create_csr = x509_mbedtls_create_csr;
engine->base.create_self_signed_certificate = x509_mbedtls_create_self_signed_certificate;
engine->base.create_ca_signed_certificate = x509_mbedtls_create_ca_signed_certificate;
#endif
engine->base.load_certificate = x509_mbedtls_load_certificate;
engine->base.release_certificate = x509_mbedtls_release_certificate;
#ifdef X509_ENABLE_CREATE_CERTIFICATES
engine->base.get_certificate_der = x509_mbedtls_get_certificate_der;
#endif
#ifdef X509_ENABLE_AUTHENTICATION
engine->base.get_certificate_version = x509_mbedtls_get_certificate_version;
engine->base.get_serial_number = x509_mbedtls_get_serial_number;
engine->base.get_public_key_type = x509_mbedtls_get_public_key_type;
engine->base.get_public_key_length = x509_mbedtls_get_public_key_length;
engine->base.get_public_key = x509_mbedtls_get_public_key;
engine->base.init_ca_cert_store = x509_mbedtls_init_ca_cert_store;
engine->base.release_ca_cert_store = x509_mbedtls_release_ca_cert_store;
engine->base.add_root_ca = x509_mbedtls_add_root_ca;
engine->base.add_trusted_ca = x509_mbedtls_add_trusted_ca;
engine->base.add_intermediate_ca = x509_mbedtls_add_intermediate_ca;
engine->base.authenticate = x509_mbedtls_authenticate;
#endif
engine->state = state;
return x509_mbedtls_init_state (engine);
}
/**
* Initialize only the variable state of on mbedTLS X.509 engine. The rest of the instance is
* assumed to already have been initialized.
*
* This would generally be used with a statically initialized instance.
*
* @param engine The X.509 engine that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int x509_mbedtls_init_state (const struct x509_engine_mbedtls *engine)
{
int status;
if ((engine == NULL) || (engine->state == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
memset (engine->state, 0, sizeof (*engine->state));
mbedtls_ctr_drbg_init (&engine->state->ctr_drbg);
mbedtls_entropy_init (&engine->state->entropy);
status = mbedtls_ctr_drbg_seed (&engine->state->ctr_drbg, mbedtls_entropy_func,
&engine->state->entropy, NULL, 0);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CRYPTO,
CRYPTO_LOG_MSG_MBEDTLS_CTR_DRBG_SEED_EC, status, 0);
goto exit;
}
return 0;
exit:
mbedtls_entropy_free (&engine->state->entropy);
mbedtls_ctr_drbg_free (&engine->state->ctr_drbg);
return status;
}
/**
* Release an mbedTLS X.509 engine.
*
* @param engine The X.509 engine to release.
*/
void x509_mbedtls_release (const struct x509_engine_mbedtls *engine)
{
if (engine) {
mbedtls_entropy_free (&engine->state->entropy);
mbedtls_ctr_drbg_free (&engine->state->ctr_drbg);
}
}