pem_rfc7468/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8#![deny(unsafe_code)]
9#![warn(
10 clippy::integer_arithmetic,
11 clippy::panic,
12 clippy::panic_in_result_fn,
13 clippy::unwrap_used,
14 missing_docs,
15 rust_2018_idioms,
16 unused_qualifications
17)]
18
19//! # Usage
20//!
21#![cfg_attr(feature = "std", doc = " ```")]
22#![cfg_attr(not(feature = "std"), doc = " ```ignore")]
23//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
24//! /// Example PEM document
25//! /// NOTE: do not actually put private key literals into your source code!!!
26//! let example_pem = "\
27//! -----BEGIN PRIVATE KEY-----
28//! MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF
29//! -----END PRIVATE KEY-----
30//! ";
31//!
32//! // Decode PEM
33//! let (type_label, data) = pem_rfc7468::decode_vec(example_pem.as_bytes())?;
34//! assert_eq!(type_label, "PRIVATE KEY");
35//! assert_eq!(
36//! data,
37//! &[
38//! 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 23, 237, 156, 115, 233, 219,
39//! 100, 158, 193, 137, 166, 18, 131, 28, 95, 197, 112, 35, 130, 7, 193, 170, 157, 251,
40//! 210, 197, 62, 63, 245, 229, 234, 133
41//! ]
42//! );
43//!
44//! // Encode PEM
45//! use pem_rfc7468::LineEnding;
46//! let encoded_pem = pem_rfc7468::encode_string(type_label, LineEnding::default(), &data)?;
47//! assert_eq!(&encoded_pem, example_pem);
48//! # Ok(())
49//! # }
50//! ```
51
52#[cfg(feature = "alloc")]
53#[macro_use]
54extern crate alloc;
55#[cfg(feature = "std")]
56extern crate std;
57
58mod decoder;
59mod encoder;
60mod error;
61mod grammar;
62
63pub use crate::{
64 decoder::{decode, decode_label, Decoder},
65 encoder::{encapsulated_len, encapsulated_len_wrapped, encode, encoded_len, Encoder},
66 error::{Error, Result},
67};
68pub use base64ct::LineEnding;
69
70#[cfg(feature = "alloc")]
71pub use crate::{decoder::decode_vec, encoder::encode_string};
72
73/// The pre-encapsulation boundary appears before the encapsulated text.
74///
75/// From RFC 7468 Section 2:
76/// > There are exactly five hyphen-minus (also known as dash) characters ("-")
77/// > on both ends of the encapsulation boundaries, no more, no less.
78const PRE_ENCAPSULATION_BOUNDARY: &[u8] = b"-----BEGIN ";
79
80/// The post-encapsulation boundary appears immediately after the encapsulated text.
81const POST_ENCAPSULATION_BOUNDARY: &[u8] = b"-----END ";
82
83/// Delimiter of encapsulation boundaries.
84const ENCAPSULATION_BOUNDARY_DELIMITER: &[u8] = b"-----";
85
86/// Width at which the Base64 body of RFC7468-compliant PEM is wrapped.
87///
88/// From [RFC7468 § 2]:
89///
90/// > Generators MUST wrap the base64-encoded lines so that each line
91/// > consists of exactly 64 characters except for the final line, which
92/// > will encode the remainder of the data (within the 64-character line
93/// > boundary), and they MUST NOT emit extraneous whitespace. Parsers MAY
94/// > handle other line sizes.
95///
96/// [RFC7468 § 2]: https://datatracker.ietf.org/doc/html/rfc7468#section-2
97pub const BASE64_WRAP_WIDTH: usize = 64;
98
99/// Buffered Base64 decoder type.
100pub type Base64Decoder<'i> = base64ct::Decoder<'i, base64ct::Base64>;
101
102/// Buffered Base64 encoder type.
103pub type Base64Encoder<'o> = base64ct::Encoder<'o, base64ct::Base64>;
104
105/// Marker trait for types with an associated PEM type label.
106pub trait PemLabel {
107 /// Expected PEM type label for a given document, e.g. `"PRIVATE KEY"`
108 const PEM_LABEL: &'static str;
109
110 /// Validate that a given label matches the expected label.
111 fn validate_pem_label(actual: &str) -> Result<()> {
112 if Self::PEM_LABEL == actual {
113 Ok(())
114 } else {
115 Err(Error::UnexpectedTypeLabel {
116 expected: Self::PEM_LABEL,
117 })
118 }
119 }
120}