AuroraRuntime/Source/Crypto/X509/AuX509.cpp

496 lines
15 KiB
C++
Raw Permalink Normal View History

2021-06-27 21:25:29 +00:00
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Aux509.cpp
2021-06-27 21:25:29 +00:00
Date: 2021-6-12
Author: Reece
***/
2021-09-30 14:57:41 +00:00
#include <Source/RuntimeInternal.hpp>
2021-06-27 21:25:29 +00:00
#include "../Crypto.hpp"
#include "x509.hpp"
#include <mbedtls/x509.h>
#include <mbedtls/oid.h>
[+] Network + Protocol + TLS - Initial Commit ============================================================================= Network ]==================================================================== ============================================================================= [+] Added (very) early Aurora::IO::Net implementation [+] AuNet::EHostnameType [+] AuNet::EIPProtocol [+] AuNet::ENetworkError [+] AuNet::ETransportProtocol [+] AuNet::INetInterface [+] AuNet::INetSrvDatagram [+] AuNet::INetSrvResolve [+] AuNet::INetSrvSockets [+] AuNet::INetSrvWorkers [+] AuNet::INetWorker [+] AuNet::IPAddress [+] AuNet::IResolver [+] AuNet::ISocket [+] AuNet::IResolver [+] AuNet::ISocketBase [+] AuNet::ISocketChannel [+] AuNet::ISocketDriver [+] AuNet::ISocketDriverFactory [+] AuNet::ISocketServer [+] AuNet::ISocketServerDriver [+] AuNet::NetEndpoint [+] AuNet::NetError [+] AuNet::NetHostname (+implementation) ============================================================================= Protocol ]=================================================================== ============================================================================= [+] IProtocolInterceptor [+] IProtocolInterceptorEx [+] IProtocolStack (+implementation) ============================================================================= TLS ]======================================================================== ============================================================================= [+] ITLSContext [+] TLSProtocolRecv [+] TLSProtocolSend (+implementation) ============================================================================= IO Bug Fixes ]=============================================================== ============================================================================= [*] IOProcessor::SubmitIOWorkItem should signal the CvEvent, forcing at least once future tick (wont optimize with if in tick & not yet dispatched work items) [*] Split IOPipeWork in into IOPipeProcessor header [+] IOPipeWork::GetBuffer (internal reallocation) [*] Harden against IAsyncTransactions without a loop source [*] Missing null `if (processor->listener)` in IOProcessor [*] Solved some soft-lock conditions under Linux's LoopQueue (added deferred commits) [*] Quick hack: IOProcessor::HasItems() should OR the early can-tick check function. ============================================================================= Other ]====================================================================== ============================================================================= [+] Linux: LSSignalCatcher [+] `static void AuResetMember(Aurora::Memory::ByteBuffer &ref)` for AuROXTL [*] Attempt to enforce a normalization and don't overwrite-readptr-under-istreamwriters policy in ByteBuffer_ReadWrite (circular buffers) [*] Bad ECC ctors ============================================================================= Known issues ]=============================================================== ============================================================================= > Linux net is nowhere near done > UDP socket emulation layer isn't implemented > Ciphersuite API is a stub > Private key API is a stub > ...therefore no TLS servers > Missing thread safety precautions under net > Net implementation is still beri early
2022-08-28 19:02:06 +00:00
//#include <mbedtls/certs.h>
2021-06-27 21:25:29 +00:00
#include <mbedtls/x509_crt.h>
#include <mbedtls/oid.h>
#include <mbedtls/asn1.h>
#define AURORA_OID_ID_AD MBEDTLS_OID_PKIX "\x30"
#define AURORA_OID_ID_PE_ONE MBEDTLS_OID_PKIX "\x01"
#define AURORA_OID_ID_PE_ONE_AIA AURORA_OID_ID_PE_ONE "\x01"
#define AURORA_OID_ID_AD_CA_ISSUERS AURORA_OID_ID_AD "\x02"
#define AURORA_OID_ID_AD_OCSP AURORA_OID_ID_AD "\x01"
namespace Aurora::Crypto::X509
{
#pragma region functions copied from mbedtls, modified to do extract the asn fields we care about
2022-01-19 17:08:13 +00:00
static int x509_get_crt_ext(mbedtls_x509_crt *crt, const char *oid, int oidLength, AuFunction<void(mbedtls_x509_buf &ex, unsigned char **, unsigned char *)> cb);
2021-06-27 21:25:29 +00:00
2022-01-20 16:37:22 +00:00
static int x509_get_ca_id(mbedtls_x509_crt *crt, AuByteBuffer &key)
2021-06-27 21:25:29 +00:00
{
bool ok = false;
return x509_get_crt_ext(crt, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER, sizeof(MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end)
{
int ret = 0;
size_t len;
if ((ret = mbedtls_asn1_get_tag(p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0)
{
ok = false;
return;
}
auto tag = **p;
(*p)++;
if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0)
{
ok = false;
return;
}
2021-09-06 10:58:08 +00:00
if (!AuTryResize(key, len))
2021-06-27 21:25:29 +00:00
{
ok = false;
return;
}
memcpy(key.data(), *p, key.size());
ok = true;
}) == 0 && ok;
}
2022-01-20 16:37:22 +00:00
static int x509_get_subject_id(mbedtls_x509_crt *crt, AuByteBuffer &key)
2021-06-27 21:25:29 +00:00
{
bool ok = false;
return x509_get_crt_ext(crt, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER, sizeof(MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end)
{
int ret = 0;
size_t len;
auto tag = **p;
(*p)++;
if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0)
{
ok = false;
return;
}
2021-09-06 10:58:08 +00:00
if (!AuTryResize(key, len))
2021-06-27 21:25:29 +00:00
{
ok = false;
return;
}
memcpy(key.data(), *p, key.size());
ok = true;
}) == 0 && ok;
}
static int x509_get_aia(mbedtls_x509_crt *crt, AuList<AuString> &ocsp, AuList<AuString> &caIssers)
{
bool ok = false;
return x509_get_crt_ext(crt, AURORA_OID_ID_PE_ONE_AIA, sizeof(AURORA_OID_ID_PE_ONE_AIA) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end)
{
int ret = 0;
size_t len, sublen;
/*
AuthorityInfoAccessSyntax ::=
SEQUENCE SIZE (1..MAX) OF AccessDescription
*/
if ((ret = mbedtls_asn1_get_tag(p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0)
{
ok = false;
return;
}
while (*p != end)
{
/*
AccessDescription ::= SEQUENCE {
accessMethod OBJECT IDENTIFIER,
accessLocation GeneralName }
*/
auto endOfResource = *p + len;
if ((ret = mbedtls_asn1_get_tag(p, endOfResource, &sublen,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0)
{
ok = false;
return;
}
// access method id
mbedtls_x509_buf oid = { 0, 0, NULL };
if ((ret = mbedtls_asn1_get_tag(p, *p + sublen, &oid.len,
MBEDTLS_ASN1_OID)) != 0)
{
ok = false;
return;
}
oid.tag = MBEDTLS_ASN1_OID;
oid.p = *p;
*p += oid.len;
// general name
mbedtls_x509_buf name = { 0, 0, NULL };
auto tag = **p;
(*p)++;
if ((ret = mbedtls_asn1_get_len(p, endOfResource, &name.len)) != 0)
{
ok = false;
return;
}
if ((tag & MBEDTLS_ASN1_TAG_CLASS_MASK) !=
MBEDTLS_ASN1_CONTEXT_SPECIFIC)
{
ok = false;
return;
}
name.tag = tag;
name.p = *p;
if (oid.len == sizeof(AURORA_OID_ID_AD_OCSP) - 1)
{
if (memcmp(oid.p, AURORA_OID_ID_AD_OCSP, oid.len) == 0)
{
ocsp.push_back(AuString(reinterpret_cast<const char *>(name.p), reinterpret_cast<const char *>(name.p) + name.len));
}
}
if (oid.len == sizeof(AURORA_OID_ID_AD_CA_ISSUERS) - 1)
{
if (memcmp(oid.p, AURORA_OID_ID_AD_CA_ISSUERS, oid.len) == 0)
{
caIssers.push_back(AuString(reinterpret_cast<const char *>(name.p), reinterpret_cast<const char *>(name.p) + name.len));
}
}
*p += name.len;
}
}) == 0 && ok;
}
static int x509_get_crt_ext(mbedtls_x509_crt *crt, const char *oid, int oidLength, AuFunction<void(mbedtls_x509_buf &ex, unsigned char **, unsigned char *)> cb)
2021-06-27 21:25:29 +00:00
{
int ret = 0;
size_t len;
unsigned char *end_ext_data, *start_ext_octet, *end_ext_octet;
unsigned char *scre = crt->v3_ext.p;
unsigned char **p = &scre;
auto end = crt->v3_ext.p + crt->v3_ext.len;
if ((ret = mbedtls_asn1_get_tag(p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret);
if (end != *p + len)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
while (*p < end)
{
/*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING }
*/
mbedtls_x509_buf extn_oid = { 0, 0, NULL };
int is_critical = 0; /* DEFAULT FALSE */
int ext_type = 0;
if ((ret = mbedtls_asn1_get_tag(p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret);
end_ext_data = *p + len;
/* Get extension ID */
if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &extn_oid.len,
MBEDTLS_ASN1_OID)) != 0)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret);
extn_oid.tag = MBEDTLS_ASN1_OID;
extn_oid.p = *p;
*p += extn_oid.len;
/* Get optional critical */
if ((ret = mbedtls_asn1_get_bool(p, end_ext_data, &is_critical)) != 0 &&
(ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG))
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret);
/* Data should be octet string type */
if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len,
MBEDTLS_ASN1_OCTET_STRING)) != 0)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret);
start_ext_octet = *p;
end_ext_octet = *p + len;
if (end_ext_octet != end_ext_data)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
if (extn_oid.len != oidLength)
{
*p = end_ext_octet;
continue;
}
if (memcmp(extn_oid.p, oid, oidLength))
{
*p = end_ext_octet;
continue;
}
cb(extn_oid, p, end_ext_octet);
return 0;
}
if (*p != end)
return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH);
return MBEDTLS_ERR_X509_INVALID_EXTENSIONS;
}
template<int C>
static bool find_oid_value_in_name(const mbedtls_x509_name *name, const char(&oid)[C], AuString &value)
{
const char *short_name = NULL;
size_t retval = 0;
while (name != NULL)
{
if (!name->oid.p)
{
name = name->next;
continue;
}
if (name->oid.len != (C - 1))
{
name = name->next;
continue;
}
if (memcmp(oid, name->oid.p, (C - 1)))
{
name = name->next;
continue;
}
value = AuString(reinterpret_cast<const char *>(name->val.p), reinterpret_cast<const char *>(name->val.p) + name->val.len);
return true;
}
return false;
}
#pragma endregion
static void FindCommonNames(const mbedtls_x509_name &name, CertificateName &out)
2021-06-27 21:25:29 +00:00
{
find_oid_value_in_name(&name, MBEDTLS_OID_AT_CN, out.commonName);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_COUNTRY, out.countryCode);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_ORGANIZATION, out.organization);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_ORG_UNIT, out.department);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_STATE, out.state);
find_oid_value_in_name(&name, MBEDTLS_OID_PKCS9_EMAIL, out.email);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_TITLE, out.title);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_GIVEN_NAME, out.name);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_POSTAL_CODE, out.postcode);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_POSTAL_ADDRESS, out.address);
find_oid_value_in_name(&name, MBEDTLS_OID_AT_LOCALITY, out.locality);
2021-06-27 21:25:29 +00:00
}
bool ParseCert(const AuMemoryViewRead &der, const AuFunction<void(mbedtls_x509_crt &crt)> &cb)
2021-06-27 21:25:29 +00:00
{
bool bRet {};
2021-06-27 21:25:29 +00:00
mbedtls_x509_crt crt {};
mbedtls_x509_crt_init(&crt);
auto status = mbedtls_x509_crt_parse(&crt,
der.Begin<unsigned char>(),
der.Size());
2021-06-27 21:25:29 +00:00
if (status < 0)
{
goto out;
}
2021-06-27 21:25:29 +00:00
try
{
if (cb)
{
cb(crt);
}
bRet = true;
}
catch (...)
{
SysPushErrorCatch();
}
2021-06-27 21:25:29 +00:00
out:
mbedtls_x509_crt_free(&crt);
return bRet;
2021-06-27 21:25:29 +00:00
}
AuInt64 ConvertTime(const mbedtls_x509_time &time)
2021-06-27 21:25:29 +00:00
{
AuTime::tm tm = {};
tm.year = time.year;
tm.mon = time.mon - 1;
tm.mday = time.day - 1;
tm.sec = time.sec;
tm.min = time.min;
tm.hour = time.hour;
return AuTime::FromCivilTime(tm, AuTime::ETimezoneShift::eUTC);
2021-06-27 21:25:29 +00:00
}
static void FindUsage(CertificateDecoded &out,
const mbedtls_x509_sequence *extended_key_usage)
{
const mbedtls_x509_sequence *pCur = extended_key_usage;
while (pCur)
{
#define CHECK_CHECK(enum, oid) \
if (pCur->buf.len == sizeof(oid) - 1) \
{ \
if (!AuMemcmp(oid, pCur->buf.p, pCur->buf.len)) \
{ \
out.usage.push_back(enum); \
} \
}
CHECK_CHECK(EExtendedUsage::eServerAuth, MBEDTLS_OID_SERVER_AUTH);
CHECK_CHECK(EExtendedUsage::eClientAuth, MBEDTLS_OID_CLIENT_AUTH);
CHECK_CHECK(EExtendedUsage::eCodeSigning, MBEDTLS_OID_CODE_SIGNING);
CHECK_CHECK(EExtendedUsage::eEmailProtection, MBEDTLS_OID_EMAIL_PROTECTION);
CHECK_CHECK(EExtendedUsage::eTimeStamping, MBEDTLS_OID_TIME_STAMPING);
CHECK_CHECK(EExtendedUsage::eOCSPSigning, MBEDTLS_OID_OCSP_SIGNING);
#undef CHECK_CHECK
pCur = pCur->next;
}
}
bool DecodeInternal(const mbedtls_x509_crt &crt, CertificateDecoded &out)
2021-06-27 21:25:29 +00:00
{
auto &issuer = crt.issuer;
auto &subject = crt.subject;
2021-06-27 21:25:29 +00:00
out.version = crt.version;
out.iMaxPath = crt.private_max_pathlen;
out.bIsCA = crt.private_ca_istrue;
FindUsage(out, &crt.ext_key_usage);
FindCommonNames(issuer, out.issuer);
FindCommonNames(subject, out.subject);
2021-06-27 21:25:29 +00:00
out.validity.issued = ConvertTime(crt.valid_from);
out.validity.expire = ConvertTime(crt.valid_to);
2021-06-27 21:25:29 +00:00
x509_get_ca_id((mbedtls_x509_crt *)&crt, out.issuer.id);
x509_get_subject_id((mbedtls_x509_crt *)&crt, out.subject.id);
x509_get_aia((mbedtls_x509_crt *)&crt, out.OCSPs, out.AIAs);
if (out.serialNumber.Write(crt.serial.p, crt.serial.len) != crt.serial.len)
{
return false;
}
2021-06-27 21:25:29 +00:00
if (out.algorithmOid.Write(crt.sig_oid.p, crt.sig_oid.len) != crt.sig_oid.len)
{
return false;
}
2021-06-27 21:25:29 +00:00
if (out.publicKey.Write(crt.pk_raw.p, crt.pk_raw.len) != crt.pk_raw.len)
{
return false;
}
2021-06-27 21:25:29 +00:00
return true;
}
2021-06-27 21:25:29 +00:00
AUKN_SYM bool Decode(const AuMemoryViewRead &der, CertificateDecoded &out)
{
bool bRet { true };
return ParseCert(der,
[&](mbedtls_x509_crt &crt)
{
bRet = DecodeInternal(crt, out);
}) && bRet;
2021-06-27 21:25:29 +00:00
}
static bool IsHighRiskStateIssuer(const mbedtls_x509_crt &ca)
{
AuString issuer;
if (!find_oid_value_in_name(&ca.issuer, MBEDTLS_OID_AT_COUNTRY, issuer))
{
// FAIL!
return true;
}
if (issuer.empty())
{
// FAIL!
return true;
}
// TODO: removed <on pastebin>
// We need some kind of shim objects for IPinCertificate &
// some kind of plan on how intrastructure that has to trust public exchange points will deal with state sponsored attacks.
// this old check isnt it.
2021-06-27 21:25:29 +00:00
return false;
}
AUKN_SYM bool Validate(const AuMemoryViewRead &der, const AuMemoryViewRead &parentDer)
2021-06-27 21:25:29 +00:00
{
bool failed = false;
// gross
return ParseCert(der,
[&](mbedtls_x509_crt &crt)
{
ParseCert(parentDer,
[&](mbedtls_x509_crt &ca)
{
if ((failed |= IsHighRiskStateIssuer(ca)))
2021-06-27 21:25:29 +00:00
{
return;
}
uint32_t flags {};
failed |= 0 > mbedtls_x509_crt_verify_restartable(&crt, &ca, NULL, &mbedtls_x509_crt_profile_default, NULL, &flags, NULL, NULL, NULL);
failed |= flags != 0;
2021-06-27 21:25:29 +00:00
});
}) && !failed;
}
}