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