use core::cell::Cell;
use core::cmp::Ordering;
use std::sync::Arc;
use structured_asn1::Parser;
use structured_asn1::digest::{Digest, Resolution};
use structured_asn1::memory::{Memory, MemoryAccount, MemoryError, Work};
use structured_cms::timestamp::{
ImprintAgreement, ImprintOperand, MessageImprint, RequestAgreement, RequestImprintError,
};
use structured_pkix::algorithm::digest::{Algorithm, AsnDigest};
const SHA256: &[u8] = &[0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 1];
const SHA384: &[u8] = &[0x60, 0x86, 0x48, 1, 0x65, 3, 4, 2, 2];
const UNKNOWN: &[u8] = &[42];
#[derive(Default)]
struct Provider {
resolves: Cell<usize>,
compares: Cell<usize>,
}
impl Digest<'_> for Provider {
type Identity = Algorithm;
type State = ();
type Output = [u8; 1];
type Error = MemoryError;
fn compare<W: Work>(
&self,
left: &Algorithm,
right: &Algorithm,
work: &W,
) -> Result<Ordering, MemoryError> {
work.charge_work(1)?;
self.compares.set(self.compares.get() + 1);
Ok(left.cmp(right))
}
fn width(&self, _: &Algorithm) -> Option<usize> {
Some(1)
}
fn start<W: Work>(&self, _: &Algorithm, _: &W) -> Result<Resolution<()>, MemoryError> {
panic!("request comparison must not start hashing")
}
fn update<W: Work>(&self, (): &mut (), _: &[u8], _: &W) -> Result<(), MemoryError> {
panic!("request comparison must not read content")
}
fn finish<W: Work>(&self, (): (), _: &W) -> Result<[u8; 1], MemoryError> {
panic!("request comparison must not finalize hashing")
}
fn matches<W: Work>(
&self,
_: &Algorithm,
_: &[u8; 1],
_: &[u8],
_: &W,
) -> Result<bool, MemoryError> {
panic!("request declarations are not finished digests")
}
}
impl AsnDigest<'_> for Provider {
fn identity(&self, algorithm: Algorithm) -> Option<Algorithm> {
self.resolves.set(self.resolves.get() + 1);
Some(algorithm)
}
}
fn memory() -> Memory {
Memory::new(Arc::new(MemoryAccount::new(65536, 1_000_000)))
}
fn tlv(tag: u8, value: &[u8]) -> Vec<u8> {
let mut bytes = vec![tag, u8::try_from(value.len()).unwrap()];
bytes.extend_from_slice(value);
bytes
}
fn imprint(oid: &[u8], parameter: Option<&[u8]>, hash: &[u8]) -> Vec<u8> {
let mut algorithm = tlv(6, oid);
if let Some(parameter) = parameter {
algorithm.extend_from_slice(parameter);
}
let mut body = tlv(0x30, &algorithm);
body.extend(tlv(4, hash));
tlv(0x30, &body)
}
#[test]
fn full_identity_includes_parameters_and_kind_beyond_equal_hash_width() {
let provider = Provider::default();
let token_parser = Parser::ber(memory());
let requested_parser = Parser::ber(memory());
let token_bytes = imprint(SHA256, None, &[7]);
let mut token_parent = token_parser.read(&token_bytes, 400).unwrap();
let element = token_parent.next_element().unwrap().unwrap();
let token = MessageImprint::read(&token_parent, &element).unwrap();
for (kind, parameter, hash, expected) in [
(SHA256, Some(&[5, 0][..]), 7, RequestAgreement::Matches),
(
SHA256,
Some(&[2, 1, 1][..]),
7,
RequestAgreement::Unusable {
operand: ImprintOperand::Requested,
reason: ImprintAgreement::MalformedAlgorithm,
},
),
(SHA384, None, 7, RequestAgreement::AlgorithmDiffers),
(SHA256, None, 8, RequestAgreement::HashDiffers),
] {
let requested_bytes = imprint(kind, parameter, &[hash]);
let mut asked_parent = requested_parser.read(&requested_bytes, 800).unwrap();
let element = asked_parent.next_element().unwrap().unwrap();
let asked = MessageImprint::read(&asked_parent, &element).unwrap();
assert_eq!(
token
.matches_request(&asked, &provider, &token_parent, &asked_parent)
.unwrap(),
expected
);
}
assert_eq!(provider.compares.get(), 3);
assert_eq!(provider.resolves.get(), 7);
}
#[test]
fn matching_malformed_widths_and_unsupported_operands_never_succeed() {
let provider = Provider::default();
let parser = Parser::ber(memory());
for (token_kind, token_hash, asked_kind, asked_hash, operand, reason) in [
(
SHA256,
&[7, 8][..],
SHA256,
&[7, 8][..],
ImprintOperand::Token,
ImprintAgreement::MalformedHash,
),
(
SHA256,
&[7][..],
SHA256,
&[7, 8][..],
ImprintOperand::Requested,
ImprintAgreement::MalformedHash,
),
(
UNKNOWN,
&[7][..],
SHA256,
&[7][..],
ImprintOperand::Token,
ImprintAgreement::Unsupported,
),
(
SHA256,
&[7][..],
UNKNOWN,
&[7][..],
ImprintOperand::Requested,
ImprintAgreement::Unsupported,
),
] {
let token_bytes = imprint(token_kind, None, token_hash);
let asked_bytes = imprint(asked_kind, None, asked_hash);
let mut token_parent = parser.read(&token_bytes, 400).unwrap();
let mut asked_parent = parser.read(&asked_bytes, 800).unwrap();
let element = token_parent.next_element().unwrap().unwrap();
let token = MessageImprint::read(&token_parent, &element).unwrap();
let element = asked_parent.next_element().unwrap().unwrap();
let asked = MessageImprint::read(&asked_parent, &element).unwrap();
assert_eq!(
token
.matches_request(&asked, &provider, &token_parent, &asked_parent)
.unwrap(),
RequestAgreement::Unusable { operand, reason }
);
}
assert_eq!(provider.compares.get(), 0);
}
#[test]
fn wrong_requested_reader_preflights_before_resolving_either_operand() {
let memory = memory();
let provider = Provider::default();
let token_parser = Parser::ber(memory.clone());
let asked_parser = Parser::ber(memory.clone());
let bytes = imprint(SHA256, None, &[7]);
let mut token_parent = token_parser.read(&bytes, 400).unwrap();
let mut asked_parent = asked_parser.read(&bytes, 800).unwrap();
let element = token_parent.next_element().unwrap().unwrap();
let token = MessageImprint::read(&token_parent, &element).unwrap();
let element = asked_parent.next_element().unwrap().unwrap();
let asked = MessageImprint::read(&asked_parent, &element).unwrap();
let work = memory.work_used();
let error = token
.matches_request(&asked, &provider, &token_parent, &token_parent)
.unwrap_err();
assert!(
matches!(error, RequestImprintError::Processing { operand: ImprintOperand::Requested, source } if source.is_terminal())
);
assert_eq!(provider.resolves.get(), 0);
assert_eq!(memory.work_used(), work);
}
#[test]
fn requested_resource_refusal_keeps_its_source_role() {
let requested_memory = memory();
let provider = Provider::default();
let token_parser = Parser::ber(memory());
let asked_parser = Parser::ber(requested_memory.clone());
let bytes = imprint(SHA256, None, &[7]);
let mut token_parent = token_parser.read(&bytes, 400).unwrap();
let mut asked_parent = asked_parser.read(&bytes, 800).unwrap();
let element = token_parent.next_element().unwrap().unwrap();
let token = MessageImprint::read(&token_parent, &element).unwrap();
let element = asked_parent.next_element().unwrap().unwrap();
let asked = MessageImprint::read(&asked_parent, &element).unwrap();
requested_memory
.charge_work(requested_memory.work_remaining() as u64)
.unwrap();
let error = token
.matches_request(&asked, &provider, &token_parent, &asked_parent)
.unwrap_err();
let RequestImprintError::Processing { operand, source } = error else {
panic!("requested resource cause lost")
};
assert_eq!(operand, ImprintOperand::Requested);
assert!(source.is_resource());
assert!(source.offset() >= 800);
assert_eq!(provider.resolves.get(), 1);
assert_eq!(provider.compares.get(), 0);
}