533 lines
14 KiB
C++
533 lines
14 KiB
C++
//***************************************************************************
|
|
// apps/crypto/controlse/ccertificate.cxx
|
|
//
|
|
// Licensed to the Apache Software Foundation (ASF) under one or more
|
|
// contributor license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright ownership. The
|
|
// ASF licenses this file to you 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
|
|
//
|
|
// http://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.
|
|
//
|
|
//***************************************************************************
|
|
|
|
// Copyright 2024 NXP
|
|
|
|
//***************************************************************************
|
|
// Included Files
|
|
//***************************************************************************
|
|
|
|
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
|
#include "crypto/controlse/ccertificate.hxx"
|
|
|
|
#include "cmbedtls_se05x_extension.hxx"
|
|
#include "crypto/controlse/chex_util.hxx"
|
|
#include "crypto/controlse/cpublic_key.hxx"
|
|
#include "crypto/controlse/cserial_number.hxx"
|
|
#include "crypto/controlse/isecure_element.hxx"
|
|
#include <mbedtls/oid.h>
|
|
#include <mbedtls/pem.h>
|
|
#include <mbedtls/sha256.h>
|
|
#include <mbedtls/x509_crt.h>
|
|
#include <mbedtls/x509_csr.h>
|
|
#include <string.h>
|
|
|
|
namespace Controlse
|
|
{
|
|
|
|
//***************************************************************************
|
|
// Private Data
|
|
//***************************************************************************
|
|
|
|
static constexpr int SECONDS_IN_DAY = (60 * 60 * 24);
|
|
static constexpr size_t TBS_HASH_BUFFER_SIZE = 32;
|
|
|
|
static constexpr char certificate_header[] = "-----BEGIN CERTIFICATE-----\n";
|
|
static constexpr char certificate_footer[] = "-----END CERTIFICATE-----\n";
|
|
static constexpr size_t datetime_size = 15;
|
|
|
|
//***************************************************************************
|
|
// Class Method Implementations
|
|
//***************************************************************************
|
|
|
|
CCertificate::CCertificate(const ISecureElement &se, uint32_t keystore_id)
|
|
{
|
|
mbedtls_x509_crt_init(&crt);
|
|
is_loaded = LoadFromSecureElement(se, keystore_id);
|
|
}
|
|
|
|
CCertificate::CCertificate(const uint8_t *crt_der_or_pem, size_t crt_size)
|
|
{
|
|
mbedtls_x509_crt_init(&crt);
|
|
is_loaded = LoadFromDerOrPem(crt_der_or_pem, crt_size);
|
|
}
|
|
|
|
static void GetCurrentDateTime(char datetime[datetime_size], int seconds)
|
|
{
|
|
time_t rawtime;
|
|
struct tm tm_info;
|
|
time(&rawtime);
|
|
rawtime += seconds;
|
|
strftime(datetime, datetime_size, "%Y%m%d%H%M%S",
|
|
gmtime_r(&rawtime, &tm_info));
|
|
}
|
|
|
|
CCertificate::CCertificate(const ISecureElement &se,
|
|
const uint8_t *csr_der_or_pem, size_t csr_size,
|
|
uint32_t keystore_id)
|
|
{
|
|
mbedtls_x509_crt_init(&crt);
|
|
char from_datetime[datetime_size];
|
|
char to_datetime[datetime_size];
|
|
GetCurrentDateTime(from_datetime, 0);
|
|
GetCurrentDateTime(to_datetime, SECONDS_IN_DAY);
|
|
is_loaded = LoadFromCsrDerOrPem(se, csr_der_or_pem, csr_size, keystore_id,
|
|
from_datetime, to_datetime);
|
|
}
|
|
|
|
CCertificate::CCertificate(const ISecureElement &se,
|
|
const uint8_t *csr_der_or_pem, size_t csr_size,
|
|
uint32_t keystore_id, const char *from_datetime,
|
|
const char *to_datetime)
|
|
{
|
|
mbedtls_x509_crt_init(&crt);
|
|
is_loaded = LoadFromCsrDerOrPem(se, csr_der_or_pem, csr_size, keystore_id,
|
|
from_datetime, to_datetime);
|
|
}
|
|
|
|
CCertificate::~CCertificate() { mbedtls_x509_crt_free(&crt); }
|
|
|
|
bool CCertificate::IsLoaded() const { return is_loaded; }
|
|
|
|
bool CCertificate::StoreOnSecureElement(const ISecureElement &se,
|
|
uint32_t keystore_id) const
|
|
{
|
|
struct se05x_key_transmission_s args;
|
|
args.entry.id = keystore_id;
|
|
args.content.buffer = crt.raw.p;
|
|
args.content.buffer_size = crt.raw.len;
|
|
args.content.buffer_content_size = crt.raw.len;
|
|
return se.SetData(args);
|
|
}
|
|
|
|
bool CCertificate::LoadFromSecureElement(const ISecureElement &se,
|
|
uint32_t keystore_id)
|
|
{
|
|
size_t certificate_der_size = 1000;
|
|
uint8_t *certificate_der = new uint8_t[certificate_der_size];
|
|
struct se05x_key_transmission_s args
|
|
= { .entry = { .id = keystore_id },
|
|
.content = { .buffer = certificate_der,
|
|
.buffer_size = certificate_der_size } };
|
|
bool result = se.GetData(args);
|
|
|
|
if (result)
|
|
{
|
|
result = 0
|
|
== mbedtls_x509_crt_parse(&crt, certificate_der,
|
|
args.content.buffer_content_size);
|
|
}
|
|
|
|
delete[] certificate_der;
|
|
return result;
|
|
}
|
|
|
|
bool CCertificate::LoadFromDerOrPem(const uint8_t *crt_der_or_pem,
|
|
size_t crt_size)
|
|
{
|
|
return 0 == mbedtls_x509_crt_parse(&crt, crt_der_or_pem, crt_size);
|
|
}
|
|
|
|
bool CCertificate::LoadFromCsrDerOrPem(const ISecureElement &se,
|
|
const uint8_t *csr_der_or_pem,
|
|
size_t csr_size, uint32_t keystore_id,
|
|
const char *from_datetime,
|
|
const char *to_datetime)
|
|
{
|
|
mbedtls_x509_csr csr;
|
|
mbedtls_x509_csr_init(&csr);
|
|
auto result = 0 == mbedtls_x509_csr_parse(&csr, csr_der_or_pem, csr_size);
|
|
|
|
mbedtls_x509write_cert crt_builder;
|
|
mbedtls_x509write_crt_init(&crt_builder);
|
|
char subject_name[200];
|
|
if (result)
|
|
{
|
|
mbedtls_x509write_crt_set_version(&crt_builder,
|
|
MBEDTLS_X509_CRT_VERSION_3);
|
|
result = 0 < mbedtls_x509_dn_gets(subject_name, sizeof(subject_name),
|
|
&csr.subject);
|
|
}
|
|
|
|
mbedtls_pk_context key;
|
|
mbedtls_pk_init(&key);
|
|
if (result)
|
|
{
|
|
result = 0
|
|
== MbedtlsSe05xExtension::mbedtls_pk_setup_key_se05x(
|
|
key, se, keystore_id);
|
|
}
|
|
|
|
mbedtls_pk_context public_key;
|
|
public_key.pk_ctx = csr.pk.pk_ctx;
|
|
public_key.pk_info = csr.pk.pk_info;
|
|
|
|
// Invalidate the public key in CSR
|
|
// The public key is transferred to CRT so the CSR may not free the memory
|
|
csr.pk.pk_ctx = nullptr;
|
|
csr.pk.pk_info = nullptr;
|
|
mbedtls_x509_csr_free(&csr);
|
|
|
|
if (result)
|
|
{
|
|
mbedtls_x509write_crt_set_subject_key(&crt_builder, &public_key);
|
|
mbedtls_x509write_crt_set_issuer_key(&crt_builder, &key);
|
|
result = 0
|
|
== mbedtls_x509write_crt_set_subject_name(&crt_builder,
|
|
subject_name);
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
result = 0
|
|
== mbedtls_x509write_crt_set_issuer_name(&crt_builder,
|
|
"CN=CA,O=controlse,C=NL");
|
|
}
|
|
|
|
mbedtls_mpi serial;
|
|
mbedtls_mpi_init(&serial);
|
|
if (result)
|
|
{
|
|
mbedtls_x509write_crt_set_md_alg(&crt_builder, MBEDTLS_MD_SHA256);
|
|
result = 0 == mbedtls_mpi_read_string(&serial, 10, "1");
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
result = 0 == mbedtls_x509write_crt_set_serial(&crt_builder, &serial);
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
result = 0
|
|
== mbedtls_x509write_crt_set_validity(
|
|
&crt_builder, from_datetime, to_datetime);
|
|
}
|
|
|
|
size_t buf_size = 1000;
|
|
uint8_t *buf = nullptr;
|
|
if (result)
|
|
{
|
|
buf = new uint8_t[buf_size];
|
|
result = buf != nullptr;
|
|
}
|
|
|
|
size_t buf_content_size;
|
|
if (result)
|
|
{
|
|
auto der_result
|
|
= mbedtls_x509write_crt_der(&crt_builder, buf, buf_size, nullptr, 0);
|
|
buf_content_size = der_result;
|
|
result = 0 < der_result;
|
|
}
|
|
|
|
mbedtls_x509write_crt_free(&crt_builder);
|
|
MbedtlsSe05xExtension::mbedtls_pk_free_se05x(key);
|
|
mbedtls_pk_free(&key);
|
|
mbedtls_pk_free(&public_key);
|
|
mbedtls_mpi_free(&serial);
|
|
|
|
if (result)
|
|
{
|
|
result = LoadFromDerOrPem(buf + buf_size - buf_content_size,
|
|
buf_content_size);
|
|
}
|
|
|
|
if (buf)
|
|
{
|
|
delete[] buf;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool CCertificate::VerifyAgainst(const ISecureElement &se,
|
|
uint32_t verify_against_id) const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint8_t tbs_buffer[TBS_HASH_BUFFER_SIZE];
|
|
bool result = 0 == mbedtls_sha256(crt.tbs.p, crt.tbs.len, tbs_buffer, 0);
|
|
|
|
if (result)
|
|
{
|
|
struct se05x_signature_s verify_args = {
|
|
.key_id = verify_against_id,
|
|
.algorithm = SE05X_ALGORITHM_SHA256,
|
|
.tbs = { .buffer = tbs_buffer,
|
|
.buffer_size = sizeof(tbs_buffer),
|
|
.buffer_content_size = sizeof(tbs_buffer) },
|
|
.signature = { .buffer = crt.sig.p,
|
|
.buffer_size = crt.sig.len,
|
|
.buffer_content_size = crt.sig.len },
|
|
};
|
|
result = se.Verify(verify_args);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool check_time(const mbedtls_x509_time *before,
|
|
const mbedtls_x509_time *after)
|
|
{
|
|
if (before->year > after->year)
|
|
return false;
|
|
|
|
if (before->year == after->year && before->mon > after->mon)
|
|
return false;
|
|
|
|
if (before->year == after->year && before->mon == after->mon
|
|
&& before->day > after->day)
|
|
return false;
|
|
|
|
if (before->year == after->year && before->mon == after->mon
|
|
&& before->day == after->day && before->hour > after->hour)
|
|
return false;
|
|
|
|
if (before->year == after->year && before->mon == after->mon
|
|
&& before->day == after->day && before->hour == after->hour
|
|
&& before->min > after->min)
|
|
return false;
|
|
|
|
if (before->year == after->year && before->mon == after->mon
|
|
&& before->day == after->day && before->hour == after->hour
|
|
&& before->min == after->min && before->sec > after->sec)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CCertificate::TestValidTimerange(time_t now) const
|
|
{
|
|
|
|
auto lt = gmtime(&now);
|
|
|
|
mbedtls_x509_time mbedtls_now;
|
|
mbedtls_now.year = lt->tm_year + 1900;
|
|
mbedtls_now.mon = lt->tm_mon + 1;
|
|
mbedtls_now.day = lt->tm_mday;
|
|
mbedtls_now.hour = lt->tm_hour;
|
|
mbedtls_now.min = lt->tm_min;
|
|
mbedtls_now.sec = lt->tm_sec;
|
|
|
|
if (!check_time(&mbedtls_now, &crt.valid_to))
|
|
{
|
|
return -1;
|
|
}
|
|
if (!check_time(&crt.valid_from, &mbedtls_now))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CPublicKey *CCertificate::GetPublicKey() const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
size_t root_key_buf_size = 100;
|
|
uint8_t *root_key_buf = new uint8_t[root_key_buf_size];
|
|
|
|
bool result = false;
|
|
if (root_key_buf)
|
|
{
|
|
mbedtls_ecp_keypair *keypair
|
|
= reinterpret_cast<mbedtls_ecp_keypair *>(crt.pk.pk_ctx);
|
|
result = 0
|
|
== mbedtls_ecp_point_write_binary(
|
|
&keypair->grp, &keypair->Q, MBEDTLS_ECP_PF_UNCOMPRESSED,
|
|
&root_key_buf_size, root_key_buf, root_key_buf_size);
|
|
}
|
|
|
|
CPublicKey *public_key
|
|
= result ? new CPublicKey(root_key_buf, root_key_buf_size) : nullptr;
|
|
|
|
if (root_key_buf)
|
|
{
|
|
delete[] root_key_buf;
|
|
}
|
|
|
|
return public_key;
|
|
}
|
|
|
|
char *CCertificate::GetOid(const char *oid) const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return nullptr;
|
|
}
|
|
auto item = mbedtls_asn1_find_named_data(&crt.subject, oid, 3);
|
|
|
|
if (item)
|
|
{
|
|
auto data = new char[item->val.len + 1];
|
|
memcpy(data, item->val.p, item->val.len);
|
|
data[item->val.len] = 0;
|
|
return data;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CSerialNumber *CCertificate::GetSerialNumber() const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (crt.serial.len == CSerialNumber::SERIAL_NUMBER_SIZE)
|
|
{
|
|
auto serial = new CSerialNumber(crt.serial.p);
|
|
return serial;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
size_t CCertificate::GetNumberOfSubjectAlternativeNames() const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return 0;
|
|
}
|
|
size_t size = 0;
|
|
auto current = &crt.subject_alt_names;
|
|
|
|
do
|
|
{
|
|
if (current->buf.tag
|
|
== (MBEDTLS_ASN1_CONTEXT_SPECIFIC
|
|
| MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER))
|
|
{
|
|
size++;
|
|
}
|
|
current = current->next;
|
|
}
|
|
while (current);
|
|
return size;
|
|
}
|
|
|
|
char *CCertificate::GetSubjectAlternativeName(int item) const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto current = &crt.subject_alt_names;
|
|
|
|
// go to first uri
|
|
while (current->buf.tag
|
|
!= (MBEDTLS_ASN1_CONTEXT_SPECIFIC
|
|
| MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER))
|
|
{
|
|
current = current->next;
|
|
if (current == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// go the next uri until we get to the correct item
|
|
for (int i = 0; i < item; i++)
|
|
{
|
|
do
|
|
{
|
|
current = current->next;
|
|
if (current == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
while (current->buf.tag
|
|
!= (MBEDTLS_ASN1_CONTEXT_SPECIFIC
|
|
| MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER));
|
|
}
|
|
|
|
auto san = new char[current->buf.len + 1];
|
|
if (san)
|
|
{
|
|
memcpy(san, current->buf.p, current->buf.len);
|
|
san[current->buf.len] = 0;
|
|
}
|
|
return san;
|
|
}
|
|
|
|
size_t CCertificate::GetDer(uint8_t **der) const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return 0;
|
|
}
|
|
*der = new uint8_t[crt.raw.len];
|
|
memcpy(*der, crt.raw.p, crt.raw.len);
|
|
return crt.raw.len;
|
|
}
|
|
|
|
char *CCertificate::GetPem() const
|
|
{
|
|
if (!is_loaded)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
char pem_buf[1000];
|
|
size_t pem_content_size;
|
|
auto result = mbedtls_pem_write_buffer(
|
|
certificate_header, certificate_footer, crt.raw.p, crt.raw.len,
|
|
(uint8_t *)pem_buf, sizeof(pem_buf), &pem_content_size);
|
|
if (result != 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto pem = new char[pem_content_size + 1];
|
|
memcpy(pem, pem_buf, pem_content_size);
|
|
pem[pem_content_size] = 0;
|
|
return pem;
|
|
}
|
|
|
|
bool CCertificate::ContainsSan(const char *name, size_t size) const
|
|
{
|
|
auto san_amount = GetNumberOfSubjectAlternativeNames();
|
|
for (size_t i = 0; i < san_amount; i++)
|
|
{
|
|
auto san = GetSubjectAlternativeName(i);
|
|
if (!san)
|
|
{
|
|
return false;
|
|
}
|
|
auto found = (memcmp(name, san, size) == 0);
|
|
delete[] san;
|
|
if (found)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace Controlse
|