mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-29 08:40:57 +00:00
feat(core/prodtest): Check device certificate chain integrity using authority key identifier.
[no changelog]
This commit is contained in:
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…
Reference in New Issue
Block a user