Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
Add SPDM extensions OID check
Browse files Browse the repository at this point in the history
Signed-off-by: Ouyang, Hang <[email protected]>
  • Loading branch information
OuyangHang33 committed Dec 11, 2023
1 parent 41fe503 commit a5a1ae7
Showing 1 changed file with 181 additions and 2 deletions.
183 changes: 181 additions & 2 deletions spdmlib/src/crypto/x509v3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::error::{SpdmResult, SPDM_STATUS_VERIF_FAIL};
use crate::protocol::SpdmBaseAsymAlgo;

const LIBSPDM_CRYPTO_X509_KU_DIGITAL_SIGNATURE: u8 = 0x80;
// reference: https://www.itu.int/rec/T-REC-X.690/en
// TAG
const ASN1_TAG_CLASS_UNIVERSAL_MASK: u8 = 0x0;
Expand All @@ -13,11 +14,13 @@ const ASN1_TAG_CLASS_CONTEXT_SPECIFIC_MASK: u8 = 0x80;
const ASN1_FORM_CONSTRUCTED_MASK: u8 = 0x20;

const ASN1_TAG_NUMBER_INTEGER: u8 = 0x2;
const ASN1_TAG_BIT_STRING: u8 = 0x3;
const ASN1_TAG_NUMBER_OBJECT_IDENTIFIER: u8 = 0x6;
const ASN1_TAG_NUMBER_SEQUENCE: u8 = 0x10;

const ASN1_TAG_SEQUENCE: u8 =
ASN1_TAG_CLASS_UNIVERSAL_MASK | ASN1_FORM_CONSTRUCTED_MASK | ASN1_TAG_NUMBER_SEQUENCE;
const ASN1_TAG_EXPLICIT_EXTENSION: u8 = 0xA3;

const ASN1_LENGTH_MULTI_OCTET_MASK: u8 = 0x80;

Expand All @@ -27,6 +30,19 @@ const OID_RSA_SHA384RSA: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0
const OID_RSA_SHA512RSA: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0du8];
const OID_ECDSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02u8];
const OID_ECDSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03u8];
const OID_DMTF_SPDM_DEVICE_INFO: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x01];
const OID_DMTF_SPDM_HARDWARE_IDENTITY: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x02];
const OID_DMTF_SPDM_EKU_RESPONDER_AUTH: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x03];
const OID_DMTF_SPDM_EKU_REQUESTER_AUTH: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x04];
const OID_DMTF_MUTABLE_CERTIFICATE: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x05];
const OID_DMTF_SPDM_EXTENSION: &[u8] =
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1C, 0x82, 0x12, 0x06];
const OID_KEY_USAGE: &[u8] = &[0x06, 0x03, 0x55, 0x1D, 0x0F];

// reference: https://www.rfc-editor.org/rfc/rfc5280.txt
// IN DER encoded certificate chain slice
Expand Down Expand Up @@ -139,13 +155,32 @@ fn check_tbs_certificate(
t_walker += bytes_consumed;

// subjectPublicKeyInfo SubjectPublicKeyInfo,
check_public_key_info(&data[t_walker..])?;
let bytes_consumed = check_public_key_info(&data[t_walker..])?;
t_walker += bytes_consumed;

// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
// extensions [3] EXPLICIT Extensions OPTIONAL

Ok(length_before_tbs + tbs_length)
//key_usage EXTENSIONS,
let (find_key_usage, key_usage_value) = get_key_usage_value(&data[t_walker..])?;
let mut check_extensions_success =
if find_key_usage && (LIBSPDM_CRYPTO_X509_KU_DIGITAL_SIGNATURE & key_usage_value == 0x00) {

Check failure on line 168 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

this if-then-else expression returns a bool literal

Check failure on line 168 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

this if-then-else expression returns a bool literal
false
} else {
true
};

//extensions EXTENSIONS,
let (bytes_consumed, extension_data) = check_and_get_extensions(&data[t_walker..])?;
check_extensions_success &= check_extensions_spdm_oid(&extension_data, is_leaf_cert);

Check failure on line 176 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

this expression creates a reference which is immediately dereferenced by the compiler

Check failure on line 176 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

this expression creates a reference which is immediately dereferenced by the compiler
t_walker += bytes_consumed;

if (t_walker == length_before_tbs + tbs_length) && check_extensions_success {
return Ok(length_before_tbs + tbs_length);

Check failure on line 180 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

unneeded `return` statement

Check failure on line 180 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

unneeded `return` statement
} else {
return Err(SPDM_STATUS_VERIF_FAIL);

Check failure on line 182 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

unneeded `return` statement

Check failure on line 182 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

unneeded `return` statement
}
}

