1use const_oid::ObjectIdentifier;
7use der::{
8 asn1::{BitString, GeneralizedTime, Int, OctetString, Uint},
9 Decode, Encode, Sequence,
10};
11use rand::Rng;
12use sigstore_types::HashAlgorithm;
13use x509_cert::{ext::pkix::name::GeneralName, ext::Extensions};
14
15pub const OID_SHA256: ObjectIdentifier = const_oid::db::rfc5912::ID_SHA_256;
17
18pub const OID_SHA384: ObjectIdentifier = const_oid::db::rfc5912::ID_SHA_384;
20
21pub const OID_SHA512: ObjectIdentifier = const_oid::db::rfc5912::ID_SHA_512;
23
24pub const OID_TST_INFO: ObjectIdentifier =
26 ObjectIdentifier::new_unwrap("1.2.840.113549.1.9.16.1.4");
27
28pub fn generate_nonce() -> Int {
34 let nonce: u64 = rand::rng().random();
35 let uint = Uint::new(&nonce.to_be_bytes()).expect("valid uint from random u64");
36 Int::from(uint)
37}
38
39#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
41pub struct AlgorithmIdentifier {
42 pub algorithm: ObjectIdentifier,
44 #[asn1(optional = "true")]
46 pub parameters: Option<der::Any>,
47}
48
49impl AlgorithmIdentifier {
50 pub fn sha256() -> Self {
52 Self {
53 algorithm: OID_SHA256,
54 parameters: None,
55 }
56 }
57
58 pub fn sha384() -> Self {
60 Self {
61 algorithm: OID_SHA384,
62 parameters: None,
63 }
64 }
65
66 pub fn sha512() -> Self {
68 Self {
69 algorithm: OID_SHA512,
70 parameters: None,
71 }
72 }
73
74 pub fn to_hash_algorithm(&self) -> Option<HashAlgorithm> {
76 match self.algorithm {
77 OID_SHA256 => Some(HashAlgorithm::Sha2256),
78 OID_SHA384 => Some(HashAlgorithm::Sha2384),
79 OID_SHA512 => Some(HashAlgorithm::Sha2512),
80 _ => None,
81 }
82 }
83}
84
85impl From<HashAlgorithm> for AlgorithmIdentifier {
86 fn from(algo: HashAlgorithm) -> Self {
87 match algo {
88 HashAlgorithm::Sha2256 => Self::sha256(),
89 HashAlgorithm::Sha2384 => Self::sha384(),
90 HashAlgorithm::Sha2512 => Self::sha512(),
91 }
92 }
93}
94
95#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
102pub struct Asn1MessageImprint {
103 pub hash_algorithm: AlgorithmIdentifier,
105 pub hashed_message: OctetString,
107}
108
109impl Asn1MessageImprint {
110 pub fn new(algorithm: AlgorithmIdentifier, digest: Vec<u8>) -> Self {
112 Self {
113 hash_algorithm: algorithm,
114 hashed_message: OctetString::new(digest).expect("valid octet string"),
115 }
116 }
117}
118
119#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
122pub struct TimeStampReq {
123 pub version: u8,
125 pub message_imprint: Asn1MessageImprint,
127 #[asn1(optional = "true")]
129 pub req_policy: Option<ObjectIdentifier>,
130 #[asn1(optional = "true")]
132 pub nonce: Option<Int>,
133 #[asn1(default = "default_false")]
135 pub cert_req: bool,
136 }
138
139fn default_false() -> bool {
140 false
141}
142
143impl TimeStampReq {
144 pub fn new(message_imprint: Asn1MessageImprint) -> Self {
146 Self {
147 version: 1,
148 message_imprint,
149 req_policy: None,
150 nonce: Some(generate_nonce()),
151 cert_req: true,
152 }
153 }
154
155 pub fn new_without_nonce(message_imprint: Asn1MessageImprint) -> Self {
157 Self {
158 version: 1,
159 message_imprint,
160 req_policy: None,
161 nonce: None,
162 cert_req: true,
163 }
164 }
165
166 pub fn with_nonce(mut self, nonce: u64) -> Self {
168 let uint = Uint::new(&nonce.to_be_bytes()).expect("valid unsigned integer");
169 self.nonce = Some(Int::from(uint));
170 self
171 }
172
173 pub fn with_cert_req(mut self, cert_req: bool) -> Self {
175 self.cert_req = cert_req;
176 self
177 }
178
179 pub fn to_der(&self) -> Result<Vec<u8>, der::Error> {
181 Encode::to_der(self)
182 }
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
188#[repr(u8)]
189pub enum PkiStatus {
190 Granted = 0,
192 GrantedWithMods = 1,
194 Rejection = 2,
196 Waiting = 3,
198 RevocationWarning = 4,
200 RevocationNotification = 5,
202}
203
204impl TryFrom<u8> for PkiStatus {
205 type Error = ();
206
207 fn try_from(value: u8) -> Result<Self, Self::Error> {
208 match value {
209 0 => Ok(PkiStatus::Granted),
210 1 => Ok(PkiStatus::GrantedWithMods),
211 2 => Ok(PkiStatus::Rejection),
212 3 => Ok(PkiStatus::Waiting),
213 4 => Ok(PkiStatus::RevocationWarning),
214 5 => Ok(PkiStatus::RevocationNotification),
215 _ => Err(()),
216 }
217 }
218}
219
220#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
223pub struct PkiStatusInfo {
224 pub status: u8,
226 #[asn1(optional = "true")]
228 pub status_string: Option<Vec<String>>,
229 #[asn1(optional = "true")]
231 pub fail_info: Option<BitString>,
232}
233
234impl PkiStatusInfo {
235 pub fn is_success(&self) -> bool {
237 self.status == PkiStatus::Granted as u8 || self.status == PkiStatus::GrantedWithMods as u8
238 }
239
240 pub fn status_enum(&self) -> Option<PkiStatus> {
242 PkiStatus::try_from(self.status).ok()
243 }
244}
245
246#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
249pub struct Accuracy {
250 #[asn1(optional = "true")]
252 pub seconds: Option<u64>,
253 #[asn1(context_specific = "0", optional = "true")]
255 pub millis: Option<u16>,
256 #[asn1(context_specific = "1", optional = "true")]
258 pub micros: Option<u16>,
259}
260
261#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
264pub struct TstInfo {
265 pub version: u8,
267 pub policy: ObjectIdentifier,
269 pub message_imprint: Asn1MessageImprint,
271 pub serial_number: Int,
273 pub gen_time: GeneralizedTime,
275 #[asn1(optional = "true")]
277 pub accuracy: Option<Accuracy>,
278 #[asn1(default = "default_false")]
280 pub ordering: bool,
281 #[asn1(optional = "true")]
283 pub nonce: Option<Int>,
284 #[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
286 pub tsa: Option<GeneralName>,
287 #[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
289 pub extensions: Option<Extensions>,
290}
291
292impl TstInfo {
293 pub fn from_der_bytes(bytes: &[u8]) -> Result<Self, der::Error> {
295 Self::from_der(bytes)
296 }
297}
298
299#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
302pub struct TimeStampResp {
303 pub status: PkiStatusInfo,
305 #[asn1(optional = "true")]
307 pub time_stamp_token: Option<der::Any>,
308}
309
310impl TimeStampResp {
311 pub fn from_der_bytes(bytes: &[u8]) -> Result<Self, der::Error> {
313 Self::from_der(bytes)
314 }
315
316 pub fn is_success(&self) -> bool {
318 self.status.is_success() && self.time_stamp_token.is_some()
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn test_message_imprint_encode() {
328 let digest = vec![0u8; 32]; let imprint = Asn1MessageImprint::new(AlgorithmIdentifier::sha256(), digest);
330 let der = Encode::to_der(&imprint).unwrap();
331 assert!(!der.is_empty());
332 }
333
334 #[test]
335 fn test_timestamp_req_encode() {
336 let digest = vec![0u8; 32];
337 let imprint = Asn1MessageImprint::new(AlgorithmIdentifier::sha256(), digest);
338 let req = TimeStampReq::new(imprint);
339 let der = req.to_der().unwrap();
340 assert!(!der.is_empty());
341 }
342
343 #[test]
344 fn test_timestamp_req_has_nonce() {
345 let digest = vec![0u8; 32];
346 let imprint = Asn1MessageImprint::new(AlgorithmIdentifier::sha256(), digest);
347 let req = TimeStampReq::new(imprint);
348
349 assert!(
351 req.nonce.is_some(),
352 "Nonce should be automatically generated"
353 );
354 }
355
356 #[test]
357 fn test_generate_nonce_roundtrips_as_canonical_der() {
358 for _ in 0..1000 {
364 let nonce = generate_nonce();
365 let encoded = Encode::to_der(&nonce).expect("DER encoding must succeed");
366 let decoded = Int::from_der(&encoded);
367 assert!(
368 decoded.is_ok(),
369 "nonce failed canonical DER round-trip: {:02x?} → {:?}",
370 encoded,
371 decoded.err()
372 );
373 }
374 }
375
376 #[test]
377 fn test_uint_produces_canonical_der_for_problematic_patterns() {
378 let cases: &[&[u8]] = &[
383 &[0x00, 0x35], &[0x00, 0xFF], &[0x00, 0x00, 0x42], &[0x00, 0x00, 0x00, 0x01], ];
388
389 for input in cases {
390 let uint = Uint::new(input).unwrap();
391 let int = Int::from(uint);
392 let encoded = Encode::to_der(&int).unwrap();
393 let decoded = Int::from_der(&encoded);
394 assert!(
395 decoded.is_ok(),
396 "input {:02x?} produced non-canonical DER: {:02x?} → {:?}",
397 input,
398 encoded,
399 decoded.err()
400 );
401 }
402 }
403
404 #[test]
405 fn test_pki_status() {
406 assert!(PkiStatus::try_from(0).is_ok());
407 assert!(PkiStatus::try_from(5).is_ok());
408 assert!(PkiStatus::try_from(6).is_err());
409 }
410}