1 | //! This crate provides types for representing X.509 certificates, keys and other types as |
2 | //! commonly used in the rustls ecosystem. It is intended to be used by crates that need to work |
3 | //! with such X.509 types, such as [rustls](https://crates.io/crates/rustls), |
4 | //! [rustls-webpki](https://crates.io/crates/rustls-webpki), |
5 | //! [rustls-pemfile](https://crates.io/crates/rustls-pemfile), and others. |
6 | //! |
7 | //! Some of these crates used to define their own trivial wrappers around DER-encoded bytes. |
8 | //! However, in order to avoid inconvenient dependency edges, these were all disconnected. By |
9 | //! using a common low-level crate of types with long-term stable API, we hope to avoid the |
10 | //! downsides of unnecessary dependency edges while providing good interoperability between crates. |
11 | //! |
12 | //! ## DER and PEM |
13 | //! |
14 | //! Many of the types defined in this crate represent DER-encoded data. DER is a binary encoding of |
15 | //! the ASN.1 format commonly used in web PKI specifications. It is a binary encoding, so it is |
16 | //! relatively compact when stored in memory. However, as a binary format, it is not very easy to |
17 | //! work with for humans and in contexts where binary data is inconvenient. For this reason, |
18 | //! many tools and protocols use a ASCII-based encoding of DER, called PEM. In addition to the |
19 | //! base64-encoded DER, PEM objects are delimited by header and footer lines which indicate the type |
20 | //! of object contained in the PEM blob. |
21 | //! |
22 | //! Types here can be created from: |
23 | //! |
24 | //! - DER using (for example) [`PrivatePkcs8KeyDer::from()`]. |
25 | //! - PEM using (for example) [`pem::PemObject::from_pem_slice()`]. |
26 | //! |
27 | //! The [`pem::PemObject`] trait contains the full selection of ways to construct |
28 | //! these types from PEM encodings. That includes ways to open and read from a file, |
29 | //! from a slice, or from an `std::io` stream. |
30 | //! |
31 | //! There is also a lower-level API that allows a given PEM file to be fully consumed |
32 | //! in one pass, even if it contains different data types: see the implementation of |
33 | //! the [`pem::PemObject`] trait on the `(pem::SectionKind, Vec<u8>)` tuple. |
34 | //! |
35 | //! ## Creating new certificates and keys |
36 | //! |
37 | //! This crate does not provide any functionality for creating new certificates or keys. However, |
38 | //! the [rcgen](https://docs.rs/rcgen) crate can be used to create new certificates and keys. |
39 | //! |
40 | //! ## Cloning private keys |
41 | //! |
42 | //! This crate intentionally **does not** implement `Clone` on private key types in |
43 | //! order to minimize the exposure of private key data in memory. |
44 | //! |
45 | //! If you want to extend the lifetime of a `PrivateKeyDer<'_>`, consider [`PrivateKeyDer::clone_key()`]. |
46 | //! Alternatively since these types are immutable, consider wrapping the `PrivateKeyDer<'_>` in a [`Rc`] |
47 | //! or an [`Arc`]. |
48 | //! |
49 | //! [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html |
50 | //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html |
51 | //! [`PrivateKeyDer::clone_key()`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html#method.clone_key |
52 | //! |
53 | //! ## Target `wasm32-unknown-unknown` with the `web` feature |
54 | //! |
55 | //! [`std::time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html) |
56 | //! is unavailable in `wasm32-unknown-unknown` targets, so calls to |
57 | //! [`UnixTime::now()`](https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.UnixTime.html#method.now), |
58 | //! otherwise enabled by the [`std`](https://docs.rs/crate/rustls-pki-types/latest/features#std) feature, |
59 | //! require building instead with the [`web`](https://docs.rs/crate/rustls-pki-types/latest/features#web) |
60 | //! feature. It gets time by calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) |
61 | //! in the browser. |
62 | |
63 | #![cfg_attr (not(feature = "std" ), no_std)] |
64 | #![warn (unreachable_pub, clippy::use_self)] |
65 | #![deny (missing_docs)] |
66 | #![cfg_attr (docsrs, feature(doc_cfg, doc_auto_cfg))] |
67 | |
68 | #[cfg (feature = "alloc" )] |
69 | extern crate alloc; |
70 | |
71 | #[cfg (feature = "alloc" )] |
72 | use alloc::vec::Vec; |
73 | use core::fmt; |
74 | use core::ops::Deref; |
75 | use core::time::Duration; |
76 | #[cfg (feature = "alloc" )] |
77 | use pem::{PemObject, PemObjectFilter, SectionKind}; |
78 | #[cfg (all( |
79 | feature = "std" , |
80 | not(all(target_family = "wasm" , target_os = "unknown" )) |
81 | ))] |
82 | use std::time::SystemTime; |
83 | #[cfg (all(target_family = "wasm" , target_os = "unknown" , feature = "web" ))] |
84 | use web_time::SystemTime; |
85 | |
86 | pub mod alg_id; |
87 | mod base64; |
88 | mod server_name; |
89 | |
90 | /// Low-level PEM decoding APIs. |
91 | /// |
92 | /// These APIs allow decoding PEM format in an iterator, which means you |
93 | /// can load multiple different types of PEM section from a file in a single |
94 | /// pass. |
95 | #[cfg (feature = "alloc" )] |
96 | pub mod pem; |
97 | |
98 | pub use alg_id::AlgorithmIdentifier; |
99 | pub use server_name::{ |
100 | AddrParseError, DnsName, InvalidDnsNameError, IpAddr, Ipv4Addr, Ipv6Addr, ServerName, |
101 | }; |
102 | |
103 | /// A DER-encoded X.509 private key, in one of several formats |
104 | /// |
105 | /// See variant inner types for more detailed information. |
106 | /// |
107 | /// This can load several types of PEM-encoded private key, and then reveal |
108 | /// which types were found: |
109 | /// |
110 | /// ```rust |
111 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
112 | /// use rustls_pki_types::{PrivateKeyDer, pem::PemObject}; |
113 | /// |
114 | /// // load from a PEM file |
115 | /// let pkcs8 = PrivateKeyDer::from_pem_file("tests/data/nistp256key.pkcs8.pem" ).unwrap(); |
116 | /// let pkcs1 = PrivateKeyDer::from_pem_file("tests/data/rsa1024.pkcs1.pem" ).unwrap(); |
117 | /// let sec1 = PrivateKeyDer::from_pem_file("tests/data/nistp256key.pem" ).unwrap(); |
118 | /// assert!(matches!(pkcs8, PrivateKeyDer::Pkcs8(_))); |
119 | /// assert!(matches!(pkcs1, PrivateKeyDer::Pkcs1(_))); |
120 | /// assert!(matches!(sec1, PrivateKeyDer::Sec1(_))); |
121 | /// # } |
122 | /// ``` |
123 | #[non_exhaustive ] |
124 | #[derive (Debug, PartialEq, Eq)] |
125 | pub enum PrivateKeyDer<'a> { |
126 | /// An RSA private key |
127 | Pkcs1(PrivatePkcs1KeyDer<'a>), |
128 | /// A Sec1 private key |
129 | Sec1(PrivateSec1KeyDer<'a>), |
130 | /// A PKCS#8 private key |
131 | Pkcs8(PrivatePkcs8KeyDer<'a>), |
132 | } |
133 | |
134 | impl PrivateKeyDer<'_> { |
135 | /// Clone the private key to a `'static` value |
136 | #[cfg (feature = "alloc" )] |
137 | pub fn clone_key(&self) -> PrivateKeyDer<'static> { |
138 | use PrivateKeyDer::*; |
139 | match self { |
140 | Pkcs1(key: &PrivatePkcs1KeyDer<'_>) => Pkcs1(key.clone_key()), |
141 | Sec1(key: &PrivateSec1KeyDer<'_>) => Sec1(key.clone_key()), |
142 | Pkcs8(key: &PrivatePkcs8KeyDer<'_>) => Pkcs8(key.clone_key()), |
143 | } |
144 | } |
145 | |
146 | /// Yield the DER-encoded bytes of the private key |
147 | pub fn secret_der(&self) -> &[u8] { |
148 | match self { |
149 | PrivateKeyDer::Pkcs1(key: &PrivatePkcs1KeyDer<'_>) => key.secret_pkcs1_der(), |
150 | PrivateKeyDer::Sec1(key: &PrivateSec1KeyDer<'_>) => key.secret_sec1_der(), |
151 | PrivateKeyDer::Pkcs8(key: &PrivatePkcs8KeyDer<'_>) => key.secret_pkcs8_der(), |
152 | } |
153 | } |
154 | } |
155 | |
156 | #[cfg (feature = "alloc" )] |
157 | impl PemObject for PrivateKeyDer<'static> { |
158 | fn from_pem(kind: SectionKind, value: Vec<u8>) -> Option<Self> { |
159 | match kind { |
160 | SectionKind::RsaPrivateKey => Some(Self::Pkcs1(value.into())), |
161 | SectionKind::EcPrivateKey => Some(Self::Sec1(value.into())), |
162 | SectionKind::PrivateKey => Some(Self::Pkcs8(value.into())), |
163 | _ => None, |
164 | } |
165 | } |
166 | } |
167 | |
168 | impl<'a> From<PrivatePkcs1KeyDer<'a>> for PrivateKeyDer<'a> { |
169 | fn from(key: PrivatePkcs1KeyDer<'a>) -> Self { |
170 | Self::Pkcs1(key) |
171 | } |
172 | } |
173 | |
174 | impl<'a> From<PrivateSec1KeyDer<'a>> for PrivateKeyDer<'a> { |
175 | fn from(key: PrivateSec1KeyDer<'a>) -> Self { |
176 | Self::Sec1(key) |
177 | } |
178 | } |
179 | |
180 | impl<'a> From<PrivatePkcs8KeyDer<'a>> for PrivateKeyDer<'a> { |
181 | fn from(key: PrivatePkcs8KeyDer<'a>) -> Self { |
182 | Self::Pkcs8(key) |
183 | } |
184 | } |
185 | |
186 | impl<'a> TryFrom<&'a [u8]> for PrivateKeyDer<'a> { |
187 | type Error = &'static str; |
188 | |
189 | fn try_from(key: &'a [u8]) -> Result<Self, Self::Error> { |
190 | const SHORT_FORM_LEN_MAX: u8 = 128; |
191 | const TAG_SEQUENCE: u8 = 0x30; |
192 | const TAG_INTEGER: u8 = 0x02; |
193 | |
194 | // We expect all key formats to begin with a SEQUENCE, which requires at least 2 bytes |
195 | // in the short length encoding. |
196 | if key.first() != Some(&TAG_SEQUENCE) || key.len() < 2 { |
197 | return Err(INVALID_KEY_DER_ERR); |
198 | } |
199 | |
200 | // The length of the SEQUENCE is encoded in the second byte. We must skip this many bytes. |
201 | let skip_len = match key[1] >= SHORT_FORM_LEN_MAX { |
202 | // 1 byte for SEQUENCE tag, 1 byte for short-form len |
203 | false => 2, |
204 | // 1 byte for SEQUENCE tag, 1 byte for start of len, remaining bytes encoded |
205 | // in key[1]. |
206 | true => 2 + (key[1] - SHORT_FORM_LEN_MAX) as usize, |
207 | }; |
208 | let key_bytes = key.get(skip_len..).ok_or(INVALID_KEY_DER_ERR)?; |
209 | |
210 | // PKCS#8 (https://www.rfc-editor.org/rfc/rfc5208) describes the PrivateKeyInfo |
211 | // structure as: |
212 | // PrivateKeyInfo ::= SEQUENCE { |
213 | // version Version, |
214 | // privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, |
215 | // privateKey PrivateKey, |
216 | // attributes [0] Attributes OPTIONAL |
217 | // } |
218 | // PKCS#5 (https://www.rfc-editor.org/rfc/rfc8018) describes the AlgorithmIdentifier |
219 | // as a SEQUENCE. |
220 | // |
221 | // Therefore, we consider the outer SEQUENCE, a version number, and the start of |
222 | // an AlgorithmIdentifier to be enough to identify a PKCS#8 key. If it were PKCS#1 or SEC1 |
223 | // the version would not be followed by a SEQUENCE. |
224 | if matches!(key_bytes, [TAG_INTEGER, 0x01, _, TAG_SEQUENCE, ..]) { |
225 | return Ok(Self::Pkcs8(key.into())); |
226 | } |
227 | |
228 | // PKCS#1 (https://www.rfc-editor.org/rfc/rfc8017) describes the RSAPrivateKey structure |
229 | // as: |
230 | // RSAPrivateKey ::= SEQUENCE { |
231 | // version Version, |
232 | // modulus INTEGER, -- n |
233 | // publicExponent INTEGER, -- e |
234 | // privateExponent INTEGER, -- d |
235 | // prime1 INTEGER, -- p |
236 | // prime2 INTEGER, -- q |
237 | // exponent1 INTEGER, -- d mod (p-1) |
238 | // exponent2 INTEGER, -- d mod (q-1) |
239 | // coefficient INTEGER, -- (inverse of q) mod p |
240 | // otherPrimeInfos OtherPrimeInfos OPTIONAL |
241 | // } |
242 | // |
243 | // Therefore, we consider the outer SEQUENCE and a Version of 0 to be enough to identify |
244 | // a PKCS#1 key. If it were PKCS#8, the version would be followed by a SEQUENCE. If it |
245 | // were SEC1, the VERSION would have been 1. |
246 | if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x00]) { |
247 | return Ok(Self::Pkcs1(key.into())); |
248 | } |
249 | |
250 | // SEC1 (https://www.rfc-editor.org/rfc/rfc5915) describes the ECPrivateKey structure as: |
251 | // ECPrivateKey ::= SEQUENCE { |
252 | // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), |
253 | // privateKey OCTET STRING, |
254 | // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, |
255 | // publicKey [1] BIT STRING OPTIONAL |
256 | // } |
257 | // |
258 | // Therefore, we consider the outer SEQUENCE and an INTEGER of 1 to be enough to |
259 | // identify a SEC1 key. If it were PKCS#8 or PKCS#1, the version would have been 0. |
260 | if key_bytes.starts_with(&[TAG_INTEGER, 0x01, 0x01]) { |
261 | return Ok(Self::Sec1(key.into())); |
262 | } |
263 | |
264 | Err(INVALID_KEY_DER_ERR) |
265 | } |
266 | } |
267 | |
268 | static INVALID_KEY_DER_ERR: &str = "unknown or invalid key format" ; |
269 | |
270 | #[cfg (feature = "alloc" )] |
271 | impl TryFrom<Vec<u8>> for PrivateKeyDer<'_> { |
272 | type Error = &'static str; |
273 | |
274 | fn try_from(key: Vec<u8>) -> Result<Self, Self::Error> { |
275 | Ok(match PrivateKeyDer::try_from(&key[..])? { |
276 | PrivateKeyDer::Pkcs1(_) => Self::Pkcs1(key.into()), |
277 | PrivateKeyDer::Sec1(_) => Self::Sec1(key.into()), |
278 | PrivateKeyDer::Pkcs8(_) => Self::Pkcs8(key.into()), |
279 | }) |
280 | } |
281 | } |
282 | |
283 | /// A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC 3447 |
284 | /// |
285 | /// RSA private keys are identified in PEM context as `RSA PRIVATE KEY` and when stored in a |
286 | /// file usually use a `.pem` or `.key` extension. |
287 | /// |
288 | /// ```rust |
289 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
290 | /// use rustls_pki_types::{PrivatePkcs1KeyDer, pem::PemObject}; |
291 | /// |
292 | /// // load from a PEM file |
293 | /// PrivatePkcs1KeyDer::from_pem_file("tests/data/rsa1024.pkcs1.pem" ).unwrap(); |
294 | /// |
295 | /// // or from a PEM byte slice... |
296 | /// # let byte_slice = include_bytes!("../tests/data/rsa1024.pkcs1.pem" ); |
297 | /// PrivatePkcs1KeyDer::from_pem_slice(byte_slice).unwrap(); |
298 | /// # } |
299 | /// ``` |
300 | #[derive (PartialEq, Eq)] |
301 | pub struct PrivatePkcs1KeyDer<'a>(Der<'a>); |
302 | |
303 | impl PrivatePkcs1KeyDer<'_> { |
304 | /// Clone the private key to a `'static` value |
305 | #[cfg (feature = "alloc" )] |
306 | pub fn clone_key(&self) -> PrivatePkcs1KeyDer<'static> { |
307 | PrivatePkcs1KeyDer::from(self.0.as_ref().to_vec()) |
308 | } |
309 | |
310 | /// Yield the DER-encoded bytes of the private key |
311 | pub fn secret_pkcs1_der(&self) -> &[u8] { |
312 | self.0.as_ref() |
313 | } |
314 | } |
315 | |
316 | #[cfg (feature = "alloc" )] |
317 | impl PemObjectFilter for PrivatePkcs1KeyDer<'static> { |
318 | const KIND: SectionKind = SectionKind::RsaPrivateKey; |
319 | } |
320 | |
321 | impl<'a> From<&'a [u8]> for PrivatePkcs1KeyDer<'a> { |
322 | fn from(slice: &'a [u8]) -> Self { |
323 | Self(Der(BytesInner::Borrowed(slice))) |
324 | } |
325 | } |
326 | |
327 | #[cfg (feature = "alloc" )] |
328 | impl From<Vec<u8>> for PrivatePkcs1KeyDer<'_> { |
329 | fn from(vec: Vec<u8>) -> Self { |
330 | Self(Der(BytesInner::Owned(vec))) |
331 | } |
332 | } |
333 | |
334 | impl fmt::Debug for PrivatePkcs1KeyDer<'_> { |
335 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
336 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivatePkcs1KeyDer" ) |
337 | .field(&"[secret key elided]" ) |
338 | .finish() |
339 | } |
340 | } |
341 | |
342 | /// A Sec1-encoded plaintext private key; as specified in RFC 5915 |
343 | /// |
344 | /// Sec1 private keys are identified in PEM context as `EC PRIVATE KEY` and when stored in a |
345 | /// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate |
346 | /// documentation. |
347 | /// |
348 | /// ```rust |
349 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
350 | /// use rustls_pki_types::{PrivateSec1KeyDer, pem::PemObject}; |
351 | /// |
352 | /// // load from a PEM file |
353 | /// PrivateSec1KeyDer::from_pem_file("tests/data/nistp256key.pem" ).unwrap(); |
354 | /// |
355 | /// // or from a PEM byte slice... |
356 | /// # let byte_slice = include_bytes!("../tests/data/nistp256key.pem" ); |
357 | /// PrivateSec1KeyDer::from_pem_slice(byte_slice).unwrap(); |
358 | /// # } |
359 | /// ``` |
360 | #[derive (PartialEq, Eq)] |
361 | pub struct PrivateSec1KeyDer<'a>(Der<'a>); |
362 | |
363 | impl PrivateSec1KeyDer<'_> { |
364 | /// Clone the private key to a `'static` value |
365 | #[cfg (feature = "alloc" )] |
366 | pub fn clone_key(&self) -> PrivateSec1KeyDer<'static> { |
367 | PrivateSec1KeyDer::from(self.0.as_ref().to_vec()) |
368 | } |
369 | |
370 | /// Yield the DER-encoded bytes of the private key |
371 | pub fn secret_sec1_der(&self) -> &[u8] { |
372 | self.0.as_ref() |
373 | } |
374 | } |
375 | |
376 | #[cfg (feature = "alloc" )] |
377 | impl PemObjectFilter for PrivateSec1KeyDer<'static> { |
378 | const KIND: SectionKind = SectionKind::EcPrivateKey; |
379 | } |
380 | |
381 | impl<'a> From<&'a [u8]> for PrivateSec1KeyDer<'a> { |
382 | fn from(slice: &'a [u8]) -> Self { |
383 | Self(Der(BytesInner::Borrowed(slice))) |
384 | } |
385 | } |
386 | |
387 | #[cfg (feature = "alloc" )] |
388 | impl From<Vec<u8>> for PrivateSec1KeyDer<'_> { |
389 | fn from(vec: Vec<u8>) -> Self { |
390 | Self(Der(BytesInner::Owned(vec))) |
391 | } |
392 | } |
393 | |
394 | impl fmt::Debug for PrivateSec1KeyDer<'_> { |
395 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
396 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivateSec1KeyDer" ) |
397 | .field(&"[secret key elided]" ) |
398 | .finish() |
399 | } |
400 | } |
401 | |
402 | /// A DER-encoded plaintext private key; as specified in PKCS#8/RFC 5958 |
403 | /// |
404 | /// PKCS#8 private keys are identified in PEM context as `PRIVATE KEY` and when stored in a |
405 | /// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate |
406 | /// documentation. |
407 | /// |
408 | /// ```rust |
409 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
410 | /// use rustls_pki_types::{PrivatePkcs8KeyDer, pem::PemObject}; |
411 | /// |
412 | /// // load from a PEM file |
413 | /// PrivatePkcs8KeyDer::from_pem_file("tests/data/nistp256key.pkcs8.pem" ).unwrap(); |
414 | /// PrivatePkcs8KeyDer::from_pem_file("tests/data/rsa1024.pkcs8.pem" ).unwrap(); |
415 | /// |
416 | /// // or from a PEM byte slice... |
417 | /// # let byte_slice = include_bytes!("../tests/data/nistp256key.pkcs8.pem" ); |
418 | /// PrivatePkcs8KeyDer::from_pem_slice(byte_slice).unwrap(); |
419 | /// # } |
420 | /// ``` |
421 | #[derive (PartialEq, Eq)] |
422 | pub struct PrivatePkcs8KeyDer<'a>(Der<'a>); |
423 | |
424 | impl PrivatePkcs8KeyDer<'_> { |
425 | /// Clone the private key to a `'static` value |
426 | #[cfg (feature = "alloc" )] |
427 | pub fn clone_key(&self) -> PrivatePkcs8KeyDer<'static> { |
428 | PrivatePkcs8KeyDer::from(self.0.as_ref().to_vec()) |
429 | } |
430 | |
431 | /// Yield the DER-encoded bytes of the private key |
432 | pub fn secret_pkcs8_der(&self) -> &[u8] { |
433 | self.0.as_ref() |
434 | } |
435 | } |
436 | |
437 | #[cfg (feature = "alloc" )] |
438 | impl PemObjectFilter for PrivatePkcs8KeyDer<'static> { |
439 | const KIND: SectionKind = SectionKind::PrivateKey; |
440 | } |
441 | |
442 | impl<'a> From<&'a [u8]> for PrivatePkcs8KeyDer<'a> { |
443 | fn from(slice: &'a [u8]) -> Self { |
444 | Self(Der(BytesInner::Borrowed(slice))) |
445 | } |
446 | } |
447 | |
448 | #[cfg (feature = "alloc" )] |
449 | impl From<Vec<u8>> for PrivatePkcs8KeyDer<'_> { |
450 | fn from(vec: Vec<u8>) -> Self { |
451 | Self(Der(BytesInner::Owned(vec))) |
452 | } |
453 | } |
454 | |
455 | impl fmt::Debug for PrivatePkcs8KeyDer<'_> { |
456 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
457 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivatePkcs8KeyDer" ) |
458 | .field(&"[secret key elided]" ) |
459 | .finish() |
460 | } |
461 | } |
462 | |
463 | /// A trust anchor (a.k.a. root CA) |
464 | /// |
465 | /// Traditionally, certificate verification libraries have represented trust anchors as full X.509 |
466 | /// root certificates. However, those certificates contain a lot more data than is needed for |
467 | /// verifying certificates. The [`TrustAnchor`] representation allows an application to store |
468 | /// just the essential elements of trust anchors. |
469 | /// |
470 | /// The most common way to get one of these is to call [`rustls_webpki::anchor_from_trusted_cert()`]. |
471 | /// |
472 | /// [`rustls_webpki::anchor_from_trusted_cert()`]: https://docs.rs/rustls-webpki/latest/webpki/fn.anchor_from_trusted_cert.html |
473 | #[derive (Clone, Debug, PartialEq, Eq)] |
474 | pub struct TrustAnchor<'a> { |
475 | /// Value of the `subject` field of the trust anchor |
476 | pub subject: Der<'a>, |
477 | /// Value of the `subjectPublicKeyInfo` field of the trust anchor |
478 | pub subject_public_key_info: Der<'a>, |
479 | /// Value of DER-encoded `NameConstraints`, containing name constraints to the trust anchor, if any |
480 | pub name_constraints: Option<Der<'a>>, |
481 | } |
482 | |
483 | impl TrustAnchor<'_> { |
484 | /// Yield a `'static` lifetime of the `TrustAnchor` by allocating owned `Der` variants |
485 | #[cfg (feature = "alloc" )] |
486 | pub fn to_owned(&self) -> TrustAnchor<'static> { |
487 | #[cfg (not(feature = "std" ))] |
488 | use alloc::borrow::ToOwned; |
489 | TrustAnchor { |
490 | subject: self.subject.as_ref().to_owned().into(), |
491 | subject_public_key_info: self.subject_public_key_info.as_ref().to_owned().into(), |
492 | name_constraints: self |
493 | .name_constraints |
494 | .as_ref() |
495 | .map(|nc: &Der<'_>| nc.as_ref().to_owned().into()), |
496 | } |
497 | } |
498 | } |
499 | |
500 | /// A Certificate Revocation List; as specified in RFC 5280 |
501 | /// |
502 | /// Certificate revocation lists are identified in PEM context as `X509 CRL` and when stored in a |
503 | /// file usually use a `.crl` extension. For more on PEM files, refer to the crate documentation. |
504 | /// |
505 | /// ```rust |
506 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
507 | /// use rustls_pki_types::{CertificateRevocationListDer, pem::PemObject}; |
508 | /// |
509 | /// // load several from a PEM file |
510 | /// let crls: Vec<_> = CertificateRevocationListDer::pem_file_iter("tests/data/crl.pem" ) |
511 | /// .unwrap() |
512 | /// .collect(); |
513 | /// assert!(crls.len() >= 1); |
514 | /// |
515 | /// // or one from a PEM byte slice... |
516 | /// # let byte_slice = include_bytes!("../tests/data/crl.pem" ); |
517 | /// CertificateRevocationListDer::from_pem_slice(byte_slice).unwrap(); |
518 | /// |
519 | /// // or several from a PEM byte slice |
520 | /// let crls: Vec<_> = CertificateRevocationListDer::pem_slice_iter(byte_slice) |
521 | /// .collect(); |
522 | /// assert!(crls.len() >= 1); |
523 | /// # } |
524 | /// ``` |
525 | |
526 | #[derive (Clone, Debug, PartialEq, Eq)] |
527 | pub struct CertificateRevocationListDer<'a>(Der<'a>); |
528 | |
529 | #[cfg (feature = "alloc" )] |
530 | impl PemObjectFilter for CertificateRevocationListDer<'static> { |
531 | const KIND: SectionKind = SectionKind::Crl; |
532 | } |
533 | |
534 | impl AsRef<[u8]> for CertificateRevocationListDer<'_> { |
535 | fn as_ref(&self) -> &[u8] { |
536 | self.0.as_ref() |
537 | } |
538 | } |
539 | |
540 | impl Deref for CertificateRevocationListDer<'_> { |
541 | type Target = [u8]; |
542 | |
543 | fn deref(&self) -> &Self::Target { |
544 | self.as_ref() |
545 | } |
546 | } |
547 | |
548 | impl<'a> From<&'a [u8]> for CertificateRevocationListDer<'a> { |
549 | fn from(slice: &'a [u8]) -> Self { |
550 | Self(Der::from(slice)) |
551 | } |
552 | } |
553 | |
554 | #[cfg (feature = "alloc" )] |
555 | impl From<Vec<u8>> for CertificateRevocationListDer<'_> { |
556 | fn from(vec: Vec<u8>) -> Self { |
557 | Self(Der::from(vec)) |
558 | } |
559 | } |
560 | |
561 | /// A Certificate Signing Request; as specified in RFC 2986 |
562 | /// |
563 | /// Certificate signing requests are identified in PEM context as `CERTIFICATE REQUEST` and when stored in a |
564 | /// file usually use a `.csr` extension. For more on PEM files, refer to the crate documentation. |
565 | /// |
566 | /// ```rust |
567 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
568 | /// use rustls_pki_types::{CertificateSigningRequestDer, pem::PemObject}; |
569 | /// |
570 | /// // load from a PEM file |
571 | /// CertificateSigningRequestDer::from_pem_file("tests/data/csr.pem" ).unwrap(); |
572 | /// |
573 | /// // or from a PEM byte slice... |
574 | /// # let byte_slice = include_bytes!("../tests/data/csr.pem" ); |
575 | /// CertificateSigningRequestDer::from_pem_slice(byte_slice).unwrap(); |
576 | /// # } |
577 | /// ``` |
578 | #[derive (Clone, Debug, PartialEq, Eq)] |
579 | pub struct CertificateSigningRequestDer<'a>(Der<'a>); |
580 | |
581 | #[cfg (feature = "alloc" )] |
582 | impl PemObjectFilter for CertificateSigningRequestDer<'static> { |
583 | const KIND: SectionKind = SectionKind::Csr; |
584 | } |
585 | |
586 | impl AsRef<[u8]> for CertificateSigningRequestDer<'_> { |
587 | fn as_ref(&self) -> &[u8] { |
588 | self.0.as_ref() |
589 | } |
590 | } |
591 | |
592 | impl Deref for CertificateSigningRequestDer<'_> { |
593 | type Target = [u8]; |
594 | |
595 | fn deref(&self) -> &Self::Target { |
596 | self.as_ref() |
597 | } |
598 | } |
599 | |
600 | impl<'a> From<&'a [u8]> for CertificateSigningRequestDer<'a> { |
601 | fn from(slice: &'a [u8]) -> Self { |
602 | Self(Der::from(slice)) |
603 | } |
604 | } |
605 | |
606 | #[cfg (feature = "alloc" )] |
607 | impl From<Vec<u8>> for CertificateSigningRequestDer<'_> { |
608 | fn from(vec: Vec<u8>) -> Self { |
609 | Self(Der::from(vec)) |
610 | } |
611 | } |
612 | |
613 | /// A DER-encoded X.509 certificate; as specified in RFC 5280 |
614 | /// |
615 | /// Certificates are identified in PEM context as `CERTIFICATE` and when stored in a |
616 | /// file usually use a `.pem`, `.cer` or `.crt` extension. For more on PEM files, refer to the |
617 | /// crate documentation. |
618 | /// |
619 | /// ```rust |
620 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
621 | /// use rustls_pki_types::{CertificateDer, pem::PemObject}; |
622 | /// |
623 | /// // load several from a PEM file |
624 | /// let certs: Vec<_> = CertificateDer::pem_file_iter("tests/data/certificate.chain.pem" ) |
625 | /// .unwrap() |
626 | /// .collect(); |
627 | /// assert_eq!(certs.len(), 3); |
628 | /// |
629 | /// // or one from a PEM byte slice... |
630 | /// # let byte_slice = include_bytes!("../tests/data/certificate.chain.pem" ); |
631 | /// CertificateDer::from_pem_slice(byte_slice).unwrap(); |
632 | /// |
633 | /// // or several from a PEM byte slice |
634 | /// let certs: Vec<_> = CertificateDer::pem_slice_iter(byte_slice) |
635 | /// .collect(); |
636 | /// assert_eq!(certs.len(), 3); |
637 | /// # } |
638 | /// ``` |
639 | #[derive (Clone, Debug, PartialEq, Eq)] |
640 | pub struct CertificateDer<'a>(Der<'a>); |
641 | |
642 | impl<'a> CertificateDer<'a> { |
643 | /// A const constructor to create a `CertificateDer` from a slice of DER. |
644 | pub const fn from_slice(bytes: &'a [u8]) -> Self { |
645 | Self(Der::from_slice(der:bytes)) |
646 | } |
647 | } |
648 | |
649 | #[cfg (feature = "alloc" )] |
650 | impl PemObjectFilter for CertificateDer<'static> { |
651 | const KIND: SectionKind = SectionKind::Certificate; |
652 | } |
653 | |
654 | impl AsRef<[u8]> for CertificateDer<'_> { |
655 | fn as_ref(&self) -> &[u8] { |
656 | self.0.as_ref() |
657 | } |
658 | } |
659 | |
660 | impl Deref for CertificateDer<'_> { |
661 | type Target = [u8]; |
662 | |
663 | fn deref(&self) -> &Self::Target { |
664 | self.as_ref() |
665 | } |
666 | } |
667 | |
668 | impl<'a> From<&'a [u8]> for CertificateDer<'a> { |
669 | fn from(slice: &'a [u8]) -> Self { |
670 | Self(Der::from(slice)) |
671 | } |
672 | } |
673 | |
674 | #[cfg (feature = "alloc" )] |
675 | impl From<Vec<u8>> for CertificateDer<'_> { |
676 | fn from(vec: Vec<u8>) -> Self { |
677 | Self(Der::from(vec)) |
678 | } |
679 | } |
680 | |
681 | impl CertificateDer<'_> { |
682 | /// Converts this certificate into its owned variant, unfreezing borrowed content (if any) |
683 | #[cfg (feature = "alloc" )] |
684 | pub fn into_owned(self) -> CertificateDer<'static> { |
685 | CertificateDer(Der(self.0 .0.into_owned())) |
686 | } |
687 | } |
688 | |
689 | /// A DER-encoded SubjectPublicKeyInfo (SPKI), as specified in RFC 5280. |
690 | #[deprecated (since = "1.7.0" , note = "Prefer `SubjectPublicKeyInfoDer` instead" )] |
691 | pub type SubjectPublicKeyInfo<'a> = SubjectPublicKeyInfoDer<'a>; |
692 | |
693 | /// A DER-encoded SubjectPublicKeyInfo (SPKI), as specified in RFC 5280. |
694 | /// |
695 | /// Public keys are identified in PEM context as a `PUBLIC KEY`. |
696 | /// |
697 | /// ```rust |
698 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
699 | /// use rustls_pki_types::{SubjectPublicKeyInfoDer, pem::PemObject}; |
700 | /// |
701 | /// // load from a PEM file |
702 | /// SubjectPublicKeyInfoDer::from_pem_file("tests/data/spki.pem" ).unwrap(); |
703 | /// |
704 | /// // or from a PEM byte slice... |
705 | /// # let byte_slice = include_bytes!("../tests/data/spki.pem" ); |
706 | /// SubjectPublicKeyInfoDer::from_pem_slice(byte_slice).unwrap(); |
707 | /// # } |
708 | /// ``` |
709 | #[derive (Clone, Debug, PartialEq, Eq)] |
710 | pub struct SubjectPublicKeyInfoDer<'a>(Der<'a>); |
711 | |
712 | #[cfg (feature = "alloc" )] |
713 | impl PemObjectFilter for SubjectPublicKeyInfoDer<'static> { |
714 | const KIND: SectionKind = SectionKind::PublicKey; |
715 | } |
716 | |
717 | impl AsRef<[u8]> for SubjectPublicKeyInfoDer<'_> { |
718 | fn as_ref(&self) -> &[u8] { |
719 | self.0.as_ref() |
720 | } |
721 | } |
722 | |
723 | impl Deref for SubjectPublicKeyInfoDer<'_> { |
724 | type Target = [u8]; |
725 | |
726 | fn deref(&self) -> &Self::Target { |
727 | self.as_ref() |
728 | } |
729 | } |
730 | |
731 | impl<'a> From<&'a [u8]> for SubjectPublicKeyInfoDer<'a> { |
732 | fn from(slice: &'a [u8]) -> Self { |
733 | Self(Der::from(slice)) |
734 | } |
735 | } |
736 | |
737 | #[cfg (feature = "alloc" )] |
738 | impl From<Vec<u8>> for SubjectPublicKeyInfoDer<'_> { |
739 | fn from(vec: Vec<u8>) -> Self { |
740 | Self(Der::from(vec)) |
741 | } |
742 | } |
743 | |
744 | impl SubjectPublicKeyInfoDer<'_> { |
745 | /// Converts this SubjectPublicKeyInfo into its owned variant, unfreezing borrowed content (if any) |
746 | #[cfg (feature = "alloc" )] |
747 | pub fn into_owned(self) -> SubjectPublicKeyInfoDer<'static> { |
748 | SubjectPublicKeyInfoDer(Der(self.0 .0.into_owned())) |
749 | } |
750 | } |
751 | |
752 | /// A TLS-encoded Encrypted Client Hello (ECH) configuration list (`ECHConfigList`); as specified in |
753 | /// [draft-ietf-tls-esni-18 ยง4](https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-4) |
754 | #[derive (Clone, Eq, PartialEq)] |
755 | pub struct EchConfigListBytes<'a>(BytesInner<'a>); |
756 | |
757 | impl EchConfigListBytes<'_> { |
758 | /// Converts this config into its owned variant, unfreezing borrowed content (if any) |
759 | #[cfg (feature = "alloc" )] |
760 | pub fn into_owned(self) -> EchConfigListBytes<'static> { |
761 | EchConfigListBytes(self.0.into_owned()) |
762 | } |
763 | } |
764 | |
765 | #[cfg (feature = "alloc" )] |
766 | impl EchConfigListBytes<'static> { |
767 | /// Convert an iterator over PEM items into an `EchConfigListBytes` and private key. |
768 | /// |
769 | /// This handles the "ECHConfig file" format specified in |
770 | /// <https://www.ietf.org/archive/id/draft-farrell-tls-pemesni-05.html#name-echconfig-file> |
771 | /// |
772 | /// Use it like: |
773 | /// |
774 | /// ```rust |
775 | /// # #[cfg (all(feature = "alloc" , feature = "std" ))] { |
776 | /// # use rustls_pki_types::{EchConfigListBytes, pem::PemObject}; |
777 | /// let (config, key) = EchConfigListBytes::config_and_key_from_iter( |
778 | /// PemObject::pem_file_iter("tests/data/ech.pem" ).unwrap() |
779 | /// ).unwrap(); |
780 | /// # } |
781 | /// ``` |
782 | pub fn config_and_key_from_iter( |
783 | iter: impl Iterator<Item = Result<(SectionKind, Vec<u8>), pem::Error>>, |
784 | ) -> Result<(Self, PrivatePkcs8KeyDer<'static>), pem::Error> { |
785 | let mut key = None; |
786 | let mut config = None; |
787 | |
788 | for item in iter { |
789 | let (kind, data) = item?; |
790 | match kind { |
791 | SectionKind::PrivateKey => { |
792 | key = PrivatePkcs8KeyDer::from_pem(kind, data); |
793 | } |
794 | SectionKind::EchConfigList => { |
795 | config = Self::from_pem(kind, data); |
796 | } |
797 | _ => continue, |
798 | }; |
799 | |
800 | if let (Some(_key), Some(_config)) = (&key, &config) { |
801 | return Ok((config.take().unwrap(), key.take().unwrap())); |
802 | } |
803 | } |
804 | |
805 | Err(pem::Error::NoItemsFound) |
806 | } |
807 | } |
808 | |
809 | #[cfg (feature = "alloc" )] |
810 | impl PemObjectFilter for EchConfigListBytes<'static> { |
811 | const KIND: SectionKind = SectionKind::EchConfigList; |
812 | } |
813 | |
814 | impl fmt::Debug for EchConfigListBytes<'_> { |
815 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
816 | hex(f, self.as_ref()) |
817 | } |
818 | } |
819 | |
820 | impl AsRef<[u8]> for EchConfigListBytes<'_> { |
821 | fn as_ref(&self) -> &[u8] { |
822 | self.0.as_ref() |
823 | } |
824 | } |
825 | |
826 | impl Deref for EchConfigListBytes<'_> { |
827 | type Target = [u8]; |
828 | |
829 | fn deref(&self) -> &Self::Target { |
830 | self.as_ref() |
831 | } |
832 | } |
833 | |
834 | impl<'a> From<&'a [u8]> for EchConfigListBytes<'a> { |
835 | fn from(slice: &'a [u8]) -> Self { |
836 | Self(BytesInner::Borrowed(slice)) |
837 | } |
838 | } |
839 | |
840 | #[cfg (feature = "alloc" )] |
841 | impl From<Vec<u8>> for EchConfigListBytes<'_> { |
842 | fn from(vec: Vec<u8>) -> Self { |
843 | Self(BytesInner::Owned(vec)) |
844 | } |
845 | } |
846 | |
847 | /// An abstract signature verification algorithm. |
848 | /// |
849 | /// One of these is needed per supported pair of public key type (identified |
850 | /// with `public_key_alg_id()`) and `signatureAlgorithm` (identified with |
851 | /// `signature_alg_id()`). Note that both of these `AlgorithmIdentifier`s include |
852 | /// the parameters encoding, so separate `SignatureVerificationAlgorithm`s are needed |
853 | /// for each possible public key or signature parameters. |
854 | /// |
855 | /// Debug implementations should list the public key algorithm identifier and |
856 | /// signature algorithm identifier in human friendly form (i.e. not encoded bytes), |
857 | /// along with the name of the implementing library (to distinguish different |
858 | /// implementations of the same algorithms). |
859 | pub trait SignatureVerificationAlgorithm: Send + Sync + fmt::Debug { |
860 | /// Verify a signature. |
861 | /// |
862 | /// `public_key` is the `subjectPublicKey` value from a `SubjectPublicKeyInfo` encoding |
863 | /// and is untrusted. The key's `subjectPublicKeyInfo` matches the [`AlgorithmIdentifier`] |
864 | /// returned by `public_key_alg_id()`. |
865 | /// |
866 | /// `message` is the data over which the signature was allegedly computed. |
867 | /// It is not hashed; implementations of this trait function must do hashing |
868 | /// if that is required by the algorithm they implement. |
869 | /// |
870 | /// `signature` is the signature allegedly over `message`. |
871 | /// |
872 | /// Return `Ok(())` only if `signature` is a valid signature on `message`. |
873 | /// |
874 | /// Return `Err(InvalidSignature)` if the signature is invalid, including if the `public_key` |
875 | /// encoding is invalid. There is no need or opportunity to produce errors |
876 | /// that are more specific than this. |
877 | fn verify_signature( |
878 | &self, |
879 | public_key: &[u8], |
880 | message: &[u8], |
881 | signature: &[u8], |
882 | ) -> Result<(), InvalidSignature>; |
883 | |
884 | /// Return the `AlgorithmIdentifier` that must equal a public key's |
885 | /// `subjectPublicKeyInfo` value for this `SignatureVerificationAlgorithm` |
886 | /// to be used for signature verification. |
887 | fn public_key_alg_id(&self) -> AlgorithmIdentifier; |
888 | |
889 | /// Return the `AlgorithmIdentifier` that must equal the `signatureAlgorithm` value |
890 | /// on the data to be verified for this `SignatureVerificationAlgorithm` to be used |
891 | /// for signature verification. |
892 | fn signature_alg_id(&self) -> AlgorithmIdentifier; |
893 | |
894 | /// Return `true` if this is backed by a FIPS-approved implementation. |
895 | fn fips(&self) -> bool { |
896 | false |
897 | } |
898 | } |
899 | |
900 | /// A detail-less error when a signature is not valid. |
901 | #[derive (Debug, Copy, Clone)] |
902 | pub struct InvalidSignature; |
903 | |
904 | /// A timestamp, tracking the number of non-leap seconds since the Unix epoch. |
905 | /// |
906 | /// The Unix epoch is defined January 1, 1970 00:00:00 UTC. |
907 | #[derive (Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
908 | pub struct UnixTime(u64); |
909 | |
910 | impl UnixTime { |
911 | /// The current time, as a `UnixTime` |
912 | #[cfg (any( |
913 | all( |
914 | feature = "std" , |
915 | not(all(target_family = "wasm" , target_os = "unknown" )) |
916 | ), |
917 | all(target_family = "wasm" , target_os = "unknown" , feature = "web" ) |
918 | ))] |
919 | pub fn now() -> Self { |
920 | Self::since_unix_epoch( |
921 | SystemTime::now() |
922 | .duration_since(SystemTime::UNIX_EPOCH) |
923 | .unwrap(), // Safe: this code did not exist before 1970. |
924 | ) |
925 | } |
926 | |
927 | /// Convert a `Duration` since the start of 1970 to a `UnixTime` |
928 | /// |
929 | /// The `duration` must be relative to the Unix epoch. |
930 | pub fn since_unix_epoch(duration: Duration) -> Self { |
931 | Self(duration.as_secs()) |
932 | } |
933 | |
934 | /// Number of seconds since the Unix epoch |
935 | pub fn as_secs(&self) -> u64 { |
936 | self.0 |
937 | } |
938 | } |
939 | |
940 | /// DER-encoded data, either owned or borrowed |
941 | /// |
942 | /// This wrapper type is used to represent DER-encoded data in a way that is agnostic to whether |
943 | /// the data is owned (by a `Vec<u8>`) or borrowed (by a `&[u8]`). Support for the owned |
944 | /// variant is only available when the `alloc` feature is enabled. |
945 | #[derive (Clone, Eq, PartialEq)] |
946 | pub struct Der<'a>(BytesInner<'a>); |
947 | |
948 | impl<'a> Der<'a> { |
949 | /// A const constructor to create a `Der` from a borrowed slice |
950 | pub const fn from_slice(der: &'a [u8]) -> Self { |
951 | Self(BytesInner::Borrowed(der)) |
952 | } |
953 | } |
954 | |
955 | impl AsRef<[u8]> for Der<'_> { |
956 | fn as_ref(&self) -> &[u8] { |
957 | self.0.as_ref() |
958 | } |
959 | } |
960 | |
961 | impl Deref for Der<'_> { |
962 | type Target = [u8]; |
963 | |
964 | fn deref(&self) -> &Self::Target { |
965 | self.as_ref() |
966 | } |
967 | } |
968 | |
969 | impl<'a> From<&'a [u8]> for Der<'a> { |
970 | fn from(slice: &'a [u8]) -> Self { |
971 | Self(BytesInner::Borrowed(slice)) |
972 | } |
973 | } |
974 | |
975 | #[cfg (feature = "alloc" )] |
976 | impl From<Vec<u8>> for Der<'static> { |
977 | fn from(vec: Vec<u8>) -> Self { |
978 | Self(BytesInner::Owned(vec)) |
979 | } |
980 | } |
981 | |
982 | impl fmt::Debug for Der<'_> { |
983 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
984 | hex(f, self.as_ref()) |
985 | } |
986 | } |
987 | |
988 | #[derive (Debug, Clone)] |
989 | enum BytesInner<'a> { |
990 | #[cfg (feature = "alloc" )] |
991 | Owned(Vec<u8>), |
992 | Borrowed(&'a [u8]), |
993 | } |
994 | |
995 | #[cfg (feature = "alloc" )] |
996 | impl BytesInner<'_> { |
997 | fn into_owned(self) -> BytesInner<'static> { |
998 | BytesInner::Owned(match self { |
999 | Self::Owned(vec: Vec) => vec, |
1000 | Self::Borrowed(slice: &[u8]) => slice.to_vec(), |
1001 | }) |
1002 | } |
1003 | } |
1004 | |
1005 | impl AsRef<[u8]> for BytesInner<'_> { |
1006 | fn as_ref(&self) -> &[u8] { |
1007 | match &self { |
1008 | #[cfg (feature = "alloc" )] |
1009 | BytesInner::Owned(vec: &Vec) => vec.as_ref(), |
1010 | BytesInner::Borrowed(slice: &&[u8]) => slice, |
1011 | } |
1012 | } |
1013 | } |
1014 | |
1015 | impl PartialEq for BytesInner<'_> { |
1016 | fn eq(&self, other: &Self) -> bool { |
1017 | self.as_ref() == other.as_ref() |
1018 | } |
1019 | } |
1020 | |
1021 | impl Eq for BytesInner<'_> {} |
1022 | |
1023 | // Format an iterator of u8 into a hex string |
1024 | fn hex<'a>(f: &mut fmt::Formatter<'_>, payload: impl IntoIterator<Item = &'a u8>) -> fmt::Result { |
1025 | for (i: usize, b: &'a u8) in payload.into_iter().enumerate() { |
1026 | if i == 0 { |
1027 | write!(f, "0x" )?; |
1028 | } |
1029 | write!(f, " {:02x}" , b)?; |
1030 | } |
1031 | Ok(()) |
1032 | } |
1033 | |
1034 | #[cfg (all(test, feature = "std" ))] |
1035 | mod tests { |
1036 | use super::*; |
1037 | |
1038 | #[test ] |
1039 | fn der_debug() { |
1040 | let der = Der::from_slice(&[0x01, 0x02, 0x03]); |
1041 | assert_eq!(format!("{:?}" , der), "0x010203" ); |
1042 | } |
1043 | |
1044 | #[test ] |
1045 | fn alg_id_debug() { |
1046 | let alg_id = AlgorithmIdentifier::from_slice(&[0x01, 0x02, 0x03]); |
1047 | assert_eq!(format!("{:?}" , alg_id), "0x010203" ); |
1048 | } |
1049 | |
1050 | #[test ] |
1051 | fn bytes_inner_equality() { |
1052 | let owned_a = BytesInner::Owned(vec![1, 2, 3]); |
1053 | let owned_b = BytesInner::Owned(vec![4, 5]); |
1054 | let borrowed_a = BytesInner::Borrowed(&[1, 2, 3]); |
1055 | let borrowed_b = BytesInner::Borrowed(&[99]); |
1056 | |
1057 | // Self-equality. |
1058 | assert_eq!(owned_a, owned_a); |
1059 | assert_eq!(owned_b, owned_b); |
1060 | assert_eq!(borrowed_a, borrowed_a); |
1061 | assert_eq!(borrowed_b, borrowed_b); |
1062 | |
1063 | // Borrowed vs Owned equality |
1064 | assert_eq!(owned_a, borrowed_a); |
1065 | assert_eq!(borrowed_a, owned_a); |
1066 | |
1067 | // Owned inequality |
1068 | assert_ne!(owned_a, owned_b); |
1069 | assert_ne!(owned_b, owned_a); |
1070 | |
1071 | // Borrowed inequality |
1072 | assert_ne!(borrowed_a, borrowed_b); |
1073 | assert_ne!(borrowed_b, borrowed_a); |
1074 | |
1075 | // Borrowed vs Owned inequality |
1076 | assert_ne!(owned_a, borrowed_b); |
1077 | assert_ne!(borrowed_b, owned_a); |
1078 | } |
1079 | } |
1080 | |