nuttx-apps/crypto/controlse/ccertificate.cxx
2024-04-21 11:20:12 +08:00

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