#![forbid(unsafe_code)]
#![allow(clippy::unwrap_used)]
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use matter_cert::{MatterTime, PublicKey};
use matter_commissioning::{
issue_noc, verify_csr_response, FabricRecord, NocError, SystemNocRng, VerifiedCsr,
};
use matter_crypto::{RingSigner, Signer};
use serde::Deserialize;
#[derive(Deserialize)]
struct Fixture {
#[allow(dead_code)]
kind: String,
nocsr_elements_b64: String,
attestation_signature_b64: String,
expected_csr_nonce_hex: String,
attestation_challenge_hex: String,
dac_public_key_b64: String,
}
fn load(name: &str) -> Fixture {
let mut p: PathBuf = env!("CARGO_MANIFEST_DIR").into();
p.push("..");
p.push("..");
p.push("test-vectors");
p.push("commissioning");
p.push("noc");
p.push("negative");
p.push(format!("{name}.json"));
let bytes = fs::read(&p).unwrap_or_else(|e| panic!("missing fixture {}: {e}", p.display()));
serde_json::from_slice(&bytes).unwrap()
}
fn hex_to_array<const N: usize>(s: &str) -> [u8; N] {
let bytes = hex::decode(s).unwrap();
let mut arr = [0u8; N];
arr.copy_from_slice(&bytes);
arr
}
fn run(name: &str) -> Result<VerifiedCsr, NocError> {
let f = load(name);
let elements = B64.decode(&f.nocsr_elements_b64).unwrap();
let sig_bytes = B64.decode(&f.attestation_signature_b64).unwrap();
let mut sig = [0u8; 64];
sig.copy_from_slice(&sig_bytes);
let nonce = hex_to_array::<32>(&f.expected_csr_nonce_hex);
let challenge = hex_to_array::<16>(&f.attestation_challenge_hex);
let dac_pub = B64.decode(&f.dac_public_key_b64).unwrap();
verify_csr_response(&elements, &sig, &nonce, &challenge, &dac_pub)
}
#[test]
fn bad_csr_self_sig_rejects() {
let err = run("bad-csr-self-sig").unwrap_err();
assert!(matches!(err, NocError::BadCsrSelfSignature), "got: {err:?}");
}
#[test]
fn wrong_nonce_echo_rejects() {
let err = run("wrong-nonce-echo").unwrap_err();
assert!(matches!(err, NocError::NonceMismatch), "got: {err:?}");
}
#[test]
fn bad_att_sig_rejects() {
let err = run("bad-att-sig").unwrap_err();
assert!(
matches!(err, NocError::BadCsrAttestationSignature),
"got: {err:?}"
);
}
#[test]
fn non_p256_csr_key_rejects() {
let err = run("non-p256-csr-key").unwrap_err();
assert!(
matches!(err, NocError::CsrParse(_) | NocError::InvalidCsrPublicKey),
"got: {err:?}"
);
}
#[test]
fn malformed_nocsr_tlv_rejects() {
let err = run("malformed-nocsr-tlv").unwrap_err();
assert!(matches!(err, NocError::NocsrParse(_)), "got: {err:?}");
}
#[test]
fn malformed_pkcs10_rejects() {
let err = run("malformed-pkcs10").unwrap_err();
assert!(matches!(err, NocError::CsrParse(_)), "got: {err:?}");
}
#[test]
fn wrong_challenge_rejects() {
let err = run("wrong-challenge").unwrap_err();
assert!(
matches!(err, NocError::BadCsrAttestationSignature),
"got: {err:?}"
);
}
#[test]
fn oversized_cat_value_path() {
let (root_signer, _) = RingSigner::generate().unwrap();
let root_signer: Arc<dyn Signer> = Arc::new(root_signer);
let fabric = FabricRecord::new_root_only(
1,
root_signer,
MatterTime::from_unix_secs(1_700_000_000),
MatterTime::NO_EXPIRY,
7,
&SystemNocRng,
)
.unwrap();
let (device_signer, _) = RingSigner::generate().unwrap();
let verified = VerifiedCsr {
public_key: PublicKey::from_slice(device_signer.public_key().as_bytes()).unwrap(),
};
issue_noc(
&fabric,
&verified,
0xABCD,
&[0x1234_5678],
(
MatterTime::from_unix_secs(1_700_000_000),
MatterTime::NO_EXPIRY,
),
&SystemNocRng,
)
.unwrap();
}