use crate::x509::{Certificate, KeyUsage, X509Time, verify_chain_refs};
fn leaf_is_ca(c: &Certificate) -> bool {
c.basic_constraints().is_some_and(|bc| bc.is_ca)
}
#[must_use]
pub fn verify_pair(
sign_chain: &[Certificate],
enc_chain: &[Certificate],
anchors: &[Certificate],
at_time: Option<X509Time>,
) -> bool {
let sign_chain: alloc::vec::Vec<&Certificate> = sign_chain.iter().collect();
let enc_chain: alloc::vec::Vec<&Certificate> = enc_chain.iter().collect();
let anchors: alloc::vec::Vec<&Certificate> = anchors.iter().collect();
verify_pair_refs(&sign_chain, &enc_chain, &anchors, at_time)
}
#[doc(hidden)]
#[must_use]
pub fn verify_pair_refs(
sign_chain: &[&Certificate],
enc_chain: &[&Certificate],
anchors: &[&Certificate],
at_time: Option<X509Time>,
) -> bool {
let (Some(&s), Some(&e)) = (sign_chain.first(), enc_chain.first()) else {
return false;
};
if !s.key_usage().is_some_and(KeyUsage::digital_signature) {
return false;
}
if !e
.key_usage()
.is_some_and(|k| k.key_encipherment() || k.key_agreement())
{
return false;
}
if leaf_is_ca(s) || leaf_is_ca(e) {
return false;
}
if s.subject_is_empty()
|| s.subject_raw() != e.subject_raw()
|| s.issuer_raw() != e.issuer_raw()
{
return false;
}
if sign_chain.len() != enc_chain.len()
|| sign_chain[1..]
.iter()
.zip(&enc_chain[1..])
.any(|(a, b)| a.tbs_raw() != b.tbs_raw())
{
return false;
}
verify_chain_refs(sign_chain, anchors, at_time)
&& verify_chain_refs(enc_chain, anchors, at_time)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sm2::Sm2PrivateKey;
use crate::x509::test_support::{bc_ext, cert, key, ku_ext, name};
use alloc::vec::Vec;
struct Setup {
ik: Sm2PrivateKey,
cn: Vec<u8>,
sn: Vec<u8>,
root: Certificate,
int_a: Certificate,
int_b: Certificate,
}
fn setup() -> Setup {
let (rk, ik) = (key(1), key(2));
let (rn, cn, sn) = (name(b"root"), name(b"ca"), name(b"server"));
let ca_exts = [ku_ext(&[5], true), bc_ext(true, None, true)].concat();
Setup {
root: cert(&rk, &rn, &rn, &rk.public_key(), &ca_exts),
int_a: cert(&rk, &rn, &cn, &ik.public_key(), &ca_exts),
int_b: cert(&rk, &rn, &cn, &ik.public_key(), &ca_exts),
ik,
cn,
sn,
}
}
fn assert_pair(s: Setup, sign_exts: &[u8], enc_exts: &[u8], expect: bool) {
let sign = cert(&s.ik, &s.cn, &s.sn, &key(3).public_key(), sign_exts);
let enc = cert(&s.ik, &s.cn, &s.sn, &key(4).public_key(), enc_exts);
assert_eq!(
verify_pair(&[sign, s.int_a], &[enc, s.int_b], &[s.root], None),
expect
);
}
#[test]
fn valid_pair_verifies() {
assert_pair(setup(), &ku_ext(&[0], true), &ku_ext(&[2], true), true);
}
#[test]
fn enc_key_agreement_only_accepted() {
assert_pair(setup(), &ku_ext(&[0], true), &ku_ext(&[4], true), true);
}
#[test]
fn sign_leaf_without_digital_signature_rejected() {
assert_pair(setup(), &ku_ext(&[2], true), &ku_ext(&[2], true), false);
}
#[test]
fn enc_leaf_without_enc_bits_rejected() {
assert_pair(setup(), &ku_ext(&[0], true), &ku_ext(&[0], true), false);
}
#[test]
fn leaf_is_ca_rejected() {
let sign_exts = [ku_ext(&[0], true), bc_ext(true, None, true)].concat();
assert_pair(setup(), &sign_exts, &ku_ext(&[2], true), false);
}
fn assert_subject_pair_rejected(s: Setup, sign_subj: &[u8], enc_subj: &[u8]) {
let sign = cert(
&s.ik,
&s.cn,
sign_subj,
&key(3).public_key(),
&ku_ext(&[0], true),
);
let enc = cert(
&s.ik,
&s.cn,
enc_subj,
&key(4).public_key(),
&ku_ext(&[2], true),
);
assert!(!verify_pair(
&[sign, s.int_a],
&[enc, s.int_b],
&[s.root],
None
));
}
#[test]
fn empty_subject_rejected() {
assert_subject_pair_rejected(setup(), &name(b""), &name(b""));
}
#[test]
fn different_subject_rejected() {
assert_subject_pair_rejected(setup(), &name(b"alice"), &name(b"bob"));
}
#[test]
fn s1_cross_ca_same_name_rejected() {
let rk = key(1);
let (ca_a, ca_b) = (key(2), key(8));
let (rn, cn, sn) = (name(b"root"), name(b"ca"), name(b"server"));
let ca_exts = [ku_ext(&[5], true), bc_ext(true, None, true)].concat();
let root = cert(&rk, &rn, &rn, &rk.public_key(), &ca_exts);
let int_a = cert(&rk, &rn, &cn, &ca_a.public_key(), &ca_exts);
let int_b = cert(&rk, &rn, &cn, &ca_b.public_key(), &ca_exts);
let sign_leaf = cert(&ca_a, &cn, &sn, &key(3).public_key(), &ku_ext(&[0], true));
let enc_leaf = cert(&ca_b, &cn, &sn, &key(4).public_key(), &ku_ext(&[2], true));
assert!(!verify_pair(
&[sign_leaf, int_a],
&[enc_leaf, int_b],
&[root],
None
));
}
}