feat(core/prodtest): Check device certificate chain integrity using authority key identifier.

[no changelog]
pull/3464/head
Andrew Kozlik 5 months ago committed by matejcik
parent a1be6f8aef
commit 8de2d8989d

@ -51,6 +51,18 @@ static const optiga_metadata_item KEY_USE_SIGN = {
static const optiga_metadata_item TYPE_PTFBIND = {
(const uint8_t[]){OPTIGA_DATA_TYPE_PTFBIND}, 1};
// Identifier of context-specific constructed tag 3, which is used for
// extensions in X.509.
#define DER_X509_EXTENSIONS 0xa3
// Identifier of context-specific primitive tag 0, which is used for
// keyIdentifier in authorityKeyIdentifier.
#define DER_X509_KEY_IDENTIFIER 0x80
// DER-encoded object identifier of the authority key identifier extension
// (id-ce-authorityKeyIdentifier).
const uint8_t OID_AUTHORITY_KEY_IDENTIFIER[] = {0x06, 0x03, 0x55, 0x1d, 0x23};
static bool optiga_paired(void) {
const char *details = "";
@ -540,26 +552,90 @@ static const uint8_t ECDSA_WITH_SHA256[] = {
};
// clang-format on
static const uint8_t ROOT_PUBLIC_KEYS[][65] = {
{
// Production root public key.
0x04, 0xca, 0x97, 0x48, 0x0a, 0xc0, 0xd7, 0xb1, 0xe6, 0xef, 0xaf,
0xe5, 0x18, 0xcd, 0x43, 0x3c, 0xec, 0x2b, 0xf8, 0xab, 0x98, 0x22,
0xd7, 0x6e, 0xaf, 0xd3, 0x43, 0x63, 0xb5, 0x5d, 0x63, 0xe6, 0x03,
0x80, 0xbf, 0xf2, 0x0a, 0xcc, 0x75, 0xcd, 0xe0, 0x3c, 0xff, 0xcb,
0x50, 0xab, 0x6f, 0x8c, 0xe7, 0x0c, 0x87, 0x8e, 0x37, 0xeb, 0xc5,
0x8f, 0xf7, 0xcc, 0xa0, 0xa8, 0x3b, 0x16, 0xb1, 0x5f, 0xa5,
},
{
// Development root public key.
0x04, 0x7f, 0x77, 0x36, 0x8d, 0xea, 0x2d, 0x4d, 0x61, 0xe9, 0x89,
0xf4, 0x74, 0xa5, 0x67, 0x23, 0xc3, 0x21, 0x2d, 0xac, 0xf8, 0xa8,
0x08, 0xd8, 0x79, 0x55, 0x95, 0xef, 0x38, 0x44, 0x14, 0x27, 0xc4,
0x38, 0x9b, 0xc4, 0x54, 0xf0, 0x20, 0x89, 0xd7, 0xf0, 0x8b, 0x87,
0x30, 0x05, 0xe4, 0xc2, 0x8d, 0x43, 0x24, 0x68, 0x99, 0x78, 0x71,
0xc0, 0xbf, 0x28, 0x6f, 0xd3, 0x86, 0x1e, 0x21, 0xe9, 0x6a,
},
};
static bool get_cert_extensions(DER_ITEM *tbs_cert, DER_ITEM *extensions) {
// Find the certificate extensions in the tbsCertificate.
DER_ITEM cert_item = {0};
while (der_read_item(&tbs_cert->buf, &cert_item)) {
if (cert_item.id == DER_X509_EXTENSIONS) {
// Open the extensions sequence.
return der_read_item(&cert_item.buf, extensions) &&
extensions->id == DER_SEQUENCE;
}
}
return false;
}
static bool get_extension_value(const uint8_t *extension_oid,
size_t extension_oid_size, DER_ITEM *extensions,
DER_ITEM *extension_value) {
// Find the extension with the given OID.
DER_ITEM extension = {0};
while (der_read_item(&extensions->buf, &extension)) {
DER_ITEM extension_id = {0};
if (der_read_item(&extension.buf, &extension_id) &&
extension_id.buf.size == extension_oid_size &&
memcmp(extension_id.buf.data, extension_oid, extension_oid_size) == 0) {
// Find the extension's extnValue, skipping the optional critical flag.
while (der_read_item(&extension.buf, extension_value)) {
if (extension_value->id == DER_OCTET_STRING) {
return true;
}
}
memzero(extension_value, sizeof(DER_ITEM));
return false;
}
}
return false;
}
static bool get_authority_key_digest(DER_ITEM *tbs_cert,
const uint8_t **authority_key_digest) {
DER_ITEM extensions = {0};
if (!get_cert_extensions(tbs_cert, &extensions)) {
vcp_println("ERROR get_authority_key_digest, extensions not found.");
return false;
}
// Find the authority key identifier extension's extnValue.
DER_ITEM extension_value = {0};
if (!get_extension_value(OID_AUTHORITY_KEY_IDENTIFIER,
sizeof(OID_AUTHORITY_KEY_IDENTIFIER), &extensions,
&extension_value)) {
vcp_println(
"ERROR get_authority_key_digest, authority key identifier extension "
"not found.");
return false;
}
// Open the AuthorityKeyIdentifier sequence.
DER_ITEM auth_key_id = {0};
if (!der_read_item(&extension_value.buf, &auth_key_id) ||
auth_key_id.id != DER_SEQUENCE) {
vcp_println(
"ERROR get_authority_key_digest, failed to open authority key "
"identifier extnValue.");
return false;
}
// Find the keyIdentifier field.
DER_ITEM key_id = {0};
if (!der_read_item(&auth_key_id.buf, &key_id) ||
key_id.id != DER_X509_KEY_IDENTIFIER) {
vcp_println(
"ERROR get_authority_key_digest, failed to find keyIdentifier field.");
return false;
}
// Return the pointer to the keyIdentifier data.
if (buffer_remaining(&key_id.buf) != SHA1_DIGEST_LENGTH ||
!buffer_ptr(&key_id.buf, authority_key_digest)) {
vcp_println(
"ERROR get_authority_key_digest, invalid length of keyIdentifier.");
return false;
}
return true;
}
bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
// Checks the integrity of the device certificate chain to ensure that the
@ -593,6 +669,10 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
return false;
}
// This will be populated with a pointer to the key identifier data of the
// AuthorityKeyIdentifier extension from the last certificate in the chain.
const uint8_t *authority_key_digest = NULL;
BUFFER_READER chain_reader = {0};
buffer_reader_init(&chain_reader, chain, chain_size);
int cert_count = 0;
@ -652,6 +732,13 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
return false;
}
// Get the authority key identifier from the last certificate.
if (buffer_remaining(&chain_reader) == 0 &&
!get_authority_key_digest(&tbs_cert, &authority_key_digest)) {
// Error returned by get_authority_key_digest().
return false;
}
// Prepare the hash of tbsCertificate for the next signature verification.
sha256_Raw(tbs_cert.buf.data, tbs_cert.buf.size, digest);
@ -689,13 +776,18 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
}
}
// Verify that the last certificate in the chain is valid for one of the known
// root public keys.
for (int i = 0; i < sizeof(ROOT_PUBLIC_KEYS) / sizeof(ROOT_PUBLIC_KEYS[0]);
++i) {
if (ecdsa_verify_digest(&nist256p1, ROOT_PUBLIC_KEYS[i], sig, digest) ==
// Verify that the signature of the last certificate in the chain matches its
// own AuthorityKeyIdentifier to verify the integrity of the certificate data.
uint8_t pub_key[65] = {0};
uint8_t pub_key_digest[SHA1_DIGEST_LENGTH] = {0};
for (int recid = 0; recid < 4; ++recid) {
if (ecdsa_recover_pub_from_sig(&nist256p1, pub_key, sig, digest, recid) ==
0) {
return true;
sha1_Raw(pub_key, sizeof(pub_key), pub_key_digest);
if (memcmp(authority_key_digest, pub_key_digest,
sizeof(pub_key_digest)) == 0) {
return true;
}
}
}

@ -33,6 +33,7 @@
#define DER_SEQUENCE 0x30
#define DER_INTEGER 0x02
#define DER_BIT_STRING 0x03
#define DER_OCTET_STRING 0x04
// Struct representing a DER-encoded ASN.1 data value.
typedef struct {

Loading…
Cancel
Save