cryptographic_message_syntax/asn1/
rfc3161.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! ASN.1 types defined by RFC 3161.
6
7use {
8    crate::asn1::{rfc4210::PkiFreeText, rfc5652::ContentInfo},
9    bcder::{
10        decode::{Constructed, DecodeError, Primitive, Source},
11        encode::{self, PrimitiveContent, Values},
12        ConstOid, Integer, OctetString, Oid, Tag,
13    },
14    x509_certificate::{
15        asn1time::GeneralizedTime,
16        rfc3280::GeneralName,
17        rfc5280::{AlgorithmIdentifier, Extensions},
18    },
19};
20
21/// Content-Type for Time-Stamp Token Info.
22///
23/// 1.2.840.113549.1.9.16.1.4
24pub const OID_CONTENT_TYPE_TST_INFO: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 1, 4]);
25
26/// id-aa-timeStampToken
27///
28/// 1.2.840.113549.1.9.16.2.14
29pub const OID_TIME_STAMP_TOKEN: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 2, 14]);
30
31/// A time-stamp request.
32///
33/// ```ASN.1
34/// TimeStampReq ::= SEQUENCE  {
35///    version                  INTEGER  { v1(1) },
36///    messageImprint           MessageImprint,
37///      --a hash algorithm OID and the hash value of the data to be
38///      --time-stamped
39///    reqPolicy                TSAPolicyId                OPTIONAL,
40///    nonce                    INTEGER                    OPTIONAL,
41///    certReq                  BOOLEAN                    DEFAULT FALSE,
42///    extensions               [0] IMPLICIT Extensions    OPTIONAL  }
43/// ```
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct TimeStampReq {
46    pub version: Integer,
47    pub message_imprint: MessageImprint,
48    pub req_policy: Option<TsaPolicyId>,
49    pub nonce: Option<Integer>,
50    pub cert_req: Option<bool>,
51    pub extensions: Option<Extensions>,
52}
53
54impl TimeStampReq {
55    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
56        cons.take_sequence(|cons| {
57            let version = Integer::take_from(cons)?;
58            let message_imprint = MessageImprint::take_from(cons)?;
59            let req_policy = TsaPolicyId::take_opt_from(cons)?;
60            let nonce =
61                cons.take_opt_primitive_if(Tag::INTEGER, |prim| Integer::from_primitive(prim))?;
62            let cert_req = cons.take_opt_bool()?;
63            let extensions =
64                cons.take_opt_constructed_if(Tag::CTX_0, |cons| Extensions::take_from(cons))?;
65
66            Ok(Self {
67                version,
68                message_imprint,
69                req_policy,
70                nonce,
71                cert_req,
72                extensions,
73            })
74        })
75    }
76
77    pub fn encode_ref(&self) -> impl Values + '_ {
78        encode::sequence((
79            (&self.version).encode(),
80            self.message_imprint.encode_ref(),
81            self.req_policy
82                .as_ref()
83                .map(|req_policy| req_policy.encode_ref()),
84            self.nonce.as_ref().map(|nonce| nonce.encode()),
85            self.cert_req.as_ref().map(|cert_req| cert_req.encode_ref()),
86            self.extensions
87                .as_ref()
88                .map(|extensions| extensions.encode_ref_as(Tag::CTX_0)),
89        ))
90    }
91}
92
93/// Message imprint.
94///
95/// ```ASN.1
96/// MessageImprint ::= SEQUENCE  {
97///      hashAlgorithm                AlgorithmIdentifier,
98///      hashedMessage                OCTET STRING  }
99/// ```
100#[derive(Clone, Debug, Eq, PartialEq)]
101pub struct MessageImprint {
102    pub hash_algorithm: AlgorithmIdentifier,
103    pub hashed_message: OctetString,
104}
105
106impl MessageImprint {
107    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
108        cons.take_sequence(|cons| {
109            let hash_algorithm = AlgorithmIdentifier::take_from(cons)?;
110            let hashed_message = OctetString::take_from(cons)?;
111
112            Ok(Self {
113                hash_algorithm,
114                hashed_message,
115            })
116        })
117    }
118
119    pub fn encode_ref(&self) -> impl Values + '_ {
120        encode::sequence((&self.hash_algorithm, self.hashed_message.encode_ref()))
121    }
122}
123
124pub type TsaPolicyId = Oid;
125
126/// Time stamp response.
127///
128/// ```ASN.1
129/// TimeStampResp ::= SEQUENCE  {
130///      status                  PKIStatusInfo,
131///      timeStampToken          TimeStampToken     OPTIONAL  }
132/// ```
133#[derive(Clone, Debug, Eq, PartialEq)]
134pub struct TimeStampResp {
135    pub status: PkiStatusInfo,
136    pub time_stamp_token: Option<TimeStampToken>,
137}
138
139impl TimeStampResp {
140    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
141        cons.take_sequence(|cons| {
142            let status = PkiStatusInfo::take_from(cons)?;
143            let time_stamp_token = TimeStampToken::take_opt_from(cons)?;
144
145            Ok(Self {
146                status,
147                time_stamp_token,
148            })
149        })
150    }
151
152    pub fn encode_ref(&self) -> impl Values + '_ {
153        encode::sequence((
154            self.status.encode_ref(),
155            if let Some(time_stamp_token) = &self.time_stamp_token {
156                Some(time_stamp_token)
157            } else {
158                None
159            },
160        ))
161    }
162}
163
164/// PKI status info
165///
166/// ```ASN.1
167/// PKIStatusInfo ::= SEQUENCE {
168///     status        PKIStatus,
169///     statusString  PKIFreeText     OPTIONAL,
170///     failInfo      PKIFailureInfo  OPTIONAL  }
171/// ```
172#[derive(Clone, Debug, Eq, PartialEq)]
173pub struct PkiStatusInfo {
174    pub status: PkiStatus,
175    pub status_string: Option<PkiFreeText>,
176    pub fail_info: Option<PkiFailureInfo>,
177}
178
179impl PkiStatusInfo {
180    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
181        cons.take_sequence(|cons| {
182            let status = PkiStatus::take_from(cons)?;
183            let status_string = PkiFreeText::take_opt_from(cons)?;
184            let fail_info = PkiFailureInfo::take_opt_from(cons)?;
185
186            Ok(Self {
187                status,
188                status_string,
189                fail_info,
190            })
191        })
192    }
193
194    pub fn encode_ref(&self) -> impl Values + '_ {
195        encode::sequence((
196            self.status.encode(),
197            self.status_string
198                .as_ref()
199                .map(|status_string| status_string.encode_ref()),
200            self.fail_info.as_ref().map(|fail_info| fail_info.encode()),
201        ))
202    }
203}
204
205/// PKI status.
206///
207/// ```ASN.1
208/// PKIStatus ::= INTEGER {
209///     granted                (0),
210///     -- when the PKIStatus contains the value zero a TimeStampToken, as
211///        requested, is present.
212///     grantedWithMods        (1),
213///      -- when the PKIStatus contains the value one a TimeStampToken,
214///        with modifications, is present.
215///     rejection              (2),
216///     waiting                (3),
217///     revocationWarning      (4),
218///      -- this message contains a warning that a revocation is
219///      -- imminent
220///     revocationNotification (5)
221///      -- notification that a revocation has occurred   }
222///
223///     -- When the TimeStampToken is not present
224///     -- failInfo indicates the reason why the
225///     -- time-stamp request was rejected and
226///     -- may be one of the following values.
227/// ```
228#[derive(Clone, Copy, Debug, Eq, PartialEq)]
229pub enum PkiStatus {
230    Granted = 0,
231    GrantedWithMods = 1,
232    Rejection = 2,
233    Waiting = 3,
234    RevocationWarning = 4,
235    RevocationNotification = 5,
236}
237
238impl PkiStatus {
239    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
240        match cons.take_primitive_if(Tag::INTEGER, Integer::i8_from_primitive)? {
241            0 => Ok(Self::Granted),
242            1 => Ok(Self::GrantedWithMods),
243            2 => Ok(Self::Rejection),
244            3 => Ok(Self::Waiting),
245            4 => Ok(Self::RevocationWarning),
246            5 => Ok(Self::RevocationNotification),
247            _ => Err(cons.content_err("unknown PKIStatus value")),
248        }
249    }
250
251    pub fn encode(self) -> impl Values {
252        u8::from(self).encode()
253    }
254}
255
256impl From<PkiStatus> for u8 {
257    fn from(v: PkiStatus) -> u8 {
258        match v {
259            PkiStatus::Granted => 0,
260            PkiStatus::GrantedWithMods => 1,
261            PkiStatus::Rejection => 2,
262            PkiStatus::Waiting => 3,
263            PkiStatus::RevocationWarning => 4,
264            PkiStatus::RevocationNotification => 5,
265        }
266    }
267}
268
269/// PKI failure info.
270///
271/// ```ASN.1
272/// PKIFailureInfo ::= BIT STRING {
273///     badAlg               (0),
274///       -- unrecognized or unsupported Algorithm Identifier
275///     badRequest           (2),
276///       -- transaction not permitted or supported
277///     badDataFormat        (5),
278///       -- the data submitted has the wrong format
279///     timeNotAvailable    (14),
280///       -- the TSA's time source is not available
281///     unacceptedPolicy    (15),
282///       -- the requested TSA policy is not supported by the TSA.
283///     unacceptedExtension (16),
284///       -- the requested extension is not supported by the TSA.
285///     addInfoNotAvailable (17)
286///       -- the additional information requested could not be understood
287///       -- or is not available
288///     systemFailure       (25)
289///       -- the request cannot be handled due to system failure  }
290/// ```
291#[derive(Clone, Copy, Debug, Eq, PartialEq)]
292pub enum PkiFailureInfo {
293    BadAlg = 0,
294    BadRequest = 1,
295    BadDataFormat = 5,
296    TimeNotAvailable = 14,
297    UnacceptedPolicy = 15,
298    UnacceptedExtension = 16,
299    AddInfoNotAvailable = 17,
300    SystemFailure = 25,
301}
302
303impl PkiFailureInfo {
304    pub fn take_opt_from<S: Source>(
305        cons: &mut Constructed<S>,
306    ) -> Result<Option<Self>, DecodeError<S::Error>> {
307        cons.take_opt_primitive_if(Tag::INTEGER, Self::from_primitive)
308    }
309
310    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
311        cons.take_primitive_if(Tag::INTEGER, Self::from_primitive)
312    }
313
314    pub fn from_primitive<S: Source>(
315        prim: &mut Primitive<S>,
316    ) -> Result<Self, DecodeError<S::Error>> {
317        match Integer::i8_from_primitive(prim)? {
318            0 => Ok(Self::BadAlg),
319            1 => Ok(Self::BadRequest),
320            5 => Ok(Self::BadDataFormat),
321            14 => Ok(Self::TimeNotAvailable),
322            15 => Ok(Self::UnacceptedPolicy),
323            16 => Ok(Self::UnacceptedExtension),
324            17 => Ok(Self::AddInfoNotAvailable),
325            25 => Ok(Self::SystemFailure),
326            _ => Err(prim.content_err("Unknown PKIFailureInfo value")),
327        }
328    }
329
330    pub fn encode(self) -> impl Values {
331        u8::from(self).encode()
332    }
333}
334
335impl From<PkiFailureInfo> for u8 {
336    fn from(v: PkiFailureInfo) -> u8 {
337        match v {
338            PkiFailureInfo::BadAlg => 0,
339            PkiFailureInfo::BadRequest => 1,
340            PkiFailureInfo::BadDataFormat => 5,
341            PkiFailureInfo::TimeNotAvailable => 14,
342            PkiFailureInfo::UnacceptedPolicy => 15,
343            PkiFailureInfo::UnacceptedExtension => 16,
344            PkiFailureInfo::AddInfoNotAvailable => 17,
345            PkiFailureInfo::SystemFailure => 25,
346        }
347    }
348}
349
350/// Time stamp token.
351///
352/// ```ASN.1
353/// TimeStampToken ::= ContentInfo
354/// ```
355pub type TimeStampToken = ContentInfo;
356
357/// Time stamp token info.
358///
359/// ```ASN.1
360/// TSTInfo ::= SEQUENCE  {
361///     version                      INTEGER  { v1(1) },
362///     policy                       TSAPolicyId,
363///     messageImprint               MessageImprint,
364///       -- MUST have the same value as the similar field in
365///       -- TimeStampReq
366///     serialNumber                 INTEGER,
367///      -- Time-Stamping users MUST be ready to accommodate integers
368///      -- up to 160 bits.
369///     genTime                      GeneralizedTime,
370///     accuracy                     Accuracy                 OPTIONAL,
371///     ordering                     BOOLEAN             DEFAULT FALSE,
372///     nonce                        INTEGER                  OPTIONAL,
373///       -- MUST be present if the similar field was present
374///       -- in TimeStampReq.  In that case it MUST have the same value.
375///     tsa                          [0] GeneralName          OPTIONAL,
376///     extensions                   [1] IMPLICIT Extensions  OPTIONAL   }
377/// ```
378#[derive(Clone, Debug, Eq, PartialEq)]
379pub struct TstInfo {
380    pub version: Integer,
381    pub policy: TsaPolicyId,
382    pub message_imprint: MessageImprint,
383    pub serial_number: Integer,
384    pub gen_time: GeneralizedTime,
385    pub accuracy: Option<Accuracy>,
386    pub ordering: Option<bool>,
387    pub nonce: Option<Integer>,
388    pub tsa: Option<GeneralName>,
389    pub extensions: Option<Extensions>,
390}
391
392impl TstInfo {
393    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
394        cons.take_sequence(|cons| {
395            let version = Integer::take_from(cons)?;
396            let policy = TsaPolicyId::take_from(cons)?;
397            let message_imprint = MessageImprint::take_from(cons)?;
398            let serial_number = Integer::take_from(cons)?;
399            let gen_time = GeneralizedTime::take_from_allow_fractional_z(cons)?;
400            let accuracy = Accuracy::take_opt_from(cons)?;
401            let ordering = cons.take_opt_bool()?;
402            let nonce =
403                cons.take_opt_primitive_if(Tag::INTEGER, |prim| Integer::from_primitive(prim))?;
404            let tsa =
405                cons.take_opt_constructed_if(Tag::CTX_0, |cons| GeneralName::take_from(cons))?;
406            let extensions =
407                cons.take_opt_constructed_if(Tag::CTX_1, |cons| Extensions::take_from(cons))?;
408
409            Ok(Self {
410                version,
411                policy,
412                message_imprint,
413                serial_number,
414                gen_time,
415                accuracy,
416                ordering,
417                nonce,
418                tsa,
419                extensions,
420            })
421        })
422    }
423
424    pub fn encode_ref(&self) -> impl Values + '_ {
425        encode::sequence((
426            (&self.version).encode(),
427            self.policy.encode_ref(),
428            self.message_imprint.encode_ref(),
429            (&self.serial_number).encode(),
430            self.gen_time.encode_ref(),
431            self.accuracy.as_ref().map(|accuracy| accuracy.encode_ref()),
432            self.ordering.as_ref().map(|ordering| ordering.encode_ref()),
433            self.nonce.as_ref().map(|nonce| nonce.encode()),
434            self.tsa
435                .as_ref()
436                .map(|tsa| tsa.encode_ref().explicit(Tag::CTX_0)),
437            self.extensions
438                .as_ref()
439                .map(|extensions| extensions.encode_ref_as(Tag::CTX_1)),
440        ))
441    }
442}
443
444/// Accuracy
445///
446/// ```ASN.1
447/// Accuracy ::= SEQUENCE {
448///                 seconds        INTEGER           OPTIONAL,
449///                 millis     [0] INTEGER  (1..999) OPTIONAL,
450///                 micros     [1] INTEGER  (1..999) OPTIONAL  }
451/// ```
452#[derive(Clone, Debug, Eq, PartialEq)]
453pub struct Accuracy {
454    pub seconds: Option<Integer>,
455    pub millis: Option<Integer>,
456    pub micros: Option<Integer>,
457}
458
459impl Accuracy {
460    pub fn take_opt_from<S: Source>(
461        cons: &mut Constructed<S>,
462    ) -> Result<Option<Self>, DecodeError<S::Error>> {
463        cons.take_opt_sequence(|cons| Self::from_sequence(cons))
464    }
465
466    pub fn from_sequence<S: Source>(
467        cons: &mut Constructed<S>,
468    ) -> Result<Self, DecodeError<S::Error>> {
469        let seconds =
470            cons.take_opt_primitive_if(Tag::INTEGER, |prim| Integer::from_primitive(prim))?;
471        let millis = cons.take_opt_constructed_if(Tag::CTX_0, |cons| Integer::take_from(cons))?;
472        let micros = cons.take_opt_constructed_if(Tag::CTX_1, |cons| Integer::take_from(cons))?;
473
474        Ok(Self {
475            seconds,
476            millis,
477            micros,
478        })
479    }
480
481    pub fn encode_ref(&self) -> impl Values + '_ {
482        encode::sequence((
483            self.seconds.as_ref().map(|seconds| seconds.encode()),
484            self.millis.as_ref().map(|millis| millis.encode()),
485            self.micros.as_ref().map(|micros| micros.encode()),
486        ))
487    }
488}