core/asn1/x509_cert_build.c (682 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_cert_build.h"
#include "asn1/ecc_der_util.h"
#include "common/buffer_util.h"
#include "common/unused.h"
#include "crypto/ecc.h"
#include "crypto/ecdsa.h"
#include "riot/reference/include/RiotDerDec.h"
#include "riot/reference/include/RiotDerEnc.h"
#include "riot/reference/include/RiotX509Bldr.h"
/**
* Certificate validity period (UTCTime).
*/
#define VALID_FROM "180101000000Z"
#define VALID_TO "99991231235959Z"
/**
* Update the DER encoding and fail if the result is non-zero.
*
* TODO: Put this is a common location.
*/
#define DER_CHK_ENCODE(func) if ((status = (func)) != 0) {goto error;}
/**
* Create a new DER certificate instance.
*
* @param x509 The certificate builder to use for cert allocation.
*
* @return The allocated certificate or null.
*/
static DERBuilderContext* x509_cert_build_new_cert (const struct x509_engine_cert_build *x509)
{
DERBuilderContext *der;
uint8_t *der_buf;
der = platform_malloc (sizeof (DERBuilderContext));
if (der == NULL) {
return NULL;
}
der_buf = platform_malloc (x509->max_cert_length);
if (der_buf == NULL) {
platform_free (der);
return NULL;
}
DERInitContext (der, der_buf, x509->max_cert_length);
return der;
}
/**
* Free a DER certificate instance.
*
* @param cert The certificate to free.
*/
static void x509_cert_build_free_cert (void *cert)
{
DERBuilderContext *x509 = (DERBuilderContext*) cert;
if (x509) {
if (x509->Buffer) {
platform_free (x509->Buffer);
}
platform_free (x509);
}
}
#ifdef X509_ENABLE_CREATE_CERTIFICATES
/**
* Add a Subject Key Identifier extension to the certificate.
*
* @param der DER encoder to update.
* @param key_identifier The key identifier for the subject public key. This is typically a SHA1
* hash of the public key data.
* @param id_length Length the key key identifier.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_subject_key_identifier_extension (DERBuilderContext *der,
const uint8_t *key_identifier, size_t id_length)
{
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, extSubjectKeyIdentifierOID));
DER_CHK_ENCODE (DERStartEnvelopingOctetString (der));
DER_CHK_ENCODE (DERAddOctetString (der, key_identifier, id_length));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Add an Authority Key Identifier extension to the certificate.
*
* @param der DER encoder to update.
* @param key_identifier The key identifier for the issuer public key. This is typically a SHA1
* hash of the public key data.
* @param id_length Length the key key identifier.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_authority_key_identifier_extension (DERBuilderContext *der,
const uint8_t *key_identifier, size_t id_length)
{
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, extAuthKeyIdentifierOID));
DER_CHK_ENCODE (DERStartEnvelopingOctetString (der));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddAuthKeyBitString (der, key_identifier, id_length));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Add a Key Usage extension to the certificate.
*
* @param der DER encoder to update.
* @param type The type of certificate being created.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_key_usage_extension (DERBuilderContext *der, int type)
{
uint8_t key_usage;
uint8_t bits;
int status;
if (type != X509_CERT_END_ENTITY) {
key_usage = RIOT_X509_KEY_USAGE_CERT_SIGN;
bits = 6;
}
else {
key_usage = RIOT_X509_KEY_USAGE_END_ENTITY;
bits = 5;
}
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, keyUsageOID)); /* TODO: Statically encode all OIDs. */
DER_CHK_ENCODE (DERAddBoolean (der, true));
DER_CHK_ENCODE (DERStartEnvelopingOctetString (der));
DER_CHK_ENCODE (DERAddNamedBitString (der, &key_usage, 1, bits));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Add an Extended Key Usage extension to the certificate.
*
* @param der DER encoder to update.
* @param oid The key usage OID to add in the extension.
* @param oid_length Length of the key usage OID.
* @param critical True to indicate the EKU extension should be marked as critical.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_extended_key_usage_extension (DERBuilderContext *der,
const uint8_t *oid, size_t oid_length, bool critical)
{
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, extKeyUsageOID));
if (critical) {
DER_CHK_ENCODE (DERAddBoolean (der, true));
}
DER_CHK_ENCODE (DERStartEnvelopingOctetString (der));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
if (oid == NULL) {
DER_CHK_ENCODE (DERAddOID (der, clientAuthOID));
}
else {
DER_CHK_ENCODE (DERAddEncodedOID (der, oid, oid_length));
}
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Add a Basic Constraints extension to the certificate.
*
* @param der DER encoder to update.
* @param type The type of certificate being created.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_basic_constraints_extension (DERBuilderContext *der, int type)
{
int status;
/* End entity certificates don't need basic constraints. */
if (type != X509_CERT_END_ENTITY) {
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, basicConstraintsOID));
DER_CHK_ENCODE (DERAddBoolean (der, true));
DER_CHK_ENCODE (DERStartEnvelopingOctetString (der));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddBoolean (der, true));
if (type < X509_CERT_CA_NO_PATHLEN) {
DER_CHK_ENCODE (DERAddInteger (der, X509_CERT_PATHLEN (type)));
}
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
}
return 0;
error:
return status;
}
/**
* Add a custom extension to the certificate.
*
* @param der DER encoder to update.
* @param builder The extension to add.
*
* @return 0 if the extension was added successfully or an error code.
*/
static int x509_cert_build_add_custom_extension (DERBuilderContext *der,
const struct x509_extension_builder *builder)
{
struct x509_extension extension = {0};
int status;
if (builder == NULL) {
/* Silently skip null extensions. */
return 0;
}
status = builder->build (builder, &extension);
if (status != 0) {
return status;
}
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddEncodedOID (der, extension.oid, extension.oid_length));
if (extension.critical) {
DER_CHK_ENCODE (DERAddBoolean (der, true));
}
DER_CHK_ENCODE (DERAddOctetString (der, extension.data, extension.data_length));
DER_CHK_ENCODE (DERPopNesting (der));
builder->free (builder, &extension);
return 0;
error:
builder->free (builder, &extension);
return status;
}
/**
* Add an X.509 Name to the certificate.
*
* @param der DER encoder to update.
* @param common_name The common name to encode in the certificate.
*
* @return 0 if the name was added successfully or an error code.
*/
static int x509_cert_build_add_x509_name (DERBuilderContext *der, const char *common_name)
{
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, false));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, commonNameOID));
DER_CHK_ENCODE (DERAddUTF8String (der, common_name));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Build the TBS (To Be Signed) data for a CSR.
*
* @param der DER encoder to update.
* @param subject_name Common name for the subject of the CSR.
* @param pub_key_der DER encoded public key for the CSR.
* @param pub_key_length Length of the CSR public key.
* @param type The type of CSR being generated.
* @param eku Optional OID to assign to an Extended Key Usage extension. Only valid for CA CSRs.
* @param eku_length Length of the EKU OID.
* @param extra_extensions List of custom extensions that should be added to the CSR.
* @param ext_count The number of custom extensions in the list.
*
* @return 0 if the CSR TBS was generated successfully or an error code.
*/
static int x509_cert_build_build_csr_tbs_data (DERBuilderContext *der, const char *subject_name,
const uint8_t *pub_key_der, size_t pub_key_length, int type, const uint8_t *eku,
size_t eku_length, const struct x509_extension_builder *const *extra_extensions,
size_t ext_count)
{
size_t i;
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddInteger (der, 0));
DER_CHK_ENCODE (x509_cert_build_add_x509_name (der, subject_name));
DER_CHK_ENCODE (DERAddPublicKey (der, pub_key_der, pub_key_length));
DER_CHK_ENCODE (DERStartExplicit (der, 0));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, extensionRequestOID));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, false));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (x509_cert_build_add_key_usage_extension (der, type));
if ((type == X509_CERT_END_ENTITY) || (eku != NULL)) {
DER_CHK_ENCODE (x509_cert_build_add_extended_key_usage_extension (der, eku, eku_length,
(type == X509_CERT_END_ENTITY)));
}
DER_CHK_ENCODE (x509_cert_build_add_basic_constraints_extension (der, type));
for (i = 0; i < ext_count; i++) {
DER_CHK_ENCODE (x509_cert_build_add_custom_extension (der, extra_extensions[i]));
}
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
/**
* Sign the "To Be Signed" region of the certificate using ECDSA and update the certificate with the
* signature.
*
* @param der The DER encoder that contains the TBS certificate region to sign.
* @param priv_key The signing key.
* @param key_length Length of the signing key.
* @param ecc The ECC engine to use for signature generation.
* @param hash The hash engine to use for signature generation.
* @param sig_hash The type of hash to use when generating the signature.
* @param sig_oid OID indicating the type of signature being generated.
*
* @return 0 if the certificate was signed successfully or an error code.
*/
static int x509_cert_build_sign_certificate (DERBuilderContext *der, const uint8_t *priv_key,
size_t key_length, const struct ecc_engine *ecc, const struct hash_engine *hash,
enum hash_type sig_hash, const int *sig_oid)
{
uint8_t tbs_sig[ECC_DER_ECDSA_MAX_LENGTH] = {0};
int sig_len;
int status;
sig_len = ecdsa_sign_message (ecc, hash, sig_hash, NULL, priv_key, key_length, der->Buffer,
DERGetEncodedLength (der), tbs_sig, sizeof (tbs_sig));
if (ROT_IS_ERROR (sig_len)) {
return sig_len;
}
/* Update the DER encoding with the signature data. */
DER_CHK_ENCODE (DERTbsToCert (der));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, sig_oid));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERAddBitString (der, tbs_sig, sig_len));
DER_CHK_ENCODE (DERPopNesting (der));
buffer_zeroize (tbs_sig, sizeof (tbs_sig));
return 0;
error:
buffer_zeroize (tbs_sig, sizeof (tbs_sig));
return status;
}
int x509_cert_build_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_cert_build *x509 = (const struct x509_engine_cert_build*) engine;
DERBuilderContext *der;
struct ecc_public_key ecc_pub_key;
uint8_t *pub_key_der = NULL;
size_t pub_key_der_len;
const int *sig_oid;
int status;
if (csr == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
*csr = NULL;
if ((x509 == 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:
sig_oid = ecdsaWithSHA256OID;
break;
case HASH_TYPE_SHA384:
sig_oid = ecdsaWithSHA384OID;
break;
case HASH_TYPE_SHA512:
sig_oid = ecdsaWithSHA512OID;
break;
default:
return X509_ENGINE_UNSUPPORTED_SIG_HASH;
}
status = x509->ecc->init_key_pair (x509->ecc, priv_key, key_length, NULL, &ecc_pub_key);
if (status != 0) {
return status;
}
status = x509->ecc->get_public_key_der (x509->ecc, &ecc_pub_key, &pub_key_der,
&pub_key_der_len);
if (status != 0) {
goto err_free_key;
}
der = x509_cert_build_new_cert (x509);
if (der == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_key_der;
}
status = x509_cert_build_build_csr_tbs_data (der, name, pub_key_der, pub_key_der_len, type, eku,
eku_length, extra_extensions, ext_count);
if (status != 0) {
status = (status == -1) ? X509_ENGINE_CSR_FAILED : status;
goto err_free_cert;
}
status = x509_cert_build_sign_certificate (der, priv_key, key_length, x509->ecc, x509->hash,
sig_hash, sig_oid);
if (status != 0) {
status = (status == -1) ? X509_ENGINE_CSR_FAILED : status;
goto err_free_cert;
}
*csr_length = DERGetEncodedLength (der);
*csr = der->Buffer;
der->Buffer = NULL;
status = 0;
err_free_cert:
x509_cert_build_free_cert (der);
err_free_key_der:
platform_free (pub_key_der);
err_free_key:
x509->ecc->release_key_pair (x509->ecc, NULL, &ecc_pub_key);
return status;
}
/**
* Validate that the provided serial number is non-zero.
*
* @param serial_num The serial number to check.
* @param serial_length The length of the serial number.
*
* @return 0 if the serial number is valid or an error code.
*/
static int x509_cert_build_check_serial_number (const uint8_t *serial_num, size_t serial_length)
{
size_t i;
int status;
status = X509_ENGINE_INVALID_SERIAL_NUM;
for (i = 0; i < serial_length; i++) {
if (serial_num[i] != 0) {
return 0;
}
}
return status;
}
/**
* Build the TBS (To Be Signed) data for an X.509 certificate.
*
* @param der DER encoder to update.
* @param issuer_name Common name for the issuer for this certificate.
* @param auth_key_id SHA1 hash of the issuer's public key.
* @param subject_name Common name of the subject of this certificate.
* @param subject_key_id SHA1 hash of the subject's public key.
* @param serial_num The serial number to assign to the certificate.
* @param serial_length Length of the serial number.
* @param pub_key_der DER encoded public key for the certificate.
* @param pub_key_length Length of the certificate public key.
* @param sig_oid OID indicating the type of signature that will be generated for the certificate.
* @param type The type of certificate being generated.
* @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 TBS was generated successfully or an error code.
*/
static int x509_cert_build_build_certificate_tbs_data (DERBuilderContext *der,
const char *issuer_name, const uint8_t *auth_key_id, const char *subject_name,
const uint8_t *subject_key_id, const uint8_t *serial_num, size_t serial_length,
const uint8_t *pub_key_der, size_t pub_key_length, const int *sig_oid, int type,
const struct x509_extension_builder *const *extra_extensions, size_t ext_count)
{
size_t i;
int status;
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddShortExplicitInteger (der, 2));
DER_CHK_ENCODE (DERAddIntegerFromArray (der, serial_num, serial_length));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddOID (der, sig_oid));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (x509_cert_build_add_x509_name (der, issuer_name));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (DERAddTime (der, VALID_FROM));
DER_CHK_ENCODE (DERAddTime (der, VALID_TO));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (x509_cert_build_add_x509_name (der, subject_name));
DER_CHK_ENCODE (DERAddPublicKey (der, pub_key_der, pub_key_length));
DER_CHK_ENCODE (DERStartExplicit (der, 3));
DER_CHK_ENCODE (DERStartSequenceOrSet (der, true));
DER_CHK_ENCODE (x509_cert_build_add_subject_key_identifier_extension (der, subject_key_id,
SHA1_HASH_LENGTH));
DER_CHK_ENCODE (x509_cert_build_add_authority_key_identifier_extension (der, auth_key_id,
SHA1_HASH_LENGTH));
DER_CHK_ENCODE (x509_cert_build_add_key_usage_extension (der, type));
if (type == X509_CERT_END_ENTITY) {
DER_CHK_ENCODE (x509_cert_build_add_extended_key_usage_extension (der, NULL, 0, true));
}
DER_CHK_ENCODE (x509_cert_build_add_basic_constraints_extension (der, type));
for (i = 0; i < ext_count; i++) {
DER_CHK_ENCODE (x509_cert_build_add_custom_extension (der, extra_extensions[i]));
}
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
DER_CHK_ENCODE (DERPopNesting (der));
return 0;
error:
return status;
}
int x509_cert_build_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)
{
const struct x509_engine_cert_build *x509 = (const struct x509_engine_cert_build*) engine;
DERBuilderContext *der;
struct ecc_public_key ecc_pub_key;
uint8_t *pub_key_der = NULL;
size_t pub_key_der_len;
const uint8_t *auth_key;
uint8_t auth_key_digest[SHA1_DIGEST_LENGTH];
const int *sig_oid;
int status;
if ((x509 == 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;
}
switch (sig_hash) {
case HASH_TYPE_SHA256:
sig_oid = ecdsaWithSHA256OID;
break;
case HASH_TYPE_SHA384:
sig_oid = ecdsaWithSHA384OID;
break;
case HASH_TYPE_SHA512:
sig_oid = ecdsaWithSHA512OID;
break;
default:
return X509_ENGINE_UNSUPPORTED_SIG_HASH;
}
status = x509_cert_build_check_serial_number (serial_num, serial_length);
if (status != 0) {
return status;
}
cert->context = NULL;
status = x509->ecc->init_key_pair (x509->ecc, priv_key, key_length, NULL, &ecc_pub_key);
if (status != 0) {
return status;
}
status = x509->ecc->get_public_key_der (x509->ecc, &ecc_pub_key, &pub_key_der,
&pub_key_der_len);
if (status != 0) {
goto err_free_key;
}
status = ecc_der_decode_public_key_no_copy (pub_key_der, pub_key_der_len, &auth_key);
if (ROT_IS_ERROR (status)) {
/* This is highly unlikely since the encoded DER just came from the ECC engine. */
goto err_free_key;
}
status = x509->hash->calculate_sha1 (x509->hash, auth_key, status, auth_key_digest,
sizeof (auth_key_digest));
if (status != 0) {
goto err_free_key_der;
}
der = x509_cert_build_new_cert (x509);
if (der == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_key_der;
}
status = x509_cert_build_build_certificate_tbs_data (der, name, auth_key_digest, name,
auth_key_digest, serial_num, serial_length, pub_key_der, pub_key_der_len, sig_oid, type,
extra_extensions, ext_count);
if (status != 0) {
status = (status == -1) ? X509_ENGINE_SELF_SIGNED_FAILED : status;
goto err_free_cert;
}
status = x509_cert_build_sign_certificate (der, priv_key, key_length, x509->ecc, x509->hash,
sig_hash, sig_oid);
if (status == -1) {
status = X509_ENGINE_SELF_SIGNED_FAILED;
}
err_free_cert:
if (status == 0) {
cert->context = der;
}
else {
x509_cert_build_free_cert (der);
}
err_free_key_der:
platform_free (pub_key_der);
err_free_key:
x509->ecc->release_key_pair (x509->ecc, NULL, &ecc_pub_key);
return status;
}
int x509_cert_build_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)
{
const struct x509_engine_cert_build *x509 = (const struct x509_engine_cert_build*) engine;
DERBuilderContext *der;
DERBuilderContext *ca_ctx;
struct ecc_public_key auth_pub_key;
uint8_t *auth_key_der;
size_t auth_key_der_len;
const uint8_t *auth_key;
uint8_t auth_key_digest[SHA1_DIGEST_LENGTH];
struct ecc_public_key subject_pub_key;
const uint8_t *subject_key_der = key;
size_t subject_key_der_len = key_length;
const uint8_t *subject_key;
size_t subject_key_len;
uint8_t subject_key_digest[SHA1_DIGEST_LENGTH];
const int *sig_oid;
char *issuer = NULL;
int status;
if ((x509 == 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;
}
switch (sig_hash) {
case HASH_TYPE_SHA256:
sig_oid = ecdsaWithSHA256OID;
break;
case HASH_TYPE_SHA384:
sig_oid = ecdsaWithSHA384OID;
break;
case HASH_TYPE_SHA512:
sig_oid = ecdsaWithSHA512OID;
break;
default:
return X509_ENGINE_UNSUPPORTED_SIG_HASH;
}
status = x509_cert_build_check_serial_number (serial_num, serial_length);
if (status != 0) {
return status;
}
cert->context = NULL;
ca_ctx = (DERBuilderContext*) ca_cert->context;
status = x509->ecc->init_key_pair (x509->ecc, ca_priv_key, ca_key_length, NULL, &auth_pub_key);
if (status != 0) {
return status;
}
status = x509->ecc->get_public_key_der (x509->ecc, &auth_pub_key, &auth_key_der,
&auth_key_der_len);
if (status != 0) {
goto err_free_key;
}
status = ecc_der_decode_public_key_no_copy (auth_key_der, auth_key_der_len, &auth_key);
if (ROT_IS_ERROR (status)) {
/* This is highly unlikely since the encoded DER just came from the ECC engine. */
goto err_free_key;
}
status = x509->hash->calculate_sha1 (x509->hash, auth_key, status, auth_key_digest,
sizeof (auth_key_digest));
if (status != 0) {
goto err_free_key_der;
}
status = DERDECGetPubKey (&subject_key, &subject_key_len, key, key_length);
if (status != 0) {
/* The key data does not contain a public key. Get the public key from the private key. */
status = x509->ecc->init_key_pair (x509->ecc, key, key_length, NULL, &subject_pub_key);
if (status != 0) {
goto err_free_key_der;
}
status = x509->ecc->get_public_key_der (x509->ecc, &subject_pub_key,
(uint8_t**) &subject_key_der, &subject_key_der_len);
x509->ecc->release_key_pair (x509->ecc, NULL, &subject_pub_key);
if (status != 0) {
goto err_free_key_der;
}
status = ecc_der_decode_public_key_no_copy (subject_key_der, subject_key_der_len,
&subject_key);
if (ROT_IS_ERROR (status)) {
/* This is highly unlikely since the encoded DER just came from the ECC engine. */
goto err_free_key_der;
}
subject_key_len = status;
}
status = x509->hash->calculate_sha1 (x509->hash, subject_key, subject_key_len,
subject_key_digest, sizeof (subject_key_digest));
if (status != 0) {
goto err_free_key_der;
}
status = DERDECGetSubjectName (&issuer, ca_ctx->Buffer, DERGetEncodedLength (ca_ctx));
if (status != RIOT_SUCCESS) {
status = X509_ENGINE_CA_SIGNED_FAILED;
goto err_free_key_der;
}
der = x509_cert_build_new_cert (x509);
if (der == NULL) {
status = X509_ENGINE_NO_MEMORY;
goto err_free_name;
}
status = x509_cert_build_build_certificate_tbs_data (der, issuer, auth_key_digest, name,
subject_key_digest, serial_num, serial_length, subject_key_der, subject_key_der_len,
sig_oid, type, extra_extensions, ext_count);
if (status != 0) {
status = (status == -1) ? X509_ENGINE_CA_SIGNED_FAILED : status;
goto err_free_cert;
}
status = x509_cert_build_sign_certificate (der, ca_priv_key, ca_key_length, x509->ecc,
x509->hash, sig_hash, sig_oid);
if (status == -1) {
status = X509_ENGINE_CA_SIGNED_FAILED;
}
err_free_cert:
if (status == 0) {
cert->context = der;
}
else {
x509_cert_build_free_cert (der);
}
err_free_name:
platform_free (issuer);
err_free_key_der:
platform_free (auth_key_der);
if (subject_key_der != key) {
platform_free ((void*) subject_key_der);
}
err_free_key:
x509->ecc->release_key_pair (x509->ecc, NULL, &auth_pub_key);
return status;
}
#endif
int x509_cert_build_load_certificate (const struct x509_engine *engine,
struct x509_certificate *cert, const uint8_t *der, size_t length)
{
const struct x509_engine_cert_build *x509 = (const struct x509_engine_cert_build*) engine;
DERBuilderContext *load_cert;
int status;
if ((x509 == NULL) || (cert == NULL) || (der == NULL) || (length == 0)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
if (length > x509->max_cert_length) {
return X509_ENGINE_BIG_CERT_SIZE;
}
cert->context = NULL;
status = DERDECVerifyCert (der, length);
if (status != RIOT_SUCCESS) {
return X509_ENGINE_LOAD_FAILED;
}
load_cert = x509_cert_build_new_cert (x509);
if (load_cert == NULL) {
return X509_ENGINE_NO_MEMORY;
}
memcpy (load_cert->Buffer, der, length);
load_cert->Position = length;
cert->context = load_cert;
return 0;
}
void x509_cert_build_release_certificate (const struct x509_engine *engine,
struct x509_certificate *cert)
{
UNUSED (engine);
if (cert) {
x509_cert_build_free_cert (cert->context);
memset (cert, 0, sizeof (struct x509_certificate));
}
}
#ifdef X509_ENABLE_CREATE_CERTIFICATES
int x509_cert_build_get_certificate_der (const struct x509_engine *engine,
const struct x509_certificate *cert, uint8_t **der, size_t *length)
{
DERBuilderContext *cert_ctx;
size_t enc_len;
if (der == NULL) {
return X509_ENGINE_INVALID_ARGUMENT;
}
*der = NULL;
if ((engine == NULL) || (cert == NULL) || (length == NULL)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
cert_ctx = (DERBuilderContext*) cert->context;
enc_len = DERGetEncodedLength (cert_ctx);
*der = platform_malloc (enc_len);
if (*der == NULL) {
return X509_ENGINE_NO_MEMORY;
}
memcpy (*der, cert_ctx->Buffer, enc_len);
*length = enc_len;
return 0;
}
#endif
/**
* Initialize an instance for building X.509 certificates using a native ASN.1/DER encoder and
* abstracted crypto engines.
*
* @param engine The X.509 engine to initialize.
* @param ecc The ECC engine to use for ECC key operations.
* @param hash The hash engine to use for calculating digests.
* @param max_length The maximum certificate length that can be constructed.
*
* @return 0 if the X.509 engine was successfully initialized or an error code.
*/
int x509_cert_build_init (struct x509_engine_cert_build *engine, const struct ecc_engine *ecc,
const struct hash_engine *hash, size_t max_cert_length)
{
if ((engine == NULL) || (ecc == NULL) || (hash == NULL) || (max_cert_length == 0)) {
return X509_ENGINE_INVALID_ARGUMENT;
}
memset (engine, 0, sizeof (struct x509_engine_cert_build));
engine->ecc = ecc;
engine->hash = hash;
engine->max_cert_length = max_cert_length;
#ifdef X509_ENABLE_CREATE_CERTIFICATES
engine->base.create_csr = x509_cert_build_create_csr;
engine->base.create_self_signed_certificate = x509_cert_build_create_self_signed_certificate;
engine->base.create_ca_signed_certificate = x509_cert_build_create_ca_signed_certificate;
#endif
engine->base.load_certificate = x509_cert_build_load_certificate;
engine->base.release_certificate = x509_cert_build_release_certificate;
#ifdef X509_ENABLE_CREATE_CERTIFICATES
engine->base.get_certificate_der = x509_cert_build_get_certificate_der;
#endif
#ifdef X509_ENABLE_AUTHENTICATION
/* None of the authentication APIs are supported by this implementation and will remain NULL. */
#endif
return 0;
}
/**
* Release an X.509 engine for building certificates.
*
* @param engine The X.509 engine to release.
*/
void x509_cert_build_release (struct x509_engine_cert_build *engine)
{
UNUSED (engine);
}