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