1 | #![deny (missing_docs)] |
2 | |
3 | //! Defines the format of certificates |
4 | //! |
5 | //! This module is used by [`x509`] and other certificate building functions |
6 | //! to describe time, strings, and objects. |
7 | //! |
8 | //! Abstract Syntax Notation One is an interface description language. |
9 | //! The specification comes from [X.208] by OSI, and rewritten in X.680. |
10 | //! ASN.1 describes properties of an object with a type set. Those types |
11 | //! can be atomic, structured, choice, and other (CHOICE and ANY). These |
12 | //! types are expressed as a number and the assignment operator ::= gives |
13 | //! the type a name. |
14 | //! |
15 | //! The implementation here provides a subset of the ASN.1 types that OpenSSL |
16 | //! uses, especially in the properties of a certificate used in HTTPS. |
17 | //! |
18 | //! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en |
19 | //! [`x509`]: ../x509/struct.X509Builder.html |
20 | //! |
21 | //! ## Examples |
22 | //! |
23 | //! ``` |
24 | //! use openssl::asn1::Asn1Time; |
25 | //! let tomorrow = Asn1Time::days_from_now(1); |
26 | //! ``` |
27 | use cfg_if::cfg_if; |
28 | use foreign_types::{ForeignType, ForeignTypeRef}; |
29 | use libc::{c_char, c_int, c_long, time_t}; |
30 | use std::cmp::Ordering; |
31 | use std::convert::TryInto; |
32 | use std::ffi::CString; |
33 | use std::fmt; |
34 | use std::ptr; |
35 | use std::slice; |
36 | use std::str; |
37 | |
38 | use crate::bio::MemBio; |
39 | use crate::bn::{BigNum, BigNumRef}; |
40 | use crate::error::ErrorStack; |
41 | use crate::nid::Nid; |
42 | use crate::stack::Stackable; |
43 | use crate::string::OpensslString; |
44 | use crate::{cvt, cvt_p}; |
45 | use openssl_macros::corresponds; |
46 | |
47 | foreign_type_and_impl_send_sync! { |
48 | type CType = ffi::ASN1_GENERALIZEDTIME; |
49 | fn drop = ffi::ASN1_GENERALIZEDTIME_free; |
50 | |
51 | /// Non-UTC representation of time |
52 | /// |
53 | /// If a time can be represented by UTCTime, UTCTime is used |
54 | /// otherwise, ASN1_GENERALIZEDTIME is used. This would be, for |
55 | /// example outside the year range of 1950-2049. |
56 | /// |
57 | /// [ASN1_GENERALIZEDTIME_set] documentation from OpenSSL provides |
58 | /// further details of implementation. Note: these docs are from the master |
59 | /// branch as documentation on the 1.1.0 branch did not include this page. |
60 | /// |
61 | /// [ASN1_GENERALIZEDTIME_set]: https://www.openssl.org/docs/manmaster/man3/ASN1_GENERALIZEDTIME_set.html |
62 | pub struct Asn1GeneralizedTime; |
63 | /// Reference to a [`Asn1GeneralizedTime`] |
64 | /// |
65 | /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html |
66 | pub struct Asn1GeneralizedTimeRef; |
67 | } |
68 | |
69 | impl fmt::Display for Asn1GeneralizedTimeRef { |
70 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
71 | unsafe { |
72 | let mem_bio: MemBio = match MemBio::new() { |
73 | Err(_) => return f.write_str(data:"error" ), |
74 | Ok(m: MemBio) => m, |
75 | }; |
76 | let print_result: Result = cvt(ffi::ASN1_GENERALIZEDTIME_print( |
77 | b:mem_bio.as_ptr(), |
78 | self.as_ptr(), |
79 | )); |
80 | match print_result { |
81 | Err(_) => f.write_str(data:"error" ), |
82 | Ok(_) => f.write_str(data:str::from_utf8_unchecked(mem_bio.get_buf())), |
83 | } |
84 | } |
85 | } |
86 | } |
87 | |
88 | /// The type of an ASN.1 value. |
89 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
90 | pub struct Asn1Type(c_int); |
91 | |
92 | #[allow (missing_docs)] // no need to document the constants |
93 | impl Asn1Type { |
94 | pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC); |
95 | |
96 | pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN); |
97 | |
98 | pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER); |
99 | |
100 | pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING); |
101 | |
102 | pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING); |
103 | |
104 | pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL); |
105 | |
106 | pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT); |
107 | |
108 | pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR); |
109 | |
110 | pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL); |
111 | |
112 | pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL); |
113 | |
114 | pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED); |
115 | |
116 | pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING); |
117 | |
118 | pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE); |
119 | |
120 | pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET); |
121 | |
122 | pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING); |
123 | |
124 | pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING); |
125 | |
126 | pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING); |
127 | |
128 | pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING); |
129 | |
130 | pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING); |
131 | |
132 | pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING); |
133 | |
134 | pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME); |
135 | |
136 | pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME); |
137 | |
138 | pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING); |
139 | |
140 | pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING); |
141 | |
142 | pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING); |
143 | |
144 | pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING); |
145 | |
146 | pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING); |
147 | |
148 | pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING); |
149 | |
150 | /// Constructs an `Asn1Type` from a raw OpenSSL value. |
151 | pub fn from_raw(value: c_int) -> Self { |
152 | Asn1Type(value) |
153 | } |
154 | |
155 | /// Returns the raw OpenSSL value represented by this type. |
156 | pub fn as_raw(&self) -> c_int { |
157 | self.0 |
158 | } |
159 | } |
160 | |
161 | /// Difference between two ASN1 times. |
162 | /// |
163 | /// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its |
164 | /// documentation for more. |
165 | /// |
166 | /// [`diff`]: struct.Asn1TimeRef.html#method.diff |
167 | /// [`Asn1TimeRef`]: struct.Asn1TimeRef.html |
168 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
169 | #[cfg (any(ossl102, boringssl))] |
170 | pub struct TimeDiff { |
171 | /// Difference in days |
172 | pub days: c_int, |
173 | /// Difference in seconds. |
174 | /// |
175 | /// This is always less than the number of seconds in a day. |
176 | pub secs: c_int, |
177 | } |
178 | |
179 | foreign_type_and_impl_send_sync! { |
180 | type CType = ffi::ASN1_TIME; |
181 | fn drop = ffi::ASN1_TIME_free; |
182 | /// Time storage and comparison |
183 | /// |
184 | /// Asn1Time should be used to store and share time information |
185 | /// using certificates. If Asn1Time is set using a string, it must |
186 | /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format. |
187 | /// |
188 | /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation |
189 | /// used by OpenSSL. |
190 | /// |
191 | /// [ASN_TIME_set]: https://www.openssl.org/docs/manmaster/crypto/ASN1_TIME_set.html |
192 | pub struct Asn1Time; |
193 | /// Reference to an [`Asn1Time`] |
194 | /// |
195 | /// [`Asn1Time`]: struct.Asn1Time.html |
196 | pub struct Asn1TimeRef; |
197 | } |
198 | |
199 | impl Asn1TimeRef { |
200 | /// Find difference between two times |
201 | #[corresponds (ASN1_TIME_diff)] |
202 | #[cfg (any(ossl102, boringssl))] |
203 | pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> { |
204 | let mut days = 0; |
205 | let mut secs = 0; |
206 | let other = compare.as_ptr(); |
207 | |
208 | let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) }; |
209 | |
210 | match err { |
211 | 0 => Err(ErrorStack::get()), |
212 | _ => Ok(TimeDiff { days, secs }), |
213 | } |
214 | } |
215 | |
216 | /// Compare two times |
217 | #[corresponds (ASN1_TIME_compare)] |
218 | #[cfg (any(ossl102, boringssl))] |
219 | pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> { |
220 | let d = self.diff(other)?; |
221 | if d.days > 0 || d.secs > 0 { |
222 | return Ok(Ordering::Less); |
223 | } |
224 | if d.days < 0 || d.secs < 0 { |
225 | return Ok(Ordering::Greater); |
226 | } |
227 | |
228 | Ok(Ordering::Equal) |
229 | } |
230 | } |
231 | |
232 | #[cfg (any(ossl102, boringssl))] |
233 | impl PartialEq for Asn1TimeRef { |
234 | fn eq(&self, other: &Asn1TimeRef) -> bool { |
235 | self.diff(other) |
236 | .map(|t| t.days == 0 && t.secs == 0) |
237 | .unwrap_or(default:false) |
238 | } |
239 | } |
240 | |
241 | #[cfg (any(ossl102, boringssl))] |
242 | impl PartialEq<Asn1Time> for Asn1TimeRef { |
243 | fn eq(&self, other: &Asn1Time) -> bool { |
244 | self.diff(other) |
245 | .map(|t| t.days == 0 && t.secs == 0) |
246 | .unwrap_or(default:false) |
247 | } |
248 | } |
249 | |
250 | #[cfg (any(ossl102, boringssl))] |
251 | impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef { |
252 | fn eq(&self, other: &Asn1Time) -> bool { |
253 | self.diff(other) |
254 | .map(|t| t.days == 0 && t.secs == 0) |
255 | .unwrap_or(default:false) |
256 | } |
257 | } |
258 | |
259 | #[cfg (any(ossl102, boringssl))] |
260 | impl PartialOrd for Asn1TimeRef { |
261 | fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> { |
262 | self.compare(other).ok() |
263 | } |
264 | } |
265 | |
266 | #[cfg (any(ossl102, boringssl))] |
267 | impl PartialOrd<Asn1Time> for Asn1TimeRef { |
268 | fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { |
269 | self.compare(other).ok() |
270 | } |
271 | } |
272 | |
273 | #[cfg (any(ossl102, boringssl))] |
274 | impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef { |
275 | fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { |
276 | self.compare(other).ok() |
277 | } |
278 | } |
279 | |
280 | impl fmt::Display for Asn1TimeRef { |
281 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
282 | unsafe { |
283 | let mem_bio: MemBio = match MemBio::new() { |
284 | Err(_) => return f.write_str(data:"error" ), |
285 | Ok(m: MemBio) => m, |
286 | }; |
287 | let print_result: Result = cvt(ffi::ASN1_TIME_print(b:mem_bio.as_ptr(), self.as_ptr())); |
288 | match print_result { |
289 | Err(_) => f.write_str(data:"error" ), |
290 | Ok(_) => f.write_str(data:str::from_utf8_unchecked(mem_bio.get_buf())), |
291 | } |
292 | } |
293 | } |
294 | } |
295 | |
296 | impl fmt::Debug for Asn1TimeRef { |
297 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
298 | f.write_str(&self.to_string()) |
299 | } |
300 | } |
301 | |
302 | impl Asn1Time { |
303 | #[corresponds (ASN1_TIME_new)] |
304 | fn new() -> Result<Asn1Time, ErrorStack> { |
305 | ffi::init(); |
306 | |
307 | unsafe { |
308 | let handle = cvt_p(ffi::ASN1_TIME_new())?; |
309 | Ok(Asn1Time::from_ptr(handle)) |
310 | } |
311 | } |
312 | |
313 | #[corresponds (X509_gmtime_adj)] |
314 | fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> { |
315 | ffi::init(); |
316 | |
317 | unsafe { |
318 | let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?; |
319 | Ok(Asn1Time::from_ptr(handle)) |
320 | } |
321 | } |
322 | |
323 | /// Creates a new time on specified interval in days from now |
324 | pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> { |
325 | Asn1Time::from_period(days as c_long * 60 * 60 * 24) |
326 | } |
327 | |
328 | /// Creates a new time from the specified `time_t` value |
329 | #[corresponds (ASN1_TIME_set)] |
330 | pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> { |
331 | ffi::init(); |
332 | |
333 | unsafe { |
334 | let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?; |
335 | Ok(Asn1Time::from_ptr(handle)) |
336 | } |
337 | } |
338 | |
339 | /// Creates a new time corresponding to the specified ASN1 time string. |
340 | #[corresponds (ASN1_TIME_set_string)] |
341 | #[allow (clippy::should_implement_trait)] |
342 | pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> { |
343 | unsafe { |
344 | let s = CString::new(s).unwrap(); |
345 | |
346 | let time = Asn1Time::new()?; |
347 | cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?; |
348 | |
349 | Ok(time) |
350 | } |
351 | } |
352 | |
353 | /// Creates a new time corresponding to the specified X509 time string. |
354 | /// |
355 | /// Requires BoringSSL or OpenSSL 1.1.1 or newer. |
356 | #[corresponds (ASN1_TIME_set_string_X509)] |
357 | #[cfg (any(ossl111, boringssl))] |
358 | pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> { |
359 | unsafe { |
360 | let s = CString::new(s).unwrap(); |
361 | |
362 | let time = Asn1Time::new()?; |
363 | cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?; |
364 | |
365 | Ok(time) |
366 | } |
367 | } |
368 | } |
369 | |
370 | #[cfg (any(ossl102, boringssl))] |
371 | impl PartialEq for Asn1Time { |
372 | fn eq(&self, other: &Asn1Time) -> bool { |
373 | self.diff(other) |
374 | .map(|t| t.days == 0 && t.secs == 0) |
375 | .unwrap_or(default:false) |
376 | } |
377 | } |
378 | |
379 | #[cfg (any(ossl102, boringssl))] |
380 | impl PartialEq<Asn1TimeRef> for Asn1Time { |
381 | fn eq(&self, other: &Asn1TimeRef) -> bool { |
382 | self.diff(other) |
383 | .map(|t| t.days == 0 && t.secs == 0) |
384 | .unwrap_or(default:false) |
385 | } |
386 | } |
387 | |
388 | #[cfg (any(ossl102, boringssl))] |
389 | impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time { |
390 | fn eq(&self, other: &&'a Asn1TimeRef) -> bool { |
391 | self.diff(other) |
392 | .map(|t| t.days == 0 && t.secs == 0) |
393 | .unwrap_or(default:false) |
394 | } |
395 | } |
396 | |
397 | #[cfg (any(ossl102, boringssl))] |
398 | impl PartialOrd for Asn1Time { |
399 | fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { |
400 | self.compare(other).ok() |
401 | } |
402 | } |
403 | |
404 | #[cfg (any(ossl102, boringssl))] |
405 | impl PartialOrd<Asn1TimeRef> for Asn1Time { |
406 | fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> { |
407 | self.compare(other).ok() |
408 | } |
409 | } |
410 | |
411 | #[cfg (any(ossl102, boringssl))] |
412 | impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time { |
413 | fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> { |
414 | self.compare(other).ok() |
415 | } |
416 | } |
417 | |
418 | foreign_type_and_impl_send_sync! { |
419 | type CType = ffi::ASN1_STRING; |
420 | fn drop = ffi::ASN1_STRING_free; |
421 | /// Primary ASN.1 type used by OpenSSL |
422 | /// |
423 | /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING |
424 | /// structures. This implementation uses [ASN1_STRING-to_UTF8] to preserve |
425 | /// compatibility with Rust's String. |
426 | /// |
427 | /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/manmaster/crypto/ASN1_STRING_to_UTF8.html |
428 | pub struct Asn1String; |
429 | /// A reference to an [`Asn1String`]. |
430 | pub struct Asn1StringRef; |
431 | } |
432 | |
433 | impl Asn1StringRef { |
434 | /// Converts the ASN.1 underlying format to UTF8 |
435 | /// |
436 | /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8. This is important to |
437 | /// consume the string in a meaningful way without knowing the underlying |
438 | /// format. |
439 | #[corresponds (ASN1_STRING_to_UTF8)] |
440 | pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> { |
441 | unsafe { |
442 | let mut ptr = ptr::null_mut(); |
443 | let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr()); |
444 | if len < 0 { |
445 | return Err(ErrorStack::get()); |
446 | } |
447 | |
448 | Ok(OpensslString::from_ptr(ptr as *mut c_char)) |
449 | } |
450 | } |
451 | |
452 | /// Return the string as an array of bytes. |
453 | /// |
454 | /// The bytes do not directly correspond to UTF-8 encoding. To interact with |
455 | /// strings in rust, it is preferable to use [`as_utf8`] |
456 | /// |
457 | /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8 |
458 | #[corresponds (ASN1_STRING_get0_data)] |
459 | pub fn as_slice(&self) -> &[u8] { |
460 | unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) } |
461 | } |
462 | |
463 | /// Returns the number of bytes in the string. |
464 | #[corresponds (ASN1_STRING_length)] |
465 | pub fn len(&self) -> usize { |
466 | unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize } |
467 | } |
468 | |
469 | /// Determines if the string is empty. |
470 | pub fn is_empty(&self) -> bool { |
471 | self.len() == 0 |
472 | } |
473 | } |
474 | |
475 | impl fmt::Debug for Asn1StringRef { |
476 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
477 | match self.as_utf8() { |
478 | Ok(openssl_string: OpensslString) => openssl_string.fmt(fmt), |
479 | Err(_) => fmt.write_str(data:"error" ), |
480 | } |
481 | } |
482 | } |
483 | |
484 | foreign_type_and_impl_send_sync! { |
485 | type CType = ffi::ASN1_INTEGER; |
486 | fn drop = ffi::ASN1_INTEGER_free; |
487 | |
488 | /// Numeric representation |
489 | /// |
490 | /// Integers in ASN.1 may include BigNum, int64 or uint64. BigNum implementation |
491 | /// can be found within [`bn`] module. |
492 | /// |
493 | /// OpenSSL documentation includes [`ASN1_INTEGER_set`]. |
494 | /// |
495 | /// [`bn`]: ../bn/index.html |
496 | /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/manmaster/crypto/ASN1_INTEGER_set.html |
497 | pub struct Asn1Integer; |
498 | /// A reference to an [`Asn1Integer`]. |
499 | pub struct Asn1IntegerRef; |
500 | } |
501 | |
502 | impl Asn1Integer { |
503 | /// Converts a bignum to an `Asn1Integer`. |
504 | /// |
505 | /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see |
506 | /// [`BigNumRef::to_asn1_integer`]. |
507 | /// |
508 | /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/manmaster/crypto/BN_to_ASN1_INTEGER.html |
509 | /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer |
510 | pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> { |
511 | bn.to_asn1_integer() |
512 | } |
513 | } |
514 | |
515 | impl Ord for Asn1Integer { |
516 | fn cmp(&self, other: &Self) -> Ordering { |
517 | Asn1IntegerRef::cmp(self, other) |
518 | } |
519 | } |
520 | impl PartialOrd for Asn1Integer { |
521 | fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> { |
522 | Some(self.cmp(other)) |
523 | } |
524 | } |
525 | impl Eq for Asn1Integer {} |
526 | impl PartialEq for Asn1Integer { |
527 | fn eq(&self, other: &Asn1Integer) -> bool { |
528 | Asn1IntegerRef::eq(self, other) |
529 | } |
530 | } |
531 | |
532 | impl Asn1IntegerRef { |
533 | #[allow (missing_docs, clippy::unnecessary_cast)] |
534 | #[deprecated (since = "0.10.6" , note = "use to_bn instead" )] |
535 | pub fn get(&self) -> i64 { |
536 | unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 } |
537 | } |
538 | |
539 | /// Converts the integer to a `BigNum`. |
540 | #[corresponds (ASN1_INTEGER_to_BN)] |
541 | pub fn to_bn(&self) -> Result<BigNum, ErrorStack> { |
542 | unsafe { |
543 | cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut())) |
544 | .map(|p| BigNum::from_ptr(p)) |
545 | } |
546 | } |
547 | |
548 | /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers |
549 | /// see [`bn`]. |
550 | /// |
551 | /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer |
552 | #[corresponds (ASN1_INTEGER_set)] |
553 | pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { |
554 | unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } |
555 | } |
556 | |
557 | /// Creates a new Asn1Integer with the same value. |
558 | #[corresponds (ASN1_INTEGER_dup)] |
559 | pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> { |
560 | unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) } |
561 | } |
562 | } |
563 | |
564 | impl Ord for Asn1IntegerRef { |
565 | fn cmp(&self, other: &Self) -> Ordering { |
566 | let res: i32 = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), b:other.as_ptr()) }; |
567 | res.cmp(&0) |
568 | } |
569 | } |
570 | impl PartialOrd for Asn1IntegerRef { |
571 | fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> { |
572 | Some(self.cmp(other)) |
573 | } |
574 | } |
575 | impl Eq for Asn1IntegerRef {} |
576 | impl PartialEq for Asn1IntegerRef { |
577 | fn eq(&self, other: &Asn1IntegerRef) -> bool { |
578 | self.cmp(other) == Ordering::Equal |
579 | } |
580 | } |
581 | |
582 | foreign_type_and_impl_send_sync! { |
583 | type CType = ffi::ASN1_BIT_STRING; |
584 | fn drop = ffi::ASN1_BIT_STRING_free; |
585 | /// Sequence of bytes |
586 | /// |
587 | /// Asn1BitString is used in [`x509`] certificates for the signature. |
588 | /// The bit string acts as a collection of bytes. |
589 | /// |
590 | /// [`x509`]: ../x509/struct.X509.html#method.signature |
591 | pub struct Asn1BitString; |
592 | /// A reference to an [`Asn1BitString`]. |
593 | pub struct Asn1BitStringRef; |
594 | } |
595 | |
596 | impl Asn1BitStringRef { |
597 | /// Returns the Asn1BitString as a slice. |
598 | #[corresponds (ASN1_STRING_get0_data)] |
599 | pub fn as_slice(&self) -> &[u8] { |
600 | unsafe { slice::from_raw_parts(data:ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } |
601 | } |
602 | |
603 | /// Returns the number of bytes in the string. |
604 | #[corresponds (ASN1_STRING_length)] |
605 | pub fn len(&self) -> usize { |
606 | unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } |
607 | } |
608 | |
609 | /// Determines if the string is empty. |
610 | pub fn is_empty(&self) -> bool { |
611 | self.len() == 0 |
612 | } |
613 | } |
614 | |
615 | foreign_type_and_impl_send_sync! { |
616 | type CType = ffi::ASN1_OCTET_STRING; |
617 | fn drop = ffi::ASN1_OCTET_STRING_free; |
618 | /// ASN.1 OCTET STRING type |
619 | pub struct Asn1OctetString; |
620 | /// A reference to an [`Asn1OctetString`]. |
621 | pub struct Asn1OctetStringRef; |
622 | } |
623 | |
624 | impl Asn1OctetString { |
625 | /// Creates an Asn1OctetString from bytes |
626 | pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> { |
627 | ffi::init(); |
628 | unsafe { |
629 | let s: *mut ASN1_OCTET_STRING = cvt_p(ffi::ASN1_OCTET_STRING_new())?; |
630 | ffi::ASN1_OCTET_STRING_set(x:s, data:value.as_ptr(), len_in:value.len().try_into().unwrap()); |
631 | Ok(Self::from_ptr(s)) |
632 | } |
633 | } |
634 | } |
635 | |
636 | impl Asn1OctetStringRef { |
637 | /// Returns the octet string as an array of bytes. |
638 | #[corresponds (ASN1_STRING_get0_data)] |
639 | pub fn as_slice(&self) -> &[u8] { |
640 | unsafe { slice::from_raw_parts(data:ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) } |
641 | } |
642 | |
643 | /// Returns the number of bytes in the octet string. |
644 | #[corresponds (ASN1_STRING_length)] |
645 | pub fn len(&self) -> usize { |
646 | unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize } |
647 | } |
648 | |
649 | /// Determines if the string is empty. |
650 | pub fn is_empty(&self) -> bool { |
651 | self.len() == 0 |
652 | } |
653 | } |
654 | |
655 | foreign_type_and_impl_send_sync! { |
656 | type CType = ffi::ASN1_OBJECT; |
657 | fn drop = ffi::ASN1_OBJECT_free; |
658 | fn clone = ffi::OBJ_dup; |
659 | |
660 | /// Object Identifier |
661 | /// |
662 | /// Represents an ASN.1 Object. Typically, NIDs, or numeric identifiers |
663 | /// are stored as a table within the [`Nid`] module. These constants are |
664 | /// used to determine attributes of a certificate, such as mapping the |
665 | /// attribute "CommonName" to "CN" which is represented as the OID of 13. |
666 | /// This attribute is a constant in the [`nid::COMMONNAME`]. |
667 | /// |
668 | /// OpenSSL documentation at [`OBJ_nid2obj`] |
669 | /// |
670 | /// [`Nid`]: ../nid/index.html |
671 | /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html |
672 | /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/manmaster/crypto/OBJ_obj2nid.html |
673 | pub struct Asn1Object; |
674 | /// A reference to an [`Asn1Object`]. |
675 | pub struct Asn1ObjectRef; |
676 | } |
677 | |
678 | impl Stackable for Asn1Object { |
679 | type StackType = ffi::stack_st_ASN1_OBJECT; |
680 | } |
681 | |
682 | impl Asn1Object { |
683 | /// Constructs an ASN.1 Object Identifier from a string representation of the OID. |
684 | #[corresponds (OBJ_txt2obj)] |
685 | #[allow (clippy::should_implement_trait)] |
686 | pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> { |
687 | unsafe { |
688 | ffi::init(); |
689 | let txt = CString::new(txt).unwrap(); |
690 | let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?; |
691 | Ok(Asn1Object::from_ptr(obj)) |
692 | } |
693 | } |
694 | |
695 | /// Return the OID as an DER encoded array of bytes. This is the ASN.1 |
696 | /// value, not including tag or length. |
697 | /// |
698 | /// Requires OpenSSL 1.1.1 or newer. |
699 | #[corresponds (OBJ_get0_data)] |
700 | #[cfg (ossl111)] |
701 | pub fn as_slice(&self) -> &[u8] { |
702 | unsafe { |
703 | let len = ffi::OBJ_length(self.as_ptr()); |
704 | slice::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len) |
705 | } |
706 | } |
707 | } |
708 | |
709 | impl Asn1ObjectRef { |
710 | /// Returns the NID associated with this OID. |
711 | pub fn nid(&self) -> Nid { |
712 | unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) } |
713 | } |
714 | } |
715 | |
716 | impl fmt::Display for Asn1ObjectRef { |
717 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
718 | unsafe { |
719 | let mut buf: [u8; 80] = [0; 80]; |
720 | let len: i32 = ffi::OBJ_obj2txt( |
721 | buf:buf.as_mut_ptr() as *mut _, |
722 | buf_len:buf.len() as c_int, |
723 | self.as_ptr(), |
724 | no_name:0, |
725 | ); |
726 | match str::from_utf8(&buf[..len as usize]) { |
727 | Err(_) => fmt.write_str(data:"error" ), |
728 | Ok(s: &str) => fmt.write_str(data:s), |
729 | } |
730 | } |
731 | } |
732 | } |
733 | |
734 | impl fmt::Debug for Asn1ObjectRef { |
735 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
736 | fmt.write_str(self.to_string().as_str()) |
737 | } |
738 | } |
739 | |
740 | cfg_if! { |
741 | if #[cfg(any(ossl110, libressl273, boringssl))] { |
742 | use ffi::ASN1_STRING_get0_data; |
743 | } else { |
744 | #[allow (bad_style)] |
745 | unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar { |
746 | ffi::ASN1_STRING_data(s) |
747 | } |
748 | } |
749 | } |
750 | |
751 | foreign_type_and_impl_send_sync! { |
752 | type CType = ffi::ASN1_ENUMERATED; |
753 | fn drop = ffi::ASN1_ENUMERATED_free; |
754 | |
755 | /// An ASN.1 enumerated. |
756 | pub struct Asn1Enumerated; |
757 | /// A reference to an [`Asn1Enumerated`]. |
758 | pub struct Asn1EnumeratedRef; |
759 | } |
760 | |
761 | impl Asn1EnumeratedRef { |
762 | /// Get the value, if it fits in the required bounds. |
763 | #[corresponds (ASN1_ENUMERATED_get_int64)] |
764 | #[cfg (ossl110)] |
765 | pub fn get_i64(&self) -> Result<i64, ErrorStack> { |
766 | let mut crl_reason: i64 = 0; |
767 | unsafe { |
768 | cvt(ffi::ASN1_ENUMERATED_get_int64( |
769 | &mut crl_reason, |
770 | self.as_ptr(), |
771 | ))?; |
772 | } |
773 | Ok(crl_reason) |
774 | } |
775 | } |
776 | |
777 | #[cfg (test)] |
778 | mod tests { |
779 | use super::*; |
780 | |
781 | use crate::bn::BigNum; |
782 | use crate::nid::Nid; |
783 | |
784 | /// Tests conversion between BigNum and Asn1Integer. |
785 | #[test ] |
786 | fn bn_cvt() { |
787 | fn roundtrip(bn: BigNum) { |
788 | let large = Asn1Integer::from_bn(&bn).unwrap(); |
789 | assert_eq!(large.to_bn().unwrap(), bn); |
790 | } |
791 | |
792 | roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000" ).unwrap()); |
793 | roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000" ).unwrap()); |
794 | roundtrip(BigNum::from_u32(1234).unwrap()); |
795 | roundtrip(-BigNum::from_u32(1234).unwrap()); |
796 | } |
797 | |
798 | #[test ] |
799 | fn time_from_str() { |
800 | Asn1Time::from_str("99991231235959Z" ).unwrap(); |
801 | #[cfg (ossl111)] |
802 | Asn1Time::from_str_x509("99991231235959Z" ).unwrap(); |
803 | } |
804 | |
805 | #[test ] |
806 | fn time_from_unix() { |
807 | let t = Asn1Time::from_unix(0).unwrap(); |
808 | assert_eq!("Jan 1 00:00:00 1970 GMT" , t.to_string()); |
809 | } |
810 | |
811 | #[test ] |
812 | #[cfg (any(ossl102, boringssl))] |
813 | fn time_eq() { |
814 | let a = Asn1Time::from_str("99991231235959Z" ).unwrap(); |
815 | let b = Asn1Time::from_str("99991231235959Z" ).unwrap(); |
816 | let c = Asn1Time::from_str("99991231235958Z" ).unwrap(); |
817 | let a_ref = a.as_ref(); |
818 | let b_ref = b.as_ref(); |
819 | let c_ref = c.as_ref(); |
820 | assert!(a == b); |
821 | assert!(a != c); |
822 | assert!(a == b_ref); |
823 | assert!(a != c_ref); |
824 | assert!(b_ref == a); |
825 | assert!(c_ref != a); |
826 | assert!(a_ref == b_ref); |
827 | assert!(a_ref != c_ref); |
828 | } |
829 | |
830 | #[test ] |
831 | #[cfg (any(ossl102, boringssl))] |
832 | fn time_ord() { |
833 | let a = Asn1Time::from_str("99991231235959Z" ).unwrap(); |
834 | let b = Asn1Time::from_str("99991231235959Z" ).unwrap(); |
835 | let c = Asn1Time::from_str("99991231235958Z" ).unwrap(); |
836 | let a_ref = a.as_ref(); |
837 | let b_ref = b.as_ref(); |
838 | let c_ref = c.as_ref(); |
839 | assert!(a >= b); |
840 | assert!(a > c); |
841 | assert!(b <= a); |
842 | assert!(c < a); |
843 | |
844 | assert!(a_ref >= b); |
845 | assert!(a_ref > c); |
846 | assert!(b_ref <= a); |
847 | assert!(c_ref < a); |
848 | |
849 | assert!(a >= b_ref); |
850 | assert!(a > c_ref); |
851 | assert!(b <= a_ref); |
852 | assert!(c < a_ref); |
853 | |
854 | assert!(a_ref >= b_ref); |
855 | assert!(a_ref > c_ref); |
856 | assert!(b_ref <= a_ref); |
857 | assert!(c_ref < a_ref); |
858 | } |
859 | |
860 | #[test ] |
861 | fn integer_to_owned() { |
862 | let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42" ).unwrap()).unwrap(); |
863 | let b = a.to_owned().unwrap(); |
864 | assert_eq!( |
865 | a.to_bn().unwrap().to_dec_str().unwrap().to_string(), |
866 | b.to_bn().unwrap().to_dec_str().unwrap().to_string(), |
867 | ); |
868 | assert_ne!(a.as_ptr(), b.as_ptr()); |
869 | } |
870 | |
871 | #[test ] |
872 | fn integer_cmp() { |
873 | let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42" ).unwrap()).unwrap(); |
874 | let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42" ).unwrap()).unwrap(); |
875 | let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43" ).unwrap()).unwrap(); |
876 | assert!(a == b); |
877 | assert!(a != c); |
878 | assert!(a < c); |
879 | assert!(c > b); |
880 | } |
881 | |
882 | #[test ] |
883 | fn object_from_str() { |
884 | let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1" ).unwrap(); |
885 | assert_eq!(object.nid(), Nid::SHA256); |
886 | } |
887 | |
888 | #[test ] |
889 | fn object_from_str_with_invalid_input() { |
890 | Asn1Object::from_str("NOT AN OID" ) |
891 | .map(|object| object.to_string()) |
892 | .expect_err("parsing invalid OID should fail" ); |
893 | } |
894 | |
895 | #[test ] |
896 | #[cfg (ossl111)] |
897 | fn object_to_slice() { |
898 | let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1" ).unwrap(); |
899 | assert_eq!( |
900 | object.as_slice(), |
901 | &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01], |
902 | ); |
903 | } |
904 | |
905 | #[test ] |
906 | fn asn1_octet_string() { |
907 | let octet_string = Asn1OctetString::new_from_bytes(b"hello world" ).unwrap(); |
908 | assert_eq!(octet_string.as_slice(), b"hello world" ); |
909 | assert_eq!(octet_string.len(), 11); |
910 | } |
911 | } |
912 | |