Skip to main content

zlicenser_protocol/tsa/
verify.rs

1//! RFC 3161 timestamp token verification.
2//! Checks message imprint, trust anchor pinning, and signature (RSA/ECDSA, SHA-256/384/512).
3
4#[cfg(feature = "tsa-verify")]
5pub use inner::{verify, verify_with_extra_cert, TsaProvider, VerifiedToken};
6
7#[cfg(feature = "tsa-verify")]
8pub(crate) mod inner {
9    use rasn::prelude::*;
10    use rasn_pkix::Certificate;
11    use sha2::{Digest, Sha256, Sha384, Sha512};
12
13    use crate::error::Error;
14
15    // OID constants
16
17    const OID_SIGNED_DATA: &[u32] = &[1, 2, 840, 113549, 1, 7, 2];
18    const OID_TST_INFO: &[u32] = &[1, 2, 840, 113549, 1, 9, 16, 1, 4];
19    const OID_MESSAGE_DIGEST: &[u32] = &[1, 2, 840, 113549, 1, 9, 4];
20    const OID_SHA256: &[u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 1];
21    const OID_SHA384: &[u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 2];
22    const OID_SHA512: &[u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 3];
23    const OID_RSA_SHA256: &[u32] = &[1, 2, 840, 113549, 1, 1, 11];
24    const OID_RSA_SHA384: &[u32] = &[1, 2, 840, 113549, 1, 1, 12];
25    const OID_RSA_SHA512: &[u32] = &[1, 2, 840, 113549, 1, 1, 13];
26    const OID_ECDSA_SHA256: &[u32] = &[1, 2, 840, 10045, 4, 3, 2];
27    const OID_ECDSA_SHA384: &[u32] = &[1, 2, 840, 10045, 4, 3, 3];
28    const OID_ECDSA_SHA512: &[u32] = &[1, 2, 840, 10045, 4, 3, 4];
29    const OID_SUBJECT_KEY_ID: &[u32] = &[2, 5, 29, 14];
30
31    fn oid(parts: &[u32]) -> ObjectIdentifier {
32        ObjectIdentifier::new(parts.to_vec()).expect("static OID is valid")
33    }
34
35    // SetOf<T> requires T: Eq + Hash; all types below carry those derives.
36
37    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
38    pub struct AlgorithmIdentifier {
39        pub algorithm: ObjectIdentifier,
40        pub parameters: Option<Any>,
41    }
42
43    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
44    pub struct IssuerAndSerialNumber {
45        pub issuer: Any,
46        pub serial_number: Integer,
47    }
48
49    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
50    #[rasn(choice)]
51    pub enum SignerIdentifier {
52        IssuerAndSerialNumber(IssuerAndSerialNumber),
53        #[rasn(tag(0))]
54        SubjectKeyIdentifier(OctetString),
55    }
56
57    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
58    pub struct Attribute {
59        pub attr_type: ObjectIdentifier,
60        pub attr_values: SetOf<Any>,
61    }
62
63    pub type Attributes = SetOf<Attribute>;
64
65    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
66    pub struct SignerInfo {
67        pub version: Integer,
68        pub sid: SignerIdentifier,
69        pub digest_algorithm: AlgorithmIdentifier,
70        #[rasn(tag(0))]
71        pub signed_attrs: Option<Attributes>,
72        pub signature_algorithm: AlgorithmIdentifier,
73        pub signature: OctetString,
74    }
75
76    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
77    pub struct EncapsulatedContentInfo {
78        pub e_content_type: ObjectIdentifier,
79        #[rasn(tag(explicit(0)))]
80        pub e_content: Option<OctetString>,
81    }
82
83    // Treat each element as opaque raw DER so we avoid needing Certificate: Hash.
84    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
85    pub struct CertificateSet(pub SetOf<Any>);
86
87    #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
88    pub struct SignedData {
89        pub version: Integer,
90        pub digest_algorithms: SetOf<AlgorithmIdentifier>,
91        pub encap_content_info: EncapsulatedContentInfo,
92        #[rasn(tag(0))]
93        pub certificates: Option<CertificateSet>,
94        pub signer_infos: SetOf<SignerInfo>,
95    }
96
97    #[derive(AsnType, Clone, Debug, Decode, Encode)]
98    pub struct ContentInfo {
99        pub content_type: ObjectIdentifier,
100        #[rasn(tag(explicit(0)))]
101        pub content: Any,
102    }
103
104    // RFC 3161 §2.4.2, accuracy is an optional sub-sequence
105    #[derive(AsnType, Clone, Debug, Decode, Encode)]
106    pub struct TstAccuracy {
107        pub seconds: Option<Integer>,
108        #[rasn(tag(0))]
109        pub millis: Option<Integer>,
110        #[rasn(tag(1))]
111        pub micros: Option<Integer>,
112    }
113
114    // full TSTInfo with optional tail fields, missing these broke FreeTSA parsing
115    #[derive(AsnType, Clone, Debug, Decode, Encode)]
116    pub struct TstInfo {
117        pub version: Integer,
118        pub policy: ObjectIdentifier,
119        pub message_imprint: MessageImprint,
120        pub serial_number: Integer,
121        pub gen_time: GeneralizedTime,
122        // --- optional fields (RFC 3161 §2.4.2) ---
123        pub accuracy: Option<TstAccuracy>,
124        // absent in DER means false; Some(true) means TSA guarantees ordering
125        pub ordering: Option<bool>,
126        pub nonce: Option<Integer>,
127        // TSA GeneralName, decoded as opaque bytes, we don't need to inspect it
128        #[rasn(tag(explicit(0)))]
129        pub tsa: Option<Any>,
130        #[rasn(tag(1))]
131        pub extensions: Option<Any>,
132    }
133
134    #[derive(AsnType, Clone, Debug, Decode, Encode)]
135    pub struct MessageImprint {
136        pub hash_algorithm: AlgorithmIdentifier,
137        pub hashed_message: OctetString,
138    }
139
140    /// Identifies which trusted provider issued the token.
141    #[derive(Debug, Clone, PartialEq, Eq)]
142    pub enum TsaProvider {
143        FreeTsa,
144        Evidency,
145        Qtsa,
146    }
147
148    #[derive(Debug, Clone)]
149    pub struct VerifiedToken {
150        pub provider: TsaProvider,
151        pub hashed_message: Vec<u8>,
152        pub serial_number: String,
153        pub gen_time_unix: i64,
154    }
155
156    struct TrustAnchor {
157        provider: TsaProvider,
158        chain: &'static [&'static [u8]],
159    }
160
161    fn trust_anchors() -> Vec<TrustAnchor> {
162        vec![
163            TrustAnchor {
164                provider: TsaProvider::FreeTsa,
165                chain: &[
166                    include_bytes!("certs/freetsa_root.der"),
167                    include_bytes!("certs/freetsa_tsa.der"),
168                ],
169            },
170            TrustAnchor {
171                provider: TsaProvider::Evidency,
172                chain: &[
173                    include_bytes!("certs/evidency_prod_root.der"),
174                    include_bytes!("certs/evidency_sandbox_root.der"),
175                ],
176            },
177            TrustAnchor {
178                provider: TsaProvider::Qtsa,
179                chain: &[
180                    include_bytes!("certs/qtsa_root.der"),
181                    include_bytes!("certs/qtsa_intermediate.der"),
182                    include_bytes!("certs/qtsa_tsa_g4.der"),
183                    include_bytes!("certs/sectigo_usertrust_root.der"),
184                ],
185            },
186        ]
187    }
188
189    /// Verifies a DER-encoded RFC 3161 token against the embedded trust anchors.
190    pub fn verify(token_der: &[u8], message: &[u8]) -> crate::Result<VerifiedToken> {
191        verify_inner(token_der, message, &[])
192    }
193
194    /// Like `verify` but also trusts extra_cert_der. Only use in tests/sandboxes.
195    pub fn verify_with_extra_cert(
196        token_der: &[u8],
197        message: &[u8],
198        extra_cert_der: &[u8],
199        extra_provider: TsaProvider,
200    ) -> crate::Result<VerifiedToken> {
201        verify_inner(token_der, message, &[(extra_cert_der, extra_provider)])
202    }
203
204    fn verify_inner(
205        token_der: &[u8],
206        message: &[u8],
207        extra_trusted: &[(&[u8], TsaProvider)],
208    ) -> crate::Result<VerifiedToken> {
209        let ci: ContentInfo = rasn::der::decode(token_der)
210            .map_err(|e| Error::TsaParse(format!("ContentInfo: {e}")))?;
211
212        if ci.content_type != oid(OID_SIGNED_DATA) {
213            return Err(Error::TsaParse("not a SignedData content type".into()));
214        }
215
216        let sd: SignedData = rasn::der::decode(ci.content.as_bytes())
217            .map_err(|e| Error::TsaParse(format!("SignedData: {e}")))?;
218
219        if sd.encap_content_info.e_content_type != oid(OID_TST_INFO) {
220            return Err(Error::TsaParse("encapContentInfo is not TSTInfo".into()));
221        }
222
223        let e_content = sd
224            .encap_content_info
225            .e_content
226            .as_ref()
227            .ok_or_else(|| Error::TsaParse("missing eContent".into()))?;
228        let tst_der: &[u8] = e_content.as_ref();
229
230        let tst: TstInfo =
231            rasn::der::decode(tst_der).map_err(|e| Error::TsaParse(format!("TSTInfo: {e}")))?;
232
233        verify_message_imprint(&tst, message)?;
234
235        let signer_infos = sd.signer_infos.to_vec();
236        let signer = signer_infos
237            .into_iter()
238            .next()
239            .ok_or_else(|| Error::TsaParse("no SignerInfo in SignedData".into()))?;
240
241        let signer_cert_der = find_signer_cert(&sd, signer, extra_trusted)?;
242        let provider = identify_provider(&signer_cert_der, extra_trusted)?;
243
244        verify_signature(signer, &signer_cert_der, tst_der)?;
245
246        Ok(VerifiedToken {
247            provider,
248            hashed_message: tst.message_imprint.hashed_message.to_vec(),
249            serial_number: format!("{}", tst.serial_number),
250            gen_time_unix: tst.gen_time.timestamp(),
251        })
252    }
253
254    fn verify_message_imprint(tst: &TstInfo, message: &[u8]) -> crate::Result<()> {
255        let alg_oid = &tst.message_imprint.hash_algorithm.algorithm;
256        let expected: Vec<u8> = if *alg_oid == oid(OID_SHA256) {
257            Sha256::digest(message).to_vec()
258        } else if *alg_oid == oid(OID_SHA384) {
259            Sha384::digest(message).to_vec()
260        } else if *alg_oid == oid(OID_SHA512) {
261            Sha512::digest(message).to_vec()
262        } else {
263            return Err(Error::TsaVerification(
264                "unsupported hash algorithm in message imprint".into(),
265            ));
266        };
267
268        if tst.message_imprint.hashed_message.as_ref() != expected.as_slice() {
269            return Err(Error::TsaVerification(
270                "message imprint hash mismatch".into(),
271            ));
272        }
273        Ok(())
274    }
275
276    fn find_signer_cert(
277        sd: &SignedData,
278        signer: &SignerInfo,
279        extra_trusted: &[(&[u8], TsaProvider)],
280    ) -> crate::Result<Vec<u8>> {
281        // 1. certs embedded in the token itself
282        if let Some(certs) = &sd.certificates {
283            for cert_any in certs.0.to_vec() {
284                let cert_der = cert_any.as_bytes();
285                if signer_matches_cert(signer, cert_der) {
286                    return Ok(cert_der.to_vec());
287                }
288            }
289        }
290
291        // 2. pinned trust anchor certs (TSAs that omit the cert expect the verifier to have it)
292        for anchor in trust_anchors() {
293            for &cert_der in anchor.chain {
294                if signer_matches_cert(signer, cert_der) {
295                    return Ok(cert_der.to_vec());
296                }
297            }
298        }
299
300        // 3. extra certs from the caller (tests/sandboxes)
301        for (cert_der, _) in extra_trusted {
302            if signer_matches_cert(signer, cert_der) {
303                return Ok(cert_der.to_vec());
304            }
305        }
306
307        Err(Error::TsaParse(
308            "signer certificate not found in token or trust anchors".into(),
309        ))
310    }
311
312    fn signer_matches_cert(signer: &SignerInfo, cert_der: &[u8]) -> bool {
313        let Ok(cert) = rasn::der::decode::<Certificate>(cert_der) else {
314            return false;
315        };
316        match &signer.sid {
317            SignerIdentifier::IssuerAndSerialNumber(isn) => {
318                cert.tbs_certificate.serial_number == isn.serial_number
319            }
320            SignerIdentifier::SubjectKeyIdentifier(skid) => cert
321                .tbs_certificate
322                .extensions
323                .as_ref()
324                .and_then(|exts| exts.iter().find(|e| e.extn_id == oid(OID_SUBJECT_KEY_ID)))
325                .map(|e| e.extn_value.as_ref() == skid.as_ref())
326                .unwrap_or(false),
327        }
328    }
329
330    fn identify_provider(
331        signer_cert_der: &[u8],
332        extra_trusted: &[(&[u8], TsaProvider)],
333    ) -> crate::Result<TsaProvider> {
334        for (cert, provider) in extra_trusted {
335            if *cert == signer_cert_der {
336                return Ok(provider.clone());
337            }
338        }
339        for anchor in trust_anchors() {
340            if anchor.chain.contains(&signer_cert_der) {
341                return Ok(anchor.provider);
342            }
343        }
344        Err(Error::TsaUntrustedProvider)
345    }
346
347    fn verify_signature(
348        signer: &SignerInfo,
349        cert_der: &[u8],
350        e_content_der: &[u8],
351    ) -> crate::Result<()> {
352        let cert: Certificate = rasn::der::decode(cert_der)
353            .map_err(|e| Error::TsaParse(format!("signer cert: {e}")))?;
354
355        let spki_der = rasn::der::encode(&cert.tbs_certificate.subject_public_key_info)
356            .map_err(|e| Error::TsaParse(format!("SPKI encode: {e}")))?;
357
358        let signed_bytes: Vec<u8> = match &signer.signed_attrs {
359            Some(attrs) => {
360                // RFC 5652: signedAttrs must be re-encoded with SET tag for signature input
361                rasn::der::encode(attrs)
362                    .map_err(|e| Error::TsaParse(format!("signedAttrs encode: {e}")))?
363            }
364            None => e_content_der.to_vec(),
365        };
366
367        if let Some(attrs) = &signer.signed_attrs {
368            verify_message_digest_attr(attrs, e_content_der, &signer.digest_algorithm)?;
369        }
370
371        let sig_alg = &signer.signature_algorithm.algorithm;
372        let sig_bytes = signer.signature.as_ref();
373
374        if *sig_alg == oid(OID_RSA_SHA256) {
375            verify_rsa_sha256(sig_bytes, &signed_bytes, &spki_der)
376        } else if *sig_alg == oid(OID_RSA_SHA384) {
377            verify_rsa_sha384(sig_bytes, &signed_bytes, &spki_der)
378        } else if *sig_alg == oid(OID_RSA_SHA512) {
379            verify_rsa_sha512(sig_bytes, &signed_bytes, &spki_der)
380        } else if *sig_alg == oid(OID_ECDSA_SHA256) {
381            verify_ecdsa_p256(sig_bytes, &signed_bytes, &spki_der)
382        } else if *sig_alg == oid(OID_ECDSA_SHA384) {
383            verify_ecdsa_p384(sig_bytes, &signed_bytes, &spki_der)
384        } else if *sig_alg == oid(OID_ECDSA_SHA512) {
385            verify_ecdsa_sha512(sig_bytes, &signed_bytes, &spki_der)
386        } else {
387            Err(Error::TsaVerification(format!(
388                "unsupported signature algorithm: {:?}",
389                sig_alg
390            )))
391        }
392    }
393
394    fn verify_message_digest_attr(
395        attrs: &Attributes,
396        e_content_der: &[u8],
397        digest_alg: &AlgorithmIdentifier,
398    ) -> crate::Result<()> {
399        let md_oid = oid(OID_MESSAGE_DIGEST);
400        let attrs_vec = attrs.to_vec();
401        let Some(md_attr) = attrs_vec.into_iter().find(|a| a.attr_type == md_oid) else {
402            return Err(Error::TsaVerification(
403                "messageDigest attribute missing".into(),
404            ));
405        };
406
407        let vals = md_attr.attr_values.to_vec();
408        let Some(md_any) = vals.into_iter().next() else {
409            return Err(Error::TsaVerification("empty messageDigest value".into()));
410        };
411
412        let expected: OctetString = rasn::der::decode(md_any.as_bytes())
413            .map_err(|e| Error::TsaParse(format!("messageDigest: {e}")))?;
414
415        let actual: Vec<u8> = if digest_alg.algorithm == oid(OID_SHA256) {
416            Sha256::digest(e_content_der).to_vec()
417        } else if digest_alg.algorithm == oid(OID_SHA384) {
418            Sha384::digest(e_content_der).to_vec()
419        } else if digest_alg.algorithm == oid(OID_SHA512) {
420            Sha512::digest(e_content_der).to_vec()
421        } else {
422            return Err(Error::TsaVerification(
423                "unsupported digest algorithm".into(),
424            ));
425        };
426
427        if expected.as_ref() != actual.as_slice() {
428            return Err(Error::TsaVerification("messageDigest mismatch".into()));
429        }
430        Ok(())
431    }
432
433    fn verify_rsa_sha256(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
434        use rsa::{pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, signature::Verifier};
435        let pk = rsa::RsaPublicKey::from_public_key_der(spki_der)
436            .map_err(|e| Error::TsaVerification(format!("RSA key: {e}")))?;
437        let vk: VerifyingKey<Sha256> = VerifyingKey::new(pk);
438        let sig = rsa::pkcs1v15::Signature::try_from(sig)
439            .map_err(|e| Error::TsaVerification(format!("RSA sig: {e}")))?;
440        vk.verify(data, &sig)
441            .map_err(|_| Error::TsaVerification("RSA-SHA256 invalid".into()))
442    }
443
444    fn verify_rsa_sha384(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
445        use rsa::{pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, signature::Verifier};
446        let pk = rsa::RsaPublicKey::from_public_key_der(spki_der)
447            .map_err(|e| Error::TsaVerification(format!("RSA key: {e}")))?;
448        let vk: VerifyingKey<Sha384> = VerifyingKey::new(pk);
449        let sig = rsa::pkcs1v15::Signature::try_from(sig)
450            .map_err(|e| Error::TsaVerification(format!("RSA sig: {e}")))?;
451        vk.verify(data, &sig)
452            .map_err(|_| Error::TsaVerification("RSA-SHA384 invalid".into()))
453    }
454
455    fn verify_rsa_sha512(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
456        use rsa::{pkcs1v15::VerifyingKey, pkcs8::DecodePublicKey, signature::Verifier};
457        let pk = rsa::RsaPublicKey::from_public_key_der(spki_der)
458            .map_err(|e| Error::TsaVerification(format!("RSA key: {e}")))?;
459        let vk: VerifyingKey<Sha512> = VerifyingKey::new(pk);
460        let sig = rsa::pkcs1v15::Signature::try_from(sig)
461            .map_err(|e| Error::TsaVerification(format!("RSA sig: {e}")))?;
462        vk.verify(data, &sig)
463            .map_err(|_| Error::TsaVerification("RSA-SHA512 invalid".into()))
464    }
465
466    fn verify_ecdsa_p256(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
467        use p256::{
468            ecdsa::{signature::Verifier, DerSignature, VerifyingKey},
469            pkcs8::DecodePublicKey,
470        };
471        let vk = VerifyingKey::from_public_key_der(spki_der)
472            .map_err(|e| Error::TsaVerification(format!("P-256 key: {e}")))?;
473        let sig = DerSignature::try_from(sig)
474            .map_err(|e| Error::TsaVerification(format!("P-256 sig: {e}")))?;
475        vk.verify(data, &sig)
476            .map_err(|_| Error::TsaVerification("ECDSA-P256-SHA256 invalid".into()))
477    }
478
479    fn verify_ecdsa_p384(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
480        use p384::ecdsa::{signature::Verifier, DerSignature, VerifyingKey};
481        use p384::pkcs8::DecodePublicKey;
482        let vk = VerifyingKey::from_public_key_der(spki_der)
483            .map_err(|e| Error::TsaVerification(format!("P-384 key: {e}")))?;
484        let sig = DerSignature::try_from(sig)
485            .map_err(|e| Error::TsaVerification(format!("P-384 sig: {e}")))?;
486        vk.verify(data, &sig)
487            .map_err(|_| Error::TsaVerification("ECDSA-P384-SHA384 invalid".into()))
488    }
489
490    // ecdsa-with-SHA512 OID doesn't name the curve, so try P-256 then P-384
491    fn verify_ecdsa_sha512(sig: &[u8], data: &[u8], spki_der: &[u8]) -> crate::Result<()> {
492        let prehash = Sha512::digest(data);
493
494        {
495            // try P-256
496            use p256::pkcs8::DecodePublicKey;
497            if let Ok(vk) = p256::ecdsa::VerifyingKey::from_public_key_der(spki_der) {
498                use p256::ecdsa::signature::hazmat::PrehashVerifier;
499                let raw = p256::ecdsa::Signature::from_der(sig)
500                    .map_err(|e| Error::TsaVerification(format!("sig: {e}")))?;
501                return vk
502                    .verify_prehash(prehash.as_slice(), &raw)
503                    .map_err(|_| Error::TsaVerification("ECDSA-P256-SHA512 invalid".into()));
504            }
505        }
506
507        {
508            // try P-384
509            use p384::pkcs8::DecodePublicKey;
510            if let Ok(vk) = p384::ecdsa::VerifyingKey::from_public_key_der(spki_der) {
511                use p384::ecdsa::signature::hazmat::PrehashVerifier;
512                let raw = p384::ecdsa::Signature::from_der(sig)
513                    .map_err(|e| Error::TsaVerification(format!("sig: {e}")))?;
514                return vk
515                    .verify_prehash(prehash.as_slice(), &raw)
516                    .map_err(|_| Error::TsaVerification("ECDSA-P384-SHA512 invalid".into()));
517            }
518        }
519
520        Err(Error::TsaVerification(
521            "ECDSA-SHA512: unsupported EC curve (expected P-256 or P-384)".into(),
522        ))
523    }
524
525    #[cfg(test)]
526    pub mod mock {
527        //! Minimal RFC 3161 token builder for tests. Uses 512-bit RSA, insecure but fast.
528
529        use rasn::prelude::*;
530        use rsa::{
531            pkcs1v15::SigningKey,
532            pkcs8::EncodePublicKey,
533            rand_core::OsRng,
534            signature::{RandomizedSigner, SignatureEncoding},
535            RsaPrivateKey,
536        };
537        use sha2::{Digest, Sha256};
538
539        use super::*;
540
541        const TEST_RSA_BITS: usize = 512;
542
543        const OID_SHA256_OBJ: &[u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 1];
544        const OID_RSA_SHA256_OBJ: &[u32] = &[1, 2, 840, 113549, 1, 1, 11];
545        const OID_SIGNED_DATA_OBJ: &[u32] = &[1, 2, 840, 113549, 1, 7, 2];
546        const OID_TST_INFO_OBJ: &[u32] = &[1, 2, 840, 113549, 1, 9, 16, 1, 4];
547        const OID_MESSAGE_DIGEST_OBJ: &[u32] = &[1, 2, 840, 113549, 1, 9, 4];
548        const OID_MOCK_POLICY: &[u32] = &[1, 3, 6, 1, 4, 1, 0, 1];
549
550        fn o(parts: &[u32]) -> ObjectIdentifier {
551            ObjectIdentifier::new(parts.to_vec()).unwrap()
552        }
553
554        fn sha256_alg() -> AlgorithmIdentifier {
555            AlgorithmIdentifier {
556                algorithm: o(OID_SHA256_OBJ),
557                parameters: None,
558            }
559        }
560
561        fn rsa_sha256_alg() -> AlgorithmIdentifier {
562            AlgorithmIdentifier {
563                algorithm: o(OID_RSA_SHA256_OBJ),
564                parameters: None,
565            }
566        }
567
568        /// Builds a mock timestamp token. Returns (token_der, spki_der).
569        pub fn build(message: &[u8]) -> (Vec<u8>, Vec<u8>) {
570            let sk = RsaPrivateKey::new(&mut OsRng, TEST_RSA_BITS).expect("RSA key gen");
571            let signing_key: SigningKey<Sha256> = SigningKey::new(sk.clone());
572
573            let spki_der = sk.to_public_key().to_public_key_der().unwrap().to_vec();
574
575            // Build TSTInfo
576            let msg_hash = Sha256::digest(message);
577            let tst = TstInfo {
578                version: Integer::from(1u8),
579                policy: o(OID_MOCK_POLICY),
580                message_imprint: MessageImprint {
581                    hash_algorithm: sha256_alg(),
582                    hashed_message: OctetString::from(msg_hash.to_vec()),
583                },
584                serial_number: Integer::from(42u8),
585                gen_time: chrono::Utc::now().fixed_offset(),
586                accuracy: None,
587                ordering: None,
588                nonce: None,
589                tsa: None,
590                extensions: None,
591            };
592            let tst_der = rasn::der::encode(&tst).unwrap();
593
594            // Build signedAttrs with the messageDigest attribute
595            let tst_digest = Sha256::digest(&tst_der);
596            let digest_attr = build_message_digest_attr(&tst_digest);
597            let mut signed_attrs = Attributes::new();
598            signed_attrs.insert(digest_attr);
599            let signed_attrs_der = rasn::der::encode(&signed_attrs).unwrap();
600
601            // Sign the signedAttrs
602            let sig_bytes: Vec<u8> = signing_key
603                .sign_with_rng(&mut OsRng, &signed_attrs_der)
604                .to_bytes()
605                .to_vec();
606
607            // SignerInfo its serial is irrelevant here, identify_provider matches on raw cert bytes
608            let signer_info = SignerInfo {
609                version: Integer::from(1u8),
610                sid: SignerIdentifier::IssuerAndSerialNumber(IssuerAndSerialNumber {
611                    issuer: Any::new(vec![]),
612                    serial_number: Integer::from(42u8),
613                }),
614                digest_algorithm: sha256_alg(),
615                signed_attrs: Some(signed_attrs),
616                signature_algorithm: rsa_sha256_alg(),
617                signature: OctetString::from(sig_bytes),
618            };
619
620            // bundle SPKI as cert so find_signer_cert matches it via extra_trusted
621            let mut certs_set = SetOf::<Any>::new();
622            certs_set.insert(Any::new(spki_der.clone()));
623
624            let mut digest_algs = SetOf::new();
625            digest_algs.insert(sha256_alg());
626            let mut signer_infos = SetOf::new();
627            signer_infos.insert(signer_info);
628
629            let sd = SignedData {
630                version: Integer::from(3u8),
631                digest_algorithms: digest_algs,
632                encap_content_info: EncapsulatedContentInfo {
633                    e_content_type: o(OID_TST_INFO_OBJ),
634                    e_content: Some(OctetString::from(tst_der)),
635                },
636                certificates: Some(CertificateSet(certs_set)),
637                signer_infos,
638            };
639            let sd_der = rasn::der::encode(&sd).unwrap();
640
641            let ci = ContentInfo {
642                content_type: o(OID_SIGNED_DATA_OBJ),
643                content: Any::new(sd_der),
644            };
645            let token_der = rasn::der::encode(&ci).unwrap();
646
647            (token_der, spki_der)
648        }
649
650        fn build_message_digest_attr(digest: &[u8]) -> Attribute {
651            let val_der = rasn::der::encode(&OctetString::from(digest.to_vec())).unwrap();
652            let mut vals = SetOf::<Any>::new();
653            vals.insert(Any::new(val_der));
654            Attribute {
655                attr_type: o(OID_MESSAGE_DIGEST_OBJ),
656                attr_values: vals,
657            }
658        }
659
660        // signer_matches_cert can't parse raw SPKI as X.509, so tests use verify_with_extra_cert
661        // rather than embedding the cert in SignedData. a proper fix would use a self-signed stub.
662    }
663
664    #[cfg(test)]
665    mod tests {
666        use super::mock;
667        use super::*;
668
669        #[test]
670        fn mock_token_parses_and_imprint_matches() {
671            let message = b"hello from the test suite";
672            let (token_der, _spki_der) = mock::build(message);
673
674            // checks parse + imprint only; full sig+cert chain is behind tsa-live-test
675            let ci: ContentInfo = rasn::der::decode(&token_der).unwrap();
676            assert_eq!(ci.content_type, oid(OID_SIGNED_DATA));
677
678            let sd: SignedData = rasn::der::decode(ci.content.as_bytes()).unwrap();
679            let tst_der: &[u8] = sd.encap_content_info.e_content.as_ref().unwrap().as_ref();
680            let tst: TstInfo = rasn::der::decode(tst_der).unwrap();
681
682            let expected: Vec<u8> = sha2::Sha256::digest(message).to_vec();
683            assert_eq!(
684                tst.message_imprint.hashed_message.as_ref(),
685                expected.as_slice()
686            );
687        }
688
689        #[test]
690        fn wrong_message_imprint_detected() {
691            let message = b"correct message";
692            let (token_der, _spki_der) = mock::build(message);
693
694            // Parse manually to check the imprint fails for a different message.
695            let ci: ContentInfo = rasn::der::decode(&token_der).unwrap();
696            let sd: SignedData = rasn::der::decode(ci.content.as_bytes()).unwrap();
697            let tst_der: &[u8] = sd.encap_content_info.e_content.as_ref().unwrap().as_ref();
698            let tst: TstInfo = rasn::der::decode(tst_der).unwrap();
699
700            let wrong_hash: Vec<u8> = sha2::Sha256::digest(b"wrong message").to_vec();
701            assert_ne!(
702                tst.message_imprint.hashed_message.as_ref(),
703                wrong_hash.as_slice()
704            );
705        }
706    }
707}