1use cmpv2::status::PkiStatusInfo;
2use cms::{
3 cert::x509::{
4 ext::{pkix::name::GeneralName, Extensions},
5 spki::AlgorithmIdentifier,
6 },
7 content_info::ContentInfo,
8};
9use der::{
10 asn1::{GeneralizedTime, Int, OctetString},
11 oid::ObjectIdentifier,
12 Any, Enumerated, Sequence,
13};
14
15#[derive(Clone, Copy, Debug, Enumerated, Eq, PartialEq, PartialOrd, Ord)]
16#[asn1(type = "INTEGER")]
17#[repr(u8)]
18pub enum TspVersion {
19 V1 = 1,
21}
22
23#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
33pub struct TimeStampReq {
34 pub version: TspVersion,
35 pub message_imprint: MessageImprint,
36 #[asn1(optional = "true")]
37 pub req_policy: Option<TsaPolicyId>,
38 #[asn1(optional = "true")]
39 pub nonce: Option<Int>,
40 #[asn1(default = "Default::default")]
41 pub cert_req: bool,
42 #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")]
43 pub extensions: Option<Extensions>,
44}
45
46pub type TsaPolicyId = ObjectIdentifier;
50
51#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
57pub struct MessageImprint {
58 pub hash_algorithm: AlgorithmIdentifier<Any>,
59 pub hashed_message: OctetString,
60}
61
62#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
68pub struct TimeStampResp<'a> {
69 pub status: PkiStatusInfo<'a>,
70 #[asn1(optional = "true")]
71 pub time_stamp_token: Option<TimeStampToken>,
72}
73
74pub type TimeStampToken = ContentInfo;
78
79#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
99pub struct TstInfo {
100 pub version: TspVersion,
101 pub policy: TsaPolicyId,
102 pub message_imprint: MessageImprint,
103 pub serial_number: Int,
104 pub gen_time: GeneralizedTime,
105 #[asn1(optional = "true")]
106 pub accuracy: Option<Accuracy>,
107 #[asn1(default = "Default::default")]
108 pub ordering: bool,
109 #[asn1(optional = "true")]
110 pub nonce: Option<Int>,
111 #[asn1(context_specific = "0", tag_mode = "EXPLICIT", optional = "true")]
112 pub tsa: Option<GeneralName>,
113 #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
114 pub extensions: Option<Extensions>,
115}
116
117#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
124pub struct Accuracy {
125 #[asn1(optional = "true")]
126 pub seconds: Option<u64>,
127 #[asn1(context_specific = "0", tag_mode = "IMPLICIT", optional = "true")]
128 pub millis: Option<i16>,
129 #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
130 pub micros: Option<i16>,
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use cmpv2::status::*;
137 use cms::signed_data::SignedData;
138 use der::oid::db::rfc5912::ID_SHA_256;
139 use der::{Decode, Encode};
140 use hex_literal::hex;
141
142 #[test]
179 fn request_test() {
180 let enc_req = hex!("30400201013031300D060960864801650304020105000420BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD0208314CFCE4E0651827");
182 let req = TimeStampReq::from_der(&enc_req).unwrap();
183 assert_eq!(req.version, TspVersion::V1);
184 assert_eq!(req.message_imprint.hash_algorithm.oid, ID_SHA_256);
185 assert_eq!(
186 req.message_imprint.hashed_message.as_bytes(),
187 hex!("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
188 );
189 assert_eq!(req.nonce.unwrap().as_bytes(), hex!("314CFCE4E0651827"));
190 }
191 #[test]
192 fn response_test() {
193 let enc_resp = hex!("3082028430030201003082027B06092A864886F70D010702A082026C30820268020103310F300D060960864801650304020105003081C9060B2A864886F70D0109100104A081B90481B63081B302010106042A0304013031300D060960864801650304020105000420BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD020104180F32303233303630373131323632365A300A020101800201F48101640101FF0208314CFCE4E0651827A048A4463044310B30090603550406130255533113301106035504080C0A536F6D652D5374617465310D300B060355040A0C04546573743111300F06035504030C0854657374205453413182018430820180020101305C3044310B30090603550406130255533113301106035504080C0A536F6D652D5374617465310D300B060355040A0C04546573743111300F06035504030C08546573742054534102146A0DCC59137C11D1C2B092042B4BC51C0D634D24300D06096086480165030402010500A08198301A06092A864886F70D010903310D060B2A864886F70D0109100104301C06092A864886F70D010905310F170D3233303630373131323632365A302B060B2A864886F70D010910020C311C301A3018301604142F36B1B52456F5AC3A1CA09794AE3D0D64AD38C2302F06092A864886F70D01090431220420BAF4CCF82E9B5B3956EADCC87346B407684F26D82B68D0E7DE0D31EA79AF648C300A06082A8648CE3D0403020467306502305A6E1C175B20A93FAB25D14CC5F5A2836D726D6D4A964B66FFBFFCE46276A96475F1408728B3385DCA37C2BA46BE17E1023100C46B7F08D03409A8ECCFD7637765412C3C5EC050E0D39CF48F0F5015950342CB18D8434FF331BA4463C086297C37D07B");
195 let resp = TimeStampResp::from_der(&enc_resp).unwrap();
196 let content = resp.time_stamp_token.unwrap().content;
197 let sd = SignedData::from_der(&content.to_der().unwrap()).unwrap();
198 let encap = sd.encap_content_info.econtent.unwrap();
199 let tst = TstInfo::from_der(&encap.value()).unwrap();
200 assert_eq!(resp.status.status, PkiStatus::Accepted);
201 assert_eq!(tst.version, TspVersion::V1);
202 assert_eq!(tst.policy.to_string(), "1.2.3.4.1");
203 assert_eq!(tst.message_imprint.hash_algorithm.oid, ID_SHA_256);
204 assert_eq!(tst.serial_number.as_bytes(), hex!("04"));
205 assert_eq!(tst.gen_time.to_unix_duration().as_secs(), 1686137186);
206 let accuracy = tst.accuracy.unwrap();
207 assert_eq!(accuracy.seconds.unwrap(), 1);
208 assert_eq!(accuracy.millis.unwrap(), 500);
209 assert_eq!(accuracy.micros.unwrap(), 100);
210 assert!(tst.ordering);
211 assert_eq!(tst.nonce.unwrap().as_bytes(), hex!("314CFCE4E0651827"));
212 let gn = tst.tsa.unwrap();
213 let dn = match gn {
214 GeneralName::DirectoryName(n) => n,
215 _ => panic!(),
216 };
217 assert_eq!(
218 dn.to_string(),
219 "C=US,STATEORPROVINCENAME=Some-State,O=Test,CN=Test TSA"
220 );
221 assert_eq!(
222 tst.message_imprint.hashed_message.as_bytes(),
223 hex!("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
224 );
225 assert!(tst.extensions.is_none());
226 }
227}