fn check_signature_algorithm(
Expand Down Expand Up @@ -257,6 +292,113 @@ fn check_public_key_info(data: &[u8]) -> SpdmResult<usize> {
check_and_skip_common_sequence(data)
}

fn check_and_get_extensions(data: &[u8]) -> SpdmResult<(usize, &[u8])> {
let len = data.len();
if len < 1 || data[0] != ASN1_TAG_EXPLICIT_EXTENSION {
Ok((len, &data[0..]))
} else {
let (payload_length, bytes_consumed) = check_length(&data[1..])?;
if len < 1 + bytes_consumed + payload_length {
Err(SPDM_STATUS_VERIF_FAIL)
} else {
Ok((
1 + bytes_consumed + payload_length,
&data[1 + bytes_consumed..1 + bytes_consumed + payload_length],
))
}
}
}

fn get_key_usage_value(data: &[u8]) -> SpdmResult<(bool, u8)> {
let len = data.len();
let mut key_usage_value_len = 0;
let mut key_usage_value_index: usize = 0;
if len < 5 {
Ok((false, 0x00))
} else {
let mut find_key_usage: bool = false;
for index in 0..len - 5 {
if object_identifiers_are_same(&data[index..index + 5], OID_KEY_USAGE) {
let (key_usage_bytes_consumed, key_usage) =
check_and_get_common_tag(&data[index + 5..])?;
if key_usage[0] != ASN1_TAG_BIT_STRING {
find_key_usage = false;
} else {
find_key_usage = true;
(key_usage_value_len, _) = check_length(&key_usage[1..])?;
key_usage_value_index =
index + 5 + key_usage_bytes_consumed - key_usage_value_len;
}
break;
} else {
continue;
}
}
if find_key_usage {
Ok((
true,
data[key_usage_value_index + key_usage_value_len - 1] as u8,

Check failure on line 340 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

casting to the same type is unnecessary (`u8` -> `u8`)

Check failure on line 340 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

casting to the same type is unnecessary (`u8` -> `u8`)
))
} else {
Ok((false, 0x00))
}
}
}

fn check_extensions_spdm_oid(data: &[u8], is_leaf_cert: bool) -> bool {
let len = data.len();
let oid_len = 10;
let extensions_end_index_oid = len - oid_len;
let mut responder_auth_oid_find_success: bool = false;
let mut requester_auth_oid_find_success: bool = false;
let mut spdm_extension_oid_find_success: bool = false;
let mut hardware_identity_oid_find_success: bool = false;
for index in 0..extensions_end_index_oid {
if object_identifiers_are_same(&data[index..index + oid_len], OID_DMTF_SPDM_DEVICE_INFO) {
info!("find device info oid\n");
} else if object_identifiers_are_same(
&data[index..index + oid_len],
OID_DMTF_SPDM_EKU_RESPONDER_AUTH,
) {
info!("find responder auth oid\n");
responder_auth_oid_find_success = true;
} else if object_identifiers_are_same(
&data[index..index + oid_len],
OID_DMTF_SPDM_EKU_REQUESTER_AUTH,
) {
info!("find requester auth oid\n");
requester_auth_oid_find_success = true;
} else if object_identifiers_are_same(
&data[index..index + oid_len],
OID_DMTF_SPDM_EXTENSION,
) {
info!("find DMTF SPDM extension oid\n");
spdm_extension_oid_find_success = true;
} else if object_identifiers_are_same(
&data[index..index + oid_len],
OID_DMTF_MUTABLE_CERTIFICATE,
) {
info!("find mutable cert oid\n");
} else if object_identifiers_are_same(
&data[index..index + oid_len],
OID_DMTF_SPDM_HARDWARE_IDENTITY,
) {
info!("find hardware identity oid\n");
hardware_identity_oid_find_success = true;
} else {
continue;
}
}
if !is_leaf_cert && (requester_auth_oid_find_success || responder_auth_oid_find_success) {

Check failure on line 392 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

this `if` has identical blocks

Check failure on line 392 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

this `if` has identical blocks
return false;

Check failure on line 393 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

unneeded `return` statement

Check failure on line 393 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

unneeded `return` statement
} else if is_leaf_cert && spdm_extension_oid_find_success && hardware_identity_oid_find_success

Check failure on line 394 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy

this if-then-else expression returns a bool literal

Check failure on line 394 in spdmlib/src/crypto/x509v3.rs

View workflow job for this annotation

GitHub Actions / Clippy + rustfmt

this if-then-else expression returns a bool literal
{
return false;
} else {
return true;
}
}

fn check_and_skip_common_sequence(data: &[u8]) -> SpdmResult<usize> {
let len = data.len();
if len < 1 || data[0] != ASN1_TAG_SEQUENCE {
Expand Down Expand Up @@ -760,3 +902,40 @@ mod tests {
assert!(is_root_certificate(&ct1_wrong).is_err());
}
}

#[test]
fn test_case0_get_key_usage_value() {
let key_usage1 = &[
0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x05, 0xE0,
];
let key_usage2_wrong = &[
0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x04, 0x03, 0x02, 0x05, 0xE0,
];
let key_usage3_wrong = &[0x30, 0x0B];
let key_usage4_wrong = &[
0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x05,
];
assert_eq!(get_key_usage_value(key_usage1), Ok((true, 0xE0)));
assert_eq!(get_key_usage_value(key_usage2_wrong), Ok((false, 0x00)));
assert_eq!(get_key_usage_value(key_usage3_wrong), Ok((false, 0x00)));
assert_eq!(
get_key_usage_value(key_usage4_wrong),
Err(SPDM_STATUS_VERIF_FAIL)
);
}

#[test]
fn test_case0_check_extensions_spdm_oid() {
let e1 = std::fs::read("../test_key/ecp384/end_responder.cert.der")
.expect("unable to read leaf cert!");
let e2 = std::fs::read("../test_key/rsa2048/end_requester_with_spdm_rsp_eku.cert.der")
.expect("unable to read leaf cert!");
let e3 = std::fs::read("../test_key/rsa2048/end_responder_with_spdm_req_eku.cert.der")
.expect("unable to read leaf cert!");
assert_eq!(check_extensions_spdm_oid(&e1[4..], false), true);
assert_eq!(check_extensions_spdm_oid(&e1[4..], true), false);
assert_eq!(check_extensions_spdm_oid(&e2[4..], true), true);
assert_eq!(check_extensions_spdm_oid(&e2[4..], false), false);
assert_eq!(check_extensions_spdm_oid(&e3[4..], true), true);
assert_eq!(check_extensions_spdm_oid(&e3[4..], false), false);
}

0 comments on commit a5a1ae7

Please sign in to comment.