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