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//! The [rustls-pemfile](https://docs.rs/rustls-pemfile) crate can be used to parse PEM files.
23//!
24//! ## Creating new certificates and keys
25//!
26//! This crate does not provide any functionality for creating new certificates or keys. However,
27//! the [rcgen](https://docs.rs/rcgen) crate can be used to create new certificates and keys.
28//!
29//! ## Cloning private keys
30//!
31//! This crate intentionally **does not** implement `Clone` on private key types in
32//! order to minimize the exposure of private key data in memory.
33//!
34//! If you want to extend the lifetime of a `PrivateKeyDer<'_>`, consider [`PrivateKeyDer::clone_key()`].
35//! Alternatively since these types are immutable, consider wrapping the `PrivateKeyDer<'_>` in a [`Rc`]
36//! or an [`Arc`].
37//!
38//! [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html
39//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
40//! [`PrivateKeyDer::clone_key()`]: https://docs.rs/rustls-pki-types/latest/rustls_pki_types/enum.PrivateKeyDer.html#method.clone_key
41
42#![cfg_attr(not(feature = "std"), no_std)]
43#![warn(unreachable_pub, clippy::use_self)]
44#![deny(missing_docs)]
45#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
46
47#[cfg(feature = "alloc")]
48extern crate alloc;
49
50#[cfg(feature = "alloc")]
51use alloc::vec::Vec;
52use core::fmt;
53use core::ops::Deref;
54use core::time::Duration;
55#[cfg(feature = "std")]
56use std::time::SystemTime;
57
58mod server_name;
59pub use server_name::{
60 AddrParseError, DnsName, InvalidDnsNameError, IpAddr, Ipv4Addr, Ipv6Addr, ServerName,
61};
62
63/// A DER-encoded X.509 private key, in one of several formats
64///
65/// See variant inner types for more detailed information.
66#[non_exhaustive]
67#[derive(Debug, PartialEq, Eq)]
68pub enum PrivateKeyDer<'a> {
69 /// An RSA private key
70 Pkcs1(PrivatePkcs1KeyDer<'a>),
71 /// A Sec1 private key
72 Sec1(PrivateSec1KeyDer<'a>),
73 /// A PKCS#8 private key
74 Pkcs8(PrivatePkcs8KeyDer<'a>),
75}
76
77impl<'a> PrivateKeyDer<'a> {
78 /// Clone the private key to a `'static` value
79 #[cfg(feature = "alloc")]
80 pub fn clone_key(&self) -> PrivateKeyDer<'static> {
81 use PrivateKeyDer::*;
82 match self {
83 Pkcs1(key: &PrivatePkcs1KeyDer<'_>) => Pkcs1(key.clone_key()),
84 Sec1(key: &PrivateSec1KeyDer<'_>) => Sec1(key.clone_key()),
85 Pkcs8(key: &PrivatePkcs8KeyDer<'_>) => Pkcs8(key.clone_key()),
86 }
87 }
88
89 /// Yield the DER-encoded bytes of the private key
90 pub fn secret_der(&self) -> &[u8] {
91 match self {
92 PrivateKeyDer::Pkcs1(key: &PrivatePkcs1KeyDer<'_>) => key.secret_pkcs1_der(),
93 PrivateKeyDer::Sec1(key: &PrivateSec1KeyDer<'_>) => key.secret_sec1_der(),
94 PrivateKeyDer::Pkcs8(key: &PrivatePkcs8KeyDer<'_>) => key.secret_pkcs8_der(),
95 }
96 }
97}
98
99impl<'a> From<PrivatePkcs1KeyDer<'a>> for PrivateKeyDer<'a> {
100 fn from(key: PrivatePkcs1KeyDer<'a>) -> Self {
101 Self::Pkcs1(key)
102 }
103}
104
105impl<'a> From<PrivateSec1KeyDer<'a>> for PrivateKeyDer<'a> {
106 fn from(key: PrivateSec1KeyDer<'a>) -> Self {
107 Self::Sec1(key)
108 }
109}
110
111impl<'a> From<PrivatePkcs8KeyDer<'a>> for PrivateKeyDer<'a> {
112 fn from(key: PrivatePkcs8KeyDer<'a>) -> Self {
113 Self::Pkcs8(key)
114 }
115}
116
117/// A DER-encoded plaintext RSA private key; as specified in PKCS#1/RFC 3447
118///
119/// RSA private keys are identified in PEM context as `RSA PRIVATE KEY` and when stored in a
120/// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate
121/// documentation.
122#[derive(PartialEq, Eq)]
123pub struct PrivatePkcs1KeyDer<'a>(Der<'a>);
124
125impl PrivatePkcs1KeyDer<'_> {
126 /// Clone the private key to a `'static` value
127 #[cfg(feature = "alloc")]
128 pub fn clone_key(&self) -> PrivatePkcs1KeyDer<'static> {
129 PrivatePkcs1KeyDer::from(self.0.as_ref().to_vec())
130 }
131
132 /// Yield the DER-encoded bytes of the private key
133 pub fn secret_pkcs1_der(&self) -> &[u8] {
134 self.0.as_ref()
135 }
136}
137
138impl<'a> From<&'a [u8]> for PrivatePkcs1KeyDer<'a> {
139 fn from(slice: &'a [u8]) -> Self {
140 Self(Der(DerInner::Borrowed(slice)))
141 }
142}
143
144#[cfg(feature = "alloc")]
145impl<'a> From<Vec<u8>> for PrivatePkcs1KeyDer<'a> {
146 fn from(vec: Vec<u8>) -> Self {
147 Self(Der(DerInner::Owned(vec)))
148 }
149}
150
151impl fmt::Debug for PrivatePkcs1KeyDer<'_> {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivatePkcs1KeyDer")
154 .field(&"[secret key elided]")
155 .finish()
156 }
157}
158
159/// A Sec1-encoded plaintext private key; as specified in RFC 5915
160///
161/// Sec1 private keys are identified in PEM context as `EC PRIVATE KEY` and when stored in a
162/// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate
163/// documentation.
164#[derive(PartialEq, Eq)]
165pub struct PrivateSec1KeyDer<'a>(Der<'a>);
166
167impl PrivateSec1KeyDer<'_> {
168 /// Clone the private key to a `'static` value
169 #[cfg(feature = "alloc")]
170 pub fn clone_key(&self) -> PrivateSec1KeyDer<'static> {
171 PrivateSec1KeyDer::from(self.0.as_ref().to_vec())
172 }
173
174 /// Yield the DER-encoded bytes of the private key
175 pub fn secret_sec1_der(&self) -> &[u8] {
176 self.0.as_ref()
177 }
178}
179
180impl<'a> From<&'a [u8]> for PrivateSec1KeyDer<'a> {
181 fn from(slice: &'a [u8]) -> Self {
182 Self(Der(DerInner::Borrowed(slice)))
183 }
184}
185
186#[cfg(feature = "alloc")]
187impl<'a> From<Vec<u8>> for PrivateSec1KeyDer<'a> {
188 fn from(vec: Vec<u8>) -> Self {
189 Self(Der(DerInner::Owned(vec)))
190 }
191}
192
193impl fmt::Debug for PrivateSec1KeyDer<'_> {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivateSec1KeyDer")
196 .field(&"[secret key elided]")
197 .finish()
198 }
199}
200
201/// A DER-encoded plaintext private key; as specified in PKCS#8/RFC 5958
202///
203/// PKCS#8 private keys are identified in PEM context as `PRIVATE KEY` and when stored in a
204/// file usually use a `.pem` or `.key` extension. For more on PEM files, refer to the crate
205/// documentation.
206#[derive(PartialEq, Eq)]
207pub struct PrivatePkcs8KeyDer<'a>(Der<'a>);
208
209impl PrivatePkcs8KeyDer<'_> {
210 /// Clone the private key to a `'static` value
211 #[cfg(feature = "alloc")]
212 pub fn clone_key(&self) -> PrivatePkcs8KeyDer<'static> {
213 PrivatePkcs8KeyDer::from(self.0.as_ref().to_vec())
214 }
215
216 /// Yield the DER-encoded bytes of the private key
217 pub fn secret_pkcs8_der(&self) -> &[u8] {
218 self.0.as_ref()
219 }
220}
221
222impl<'a> From<&'a [u8]> for PrivatePkcs8KeyDer<'a> {
223 fn from(slice: &'a [u8]) -> Self {
224 Self(Der(DerInner::Borrowed(slice)))
225 }
226}
227
228#[cfg(feature = "alloc")]
229impl<'a> From<Vec<u8>> for PrivatePkcs8KeyDer<'a> {
230 fn from(vec: Vec<u8>) -> Self {
231 Self(Der(DerInner::Owned(vec)))
232 }
233}
234
235impl fmt::Debug for PrivatePkcs8KeyDer<'_> {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f&mut DebugTuple<'_, '_>.debug_tuple(name:"PrivatePkcs8KeyDer")
238 .field(&"[secret key elided]")
239 .finish()
240 }
241}
242
243/// A trust anchor (a.k.a. root CA)
244///
245/// Traditionally, certificate verification libraries have represented trust anchors as full X.509
246/// root certificates. However, those certificates contain a lot more data than is needed for
247/// verifying certificates. The [`TrustAnchor`] representation allows an application to store
248/// just the essential elements of trust anchors.
249///
250/// The most common way to get one of these is to call [`rustls_webpki::anchor_from_trusted_cert()`].
251///
252/// [`rustls_webpki::anchor_from_trusted_cert()`]: https://docs.rs/rustls-webpki/latest/webpki/fn.anchor_from_trusted_cert.html
253#[derive(Clone, Debug, PartialEq, Eq)]
254pub struct TrustAnchor<'a> {
255 /// Value of the `subject` field of the trust anchor
256 pub subject: Der<'a>,
257 /// Value of the `subjectPublicKeyInfo` field of the trust anchor
258 pub subject_public_key_info: Der<'a>,
259 /// Value of DER-encoded `NameConstraints`, containing name constraints to the trust anchor, if any
260 pub name_constraints: Option<Der<'a>>,
261}
262
263impl TrustAnchor<'_> {
264 /// Yield a `'static` lifetime of the `TrustAnchor` by allocating owned `Der` variants
265 #[cfg(feature = "alloc")]
266 pub fn to_owned(&self) -> TrustAnchor<'static> {
267 #[cfg(not(feature = "std"))]
268 use alloc::borrow::ToOwned;
269 TrustAnchor {
270 subject: self.subject.as_ref().to_owned().into(),
271 subject_public_key_info: self.subject_public_key_info.as_ref().to_owned().into(),
272 name_constraints: self
273 .name_constraints
274 .as_ref()
275 .map(|nc: &Der<'_>| nc.as_ref().to_owned().into()),
276 }
277 }
278}
279
280/// A Certificate Revocation List; as specified in RFC 5280
281///
282/// Certificate revocation lists are identified in PEM context as `X509 CRL` and when stored in a
283/// file usually use a `.crl` extension. For more on PEM files, refer to the crate documentation.
284#[derive(Clone, Debug, PartialEq, Eq)]
285pub struct CertificateRevocationListDer<'a>(Der<'a>);
286
287impl AsRef<[u8]> for CertificateRevocationListDer<'_> {
288 fn as_ref(&self) -> &[u8] {
289 self.0.as_ref()
290 }
291}
292
293impl Deref for CertificateRevocationListDer<'_> {
294 type Target = [u8];
295
296 fn deref(&self) -> &Self::Target {
297 self.as_ref()
298 }
299}
300
301impl<'a> From<&'a [u8]> for CertificateRevocationListDer<'a> {
302 fn from(slice: &'a [u8]) -> Self {
303 Self(Der::from(slice))
304 }
305}
306
307#[cfg(feature = "alloc")]
308impl<'a> From<Vec<u8>> for CertificateRevocationListDer<'a> {
309 fn from(vec: Vec<u8>) -> Self {
310 Self(Der::from(vec))
311 }
312}
313
314/// A Certificate Signing Request; as specified in RFC 2986
315///
316/// Certificate signing requests are identified in PEM context as `CERTIFICATE REQUEST` and when stored in a
317/// file usually use a `.csr` extension. For more on PEM files, refer to the crate documentation.
318#[derive(Clone, Debug, PartialEq, Eq)]
319pub struct CertificateSigningRequestDer<'a>(Der<'a>);
320
321impl AsRef<[u8]> for CertificateSigningRequestDer<'_> {
322 fn as_ref(&self) -> &[u8] {
323 self.0.as_ref()
324 }
325}
326
327impl Deref for CertificateSigningRequestDer<'_> {
328 type Target = [u8];
329
330 fn deref(&self) -> &Self::Target {
331 self.as_ref()
332 }
333}
334
335impl<'a> From<&'a [u8]> for CertificateSigningRequestDer<'a> {
336 fn from(slice: &'a [u8]) -> Self {
337 Self(Der::from(slice))
338 }
339}
340
341#[cfg(feature = "alloc")]
342impl<'a> From<Vec<u8>> for CertificateSigningRequestDer<'a> {
343 fn from(vec: Vec<u8>) -> Self {
344 Self(Der::from(vec))
345 }
346}
347
348/// A DER-encoded X.509 certificate; as specified in RFC 5280
349///
350/// Certificates are identified in PEM context as `CERTIFICATE` and when stored in a
351/// file usually use a `.pem`, `.cer` or `.crt` extension. For more on PEM files, refer to the
352/// crate documentation.
353#[derive(Clone, Debug, PartialEq, Eq)]
354pub struct CertificateDer<'a>(Der<'a>);
355
356impl AsRef<[u8]> for CertificateDer<'_> {
357 fn as_ref(&self) -> &[u8] {
358 self.0.as_ref()
359 }
360}
361
362impl Deref for CertificateDer<'_> {
363 type Target = [u8];
364
365 fn deref(&self) -> &Self::Target {
366 self.as_ref()
367 }
368}
369
370impl<'a> From<&'a [u8]> for CertificateDer<'a> {
371 fn from(slice: &'a [u8]) -> Self {
372 Self(Der::from(slice))
373 }
374}
375
376#[cfg(feature = "alloc")]
377impl<'a> From<Vec<u8>> for CertificateDer<'a> {
378 fn from(vec: Vec<u8>) -> Self {
379 Self(Der::from(vec))
380 }
381}
382
383impl CertificateDer<'_> {
384 /// Converts this certificate into its owned variant, unfreezing borrowed content (if any)
385 #[cfg(feature = "alloc")]
386 pub fn into_owned(self) -> CertificateDer<'static> {
387 CertificateDer(Der(self.0 .0.into_owned()))
388 }
389}
390
391/// An abstract signature verification algorithm.
392///
393/// One of these is needed per supported pair of public key type (identified
394/// with `public_key_alg_id()`) and `signatureAlgorithm` (identified with
395/// `signature_alg_id()`). Note that both of these `AlgorithmIdentifier`s include
396/// the parameters encoding, so separate `SignatureVerificationAlgorithm`s are needed
397/// for each possible public key or signature parameters.
398///
399/// Debug implementations should list the public key algorithm identifier and
400/// signature algorithm identifier in human friendly form (i.e. not encoded bytes),
401/// along with the name of the implementing library (to distinguish different
402/// implementations of the same algorithms).
403pub trait SignatureVerificationAlgorithm: Send + Sync + fmt::Debug {
404 /// Verify a signature.
405 ///
406 /// `public_key` is the `subjectPublicKey` value from a `SubjectPublicKeyInfo` encoding
407 /// and is untrusted. The key's `subjectPublicKeyInfo` matches the [`AlgorithmIdentifier`]
408 /// returned by `public_key_alg_id()`.
409 ///
410 /// `message` is the data over which the signature was allegedly computed.
411 /// It is not hashed; implementations of this trait function must do hashing
412 /// if that is required by the algorithm they implement.
413 ///
414 /// `signature` is the signature allegedly over `message`.
415 ///
416 /// Return `Ok(())` only if `signature` is a valid signature on `message`.
417 ///
418 /// Return `Err(InvalidSignature)` if the signature is invalid, including if the `public_key`
419 /// encoding is invalid. There is no need or opportunity to produce errors
420 /// that are more specific than this.
421 fn verify_signature(
422 &self,
423 public_key: &[u8],
424 message: &[u8],
425 signature: &[u8],
426 ) -> Result<(), InvalidSignature>;
427
428 /// Return the `AlgorithmIdentifier` that must equal a public key's
429 /// `subjectPublicKeyInfo` value for this `SignatureVerificationAlgorithm`
430 /// to be used for signature verification.
431 fn public_key_alg_id(&self) -> AlgorithmIdentifier;
432
433 /// Return the `AlgorithmIdentifier` that must equal the `signatureAlgorithm` value
434 /// on the data to be verified for this `SignatureVerificationAlgorithm` to be used
435 /// for signature verification.
436 fn signature_alg_id(&self) -> AlgorithmIdentifier;
437
438 /// Return `true` if this is backed by a FIPS-approved implementation.
439 fn fips(&self) -> bool {
440 false
441 }
442}
443
444/// A detail-less error when a signature is not valid.
445#[derive(Debug, Copy, Clone)]
446pub struct InvalidSignature;
447
448/// A DER encoding of the PKIX AlgorithmIdentifier type:
449///
450/// ```ASN.1
451/// AlgorithmIdentifier ::= SEQUENCE {
452/// algorithm OBJECT IDENTIFIER,
453/// parameters ANY DEFINED BY algorithm OPTIONAL }
454/// -- contains a value of the type
455/// -- registered for use with the
456/// -- algorithm object identifier value
457/// ```
458/// (from <https://www.rfc-editor.org/rfc/rfc5280#section-4.1.1.2>)
459///
460/// The outer sequence encoding is *not included*, so this is the DER encoding
461/// of an OID for `algorithm` plus the `parameters` value.
462///
463/// For example, this is the `rsaEncryption` algorithm:
464///
465/// ```
466/// let rsa_encryption = rustls_pki_types::AlgorithmIdentifier::from_slice(
467/// &[
468/// // algorithm: 1.2.840.113549.1.1.1
469/// 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
470/// // parameters: NULL
471/// 0x05, 0x00
472/// ]
473/// );
474/// ```
475#[derive(Clone, Copy, PartialEq, Eq)]
476pub struct AlgorithmIdentifier(&'static [u8]);
477
478impl AlgorithmIdentifier {
479 /// Makes a new `AlgorithmIdentifier` from a static octet slice.
480 ///
481 /// This does not validate the contents of the slice.
482 pub const fn from_slice(bytes: &'static [u8]) -> Self {
483 Self(bytes)
484 }
485}
486
487impl AsRef<[u8]> for AlgorithmIdentifier {
488 fn as_ref(&self) -> &[u8] {
489 self.0
490 }
491}
492
493impl fmt::Debug for AlgorithmIdentifier {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 hex(f, self.0)
496 }
497}
498
499impl Deref for AlgorithmIdentifier {
500 type Target = [u8];
501
502 fn deref(&self) -> &Self::Target {
503 self.as_ref()
504 }
505}
506
507/// A timestamp, tracking the number of non-leap seconds since the Unix epoch.
508///
509/// The Unix epoch is defined January 1, 1970 00:00:00 UTC.
510#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
511pub struct UnixTime(u64);
512
513impl UnixTime {
514 /// The current time, as a `UnixTime`
515 #[cfg(feature = "std")]
516 pub fn now() -> Self {
517 Self::since_unix_epoch(
518 duration:SystemTime::now()
519 .duration_since(earlier:SystemTime::UNIX_EPOCH)
520 .unwrap(), // Safe: this code did not exist before 1970.
521 )
522 }
523
524 /// Convert a `Duration` since the start of 1970 to a `UnixTime`
525 ///
526 /// The `duration` must be relative to the Unix epoch.
527 pub fn since_unix_epoch(duration: Duration) -> Self {
528 Self(duration.as_secs())
529 }
530
531 /// Number of seconds since the Unix epoch
532 pub fn as_secs(&self) -> u64 {
533 self.0
534 }
535}
536
537/// DER-encoded data, either owned or borrowed
538///
539/// This wrapper type is used to represent DER-encoded data in a way that is agnostic to whether
540/// the data is owned (by a `Vec<u8>`) or borrowed (by a `&[u8]`). Support for the owned
541/// variant is only available when the `alloc` feature is enabled.
542#[derive(Clone)]
543pub struct Der<'a>(DerInner<'a>);
544
545impl<'a> Der<'a> {
546 /// A const constructor to create a `Der` from a borrowed slice
547 pub const fn from_slice(der: &'a [u8]) -> Self {
548 Self(DerInner::Borrowed(der))
549 }
550}
551
552impl AsRef<[u8]> for Der<'_> {
553 fn as_ref(&self) -> &[u8] {
554 match &self.0 {
555 #[cfg(feature = "alloc")]
556 DerInner::Owned(vec: &Vec) => vec.as_ref(),
557 DerInner::Borrowed(slice: &&[u8]) => slice,
558 }
559 }
560}
561
562impl Deref for Der<'_> {
563 type Target = [u8];
564
565 fn deref(&self) -> &Self::Target {
566 self.as_ref()
567 }
568}
569
570impl<'a> From<&'a [u8]> for Der<'a> {
571 fn from(slice: &'a [u8]) -> Self {
572 Self(DerInner::Borrowed(slice))
573 }
574}
575
576#[cfg(feature = "alloc")]
577impl From<Vec<u8>> for Der<'static> {
578 fn from(vec: Vec<u8>) -> Self {
579 Self(DerInner::Owned(vec))
580 }
581}
582
583impl fmt::Debug for Der<'_> {
584 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
585 hex(f, self.as_ref())
586 }
587}
588
589impl PartialEq for Der<'_> {
590 fn eq(&self, other: &Self) -> bool {
591 self.as_ref().eq(other.as_ref())
592 }
593}
594
595impl Eq for Der<'_> {}
596
597#[derive(Clone)]
598enum DerInner<'a> {
599 #[cfg(feature = "alloc")]
600 Owned(Vec<u8>),
601 Borrowed(&'a [u8]),
602}
603
604#[cfg(feature = "alloc")]
605impl DerInner<'_> {
606 fn into_owned(self) -> DerInner<'static> {
607 DerInner::Owned(match self {
608 Self::Owned(vec: Vec) => vec,
609 Self::Borrowed(slice: &[u8]) => slice.to_vec(),
610 })
611 }
612}
613
614// Format an iterator of u8 into a hex string
615fn hex<'a>(f: &mut fmt::Formatter<'_>, payload: impl IntoIterator<Item = &'a u8>) -> fmt::Result {
616 for (i: usize, b: &u8) in payload.into_iter().enumerate() {
617 if i == 0 {
618 write!(f, "0x")?;
619 }
620 write!(f, "{:02x}", b)?;
621 }
622 Ok(())
623}
624
625#[cfg(all(test, feature = "std"))]
626mod tests {
627 use super::*;
628
629 #[test]
630 fn der_debug() {
631 let der = Der::from_slice(&[0x01, 0x02, 0x03]);
632 assert_eq!(format!("{:?}", der), "0x010203");
633 }
634
635 #[test]
636 fn alg_id_debug() {
637 let alg_id = AlgorithmIdentifier::from_slice(&[0x01, 0x02, 0x03]);
638 assert_eq!(format!("{:?}", alg_id), "0x010203");
639 }
640}
641