diff options
Diffstat (limited to 'vendor/pem-rfc7468')
23 files changed, 1810 insertions, 0 deletions
diff --git a/vendor/pem-rfc7468/.cargo-checksum.json b/vendor/pem-rfc7468/.cargo-checksum.json new file mode 100644 index 0000000..91578e8 --- /dev/null +++ b/vendor/pem-rfc7468/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{},"package":"88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"}
\ No newline at end of file diff --git a/vendor/pem-rfc7468/CHANGELOG.md b/vendor/pem-rfc7468/CHANGELOG.md new file mode 100644 index 0000000..e25bb26 --- /dev/null +++ b/vendor/pem-rfc7468/CHANGELOG.md @@ -0,0 +1,115 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.7.0 (2023-02-26) +### Changed +- MSRV 1.60 ([#802]) +- Lint improvements ([#824]) + +[#802]: https://github.com/RustCrypto/formats/pull/802 +[#824]: https://github.com/RustCrypto/formats/pull/824 + +## 0.6.0 (2022-04-26) +### Added +- `encapsulated_len_wrapped` ([#619]) + +### Changed +- `encapsulated_len` now accepts the length of the raw input bytes prior to + Base64 encoding, and computes the length of the full PEM encoded document + including newlines when the resulting Base64 is linewrapped ([#619]) + +[#619]: https://github.com/RustCrypto/formats/pull/619 + +## 0.5.1 (2022-03-30) +### Changed +- Rename `PemLabel::TYPE_LABEL` => `::PEM_LABEL` ([#568]) + +[#568]: https://github.com/RustCrypto/formats/pull/568 + +## 0.5.0 (2022-03-29) [YANKED] +### Added +- Clippy lints for checked arithmetic and panics ([#564]) + +### Changed +- Use `str::from_utf8_unchecked` in `encode` ([#565]) + +[#564]: https://github.com/RustCrypto/formats/pull/564 +[#565]: https://github.com/RustCrypto/formats/pull/565 + +## 0.4.0 (2022-03-12) +### Added +- Buffered `Decoder` type ([#406]) +- Buffered `Encoder` type ([#463], [#474]) + +### Changed +- Return `str` from `encode` ([#482]) + +[#406]: https://github.com/RustCrypto/formats/pull/406 +[#463]: https://github.com/RustCrypto/formats/pull/463 +[#474]: https://github.com/RustCrypto/formats/pull/474 +[#482]: https://github.com/RustCrypto/formats/pull/482 + +## 0.3.1 (2021-11-17) +### Changed +- Relax `base64ct` version requirement to `^1` ([#239]) + +[#239]: https://github.com/RustCrypto/formats/pull/239 + +## 0.3.0 (2021-11-14) +### Added +- `Decoder` struct ([#177]) + +### Changed +- Rust 2021 edition upgrade; MSRV 1.56 ([#136]) +- Bump `base64ct` dependency to v1.2 ([#175]) + +[#136]: https://github.com/RustCrypto/formats/pull/136 +[#175]: https://github.com/RustCrypto/formats/pull/175 +[#177]: https://github.com/RustCrypto/formats/pull/177 + +## 0.2.4 (2021-11-07) +### Changed +- Restrict `base64ct` dependency to `<1.2` to prevent MSRV breakages + +## 0.2.3 (2021-10-17) +### Added +- `PemLabel` trait ([#117]) + +[#117]: https://github.com/RustCrypto/formats/pull/117 + +## 0.2.2 (2021-09-16) +### Changed +- Allow for data before PEM encapsulation boundary ([#40]) + +[#40]: https://github.com/RustCrypto/formats/pull/40 + +## 0.2.1 (2021-09-14) +### Added +- `decode_label` ([#22]) +- `Error::HeaderDisallowed` ([#13], [#19], [#21]) + +### Changed +- Moved to `formats` repo ([#2]) + +[#2]: https://github.com/RustCrypto/formats/pull/2 +[#13]: https://github.com/RustCrypto/formats/pull/13 +[#19]: https://github.com/RustCrypto/formats/pull/19 +[#21]: https://github.com/RustCrypto/formats/pull/21 +[#22]: https://github.com/RustCrypto/formats/pull/22 + +## 0.2.0 (2021-07-26) +### Added +- Support for customizing PEM line endings + +## 0.1.1 (2021-07-24) +### Changed +- Increase LF precedence in EOL stripping functions + +### Fixed +- Bug in the size calculation for `decode_vec` + +## 0.1.0 (2021-07-23) +- Initial release diff --git a/vendor/pem-rfc7468/Cargo.toml b/vendor/pem-rfc7468/Cargo.toml new file mode 100644 index 0000000..15c3179 --- /dev/null +++ b/vendor/pem-rfc7468/Cargo.toml @@ -0,0 +1,58 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.60" +name = "pem-rfc7468" +version = "0.7.0" +authors = ["RustCrypto Developers"] +description = """ +PEM Encoding (RFC 7468) for PKIX, PKCS, and CMS Structures, implementing a +strict subset of the original Privacy-Enhanced Mail encoding intended +specifically for use with cryptographic keys, certificates, and other messages. +Provides a no_std-friendly, constant-time implementation suitable for use with +cryptographic private keys. +""" +readme = "README.md" +keywords = [ + "crypto", + "key", + "pem", + "pkcs", + "rsa", +] +categories = [ + "cryptography", + "data-structures", + "encoding", + "no-std", + "parser-implementations", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/formats/tree/master/pem-rfc7468" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[dependencies.base64ct] +version = "1.4" + +[features] +alloc = ["base64ct/alloc"] +std = [ + "alloc", + "base64ct/std", +] diff --git a/vendor/pem-rfc7468/LICENSE-APACHE b/vendor/pem-rfc7468/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/vendor/pem-rfc7468/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/pem-rfc7468/LICENSE-MIT b/vendor/pem-rfc7468/LICENSE-MIT new file mode 100644 index 0000000..c869ada --- /dev/null +++ b/vendor/pem-rfc7468/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2021 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/pem-rfc7468/README.md b/vendor/pem-rfc7468/README.md new file mode 100644 index 0000000..4a085a2 --- /dev/null +++ b/vendor/pem-rfc7468/README.md @@ -0,0 +1,101 @@ +# [RustCrypto]: PEM Encoding ([RFC 7468]) + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +[![Build Status][build-image]][build-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] + +Pure Rust implementation of PEM Encoding ([RFC 7468]) for PKIX, PKCS, and +CMS Structures, a strict subset of the original Privacy-Enhanced Mail encoding +intended specifically for use with cryptographic keys, certificates, and other +messages. + +Provides a `no_std`-friendly, constant-time implementation suitable for use with +cryptographic private keys. + +[Documentation][docs-link] + +## About + +Many cryptography-related document formats, such as certificates (PKIX), +private and public keys/keypairs (PKCS), and other cryptographic messages (CMS) +provide an ASCII encoding which can be traced back to Privacy-Enhanced Mail +(PEM) as defined [RFC 1421], which look like the following: + +```text +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF +-----END PRIVATE KEY----- +``` + +However, all of these formats actually implement a text-based encoding that is +similar but *not* identical to the legacy PEM encoding as described in +[RFC 1421]. + +For this reason, [RFC 7468] was created to describe a stricter form of +"PEM encoding" for use in these applications which codifies the previously +de facto rules that most implementations operate by, and makes recommendations +to promote interoperability. + +This crate provides a strict interpretation of the [RFC 7468] rules, +implementing MUSTs and SHOULDs while avoiding the MAYs, targeting the +"ABNF (Strict)" subset of the grammar as described in +[RFC 7468 Section 3 Figure 3 (p6)][RFC 7468 p6]. + +## Implementation notes + +- `no_std`-friendly core implementation which requires no heap allocations + and avoids copies and temporary buffers. +- Optional `alloc`-dependent convenience features and buffered decoder/encoder. +- Uses the [`base64ct`] crate to decode/encode Base64 in constant-time. +- PEM parser avoids branching on potentially secret data as much as possible. + +The paper [Util::Lookup: Exploiting key decoding in cryptographic libraries][Util::Lookup] +demonstrates how the leakage from non-constant-time PEM parsers can be used +to practically extract RSA private keys from SGX enclaves. + +## Minimum Supported Rust Version + +This crate requires **Rust 1.60** at a minimum. + +We may change the MSRV in the future, but it will be accompanied by a minor +version bump. + +## License + +Licensed under either of: + +- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +- [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://buildstats.info/crate/pem-rfc7468 +[crate-link]: https://crates.io/crates/pem-rfc7468 +[docs-image]: https://docs.rs/pem-rfc7468/badge.svg +[docs-link]: https://docs.rs/pem-rfc7468/ +[build-image]: https://github.com/RustCrypto/formats/actions/workflows/pem-rfc7468.yml/badge.svg +[build-link]: https://github.com/RustCrypto/formats/actions/workflows/pem-rfc7468.yml +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats + +[//]: # (links) + +[RustCrypto]: https://github.com/rustcrypto +[RFC 1421]: https://datatracker.ietf.org/doc/html/rfc1421 +[RFC 7468]: https://datatracker.ietf.org/doc/html/rfc7468 +[RFC 7468 p6]: https://datatracker.ietf.org/doc/html/rfc7468#page-6 +[`base64ct`]: https://github.com/RustCrypto/formats/tree/master/base64ct +[Util::Lookup]: https://arxiv.org/pdf/2108.04600.pdf diff --git a/vendor/pem-rfc7468/src/decoder.rs b/vendor/pem-rfc7468/src/decoder.rs new file mode 100644 index 0000000..16e97fb --- /dev/null +++ b/vendor/pem-rfc7468/src/decoder.rs @@ -0,0 +1,270 @@ +//! Decoder for PEM encapsulated data. +//! +//! From RFC 7468 Section 2: +//! +//! > Textual encoding begins with a line comprising "-----BEGIN ", a +//! > label, and "-----", and ends with a line comprising "-----END ", a +//! > label, and "-----". Between these lines, or "encapsulation +//! > boundaries", are base64-encoded data according to Section 4 of +//! > [RFC 4648]. +//! +//! [RFC 4648]: https://datatracker.ietf.org/doc/html/rfc4648 + +use crate::{ + grammar, Base64Decoder, Error, Result, BASE64_WRAP_WIDTH, POST_ENCAPSULATION_BOUNDARY, + PRE_ENCAPSULATION_BOUNDARY, +}; +use core::str; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[cfg(feature = "std")] +use std::io; + +/// Decode a PEM document according to RFC 7468's "Strict" grammar. +/// +/// On success, writes the decoded document into the provided buffer, returning +/// the decoded label and the portion of the provided buffer containing the +/// decoded message. +pub fn decode<'i, 'o>(pem: &'i [u8], buf: &'o mut [u8]) -> Result<(&'i str, &'o [u8])> { + let mut decoder = Decoder::new(pem).map_err(|e| check_for_headers(pem, e))?; + let type_label = decoder.type_label(); + let buf = buf + .get_mut(..decoder.remaining_len()) + .ok_or(Error::Length)?; + let decoded = decoder.decode(buf).map_err(|e| check_for_headers(pem, e))?; + + if decoder.base64.is_finished() { + Ok((type_label, decoded)) + } else { + Err(Error::Length) + } +} + +/// Decode a PEM document according to RFC 7468's "Strict" grammar, returning +/// the result as a [`Vec`] upon success. +#[cfg(feature = "alloc")] +pub fn decode_vec(pem: &[u8]) -> Result<(&str, Vec<u8>)> { + let mut decoder = Decoder::new(pem).map_err(|e| check_for_headers(pem, e))?; + let type_label = decoder.type_label(); + let mut buf = Vec::new(); + decoder + .decode_to_end(&mut buf) + .map_err(|e| check_for_headers(pem, e))?; + Ok((type_label, buf)) +} + +/// Decode the encapsulation boundaries of a PEM document according to RFC 7468's "Strict" grammar. +/// +/// On success, returning the decoded label. +pub fn decode_label(pem: &[u8]) -> Result<&str> { + Ok(Encapsulation::try_from(pem)?.label()) +} + +/// Buffered PEM decoder. +/// +/// Stateful buffered decoder type which decodes an input PEM document according +/// to RFC 7468's "Strict" grammar. +#[derive(Clone)] +pub struct Decoder<'i> { + /// PEM type label. + type_label: &'i str, + + /// Buffered Base64 decoder. + base64: Base64Decoder<'i>, +} + +impl<'i> Decoder<'i> { + /// Create a new PEM [`Decoder`] with the default options. + /// + /// Uses the default 64-character line wrapping. + pub fn new(pem: &'i [u8]) -> Result<Self> { + Self::new_wrapped(pem, BASE64_WRAP_WIDTH) + } + + /// Create a new PEM [`Decoder`] which wraps at the given line width. + pub fn new_wrapped(pem: &'i [u8], line_width: usize) -> Result<Self> { + let encapsulation = Encapsulation::try_from(pem)?; + let type_label = encapsulation.label(); + let base64 = Base64Decoder::new_wrapped(encapsulation.encapsulated_text, line_width)?; + Ok(Self { type_label, base64 }) + } + + /// Get the PEM type label for the input document. + pub fn type_label(&self) -> &'i str { + self.type_label + } + + /// Decode data into the provided output buffer. + /// + /// There must be at least as much remaining Base64 input to be decoded + /// in order to completely fill `buf`. + pub fn decode<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { + Ok(self.base64.decode(buf)?) + } + + /// Decode all of the remaining data in the input buffer into `buf`. + #[cfg(feature = "alloc")] + pub fn decode_to_end<'o>(&mut self, buf: &'o mut Vec<u8>) -> Result<&'o [u8]> { + Ok(self.base64.decode_to_end(buf)?) + } + + /// Get the decoded length of the remaining PEM data after Base64 decoding. + pub fn remaining_len(&self) -> usize { + self.base64.remaining_len() + } + + /// Are we finished decoding the PEM input? + pub fn is_finished(&self) -> bool { + self.base64.is_finished() + } +} + +impl<'i> From<Decoder<'i>> for Base64Decoder<'i> { + fn from(decoder: Decoder<'i>) -> Base64Decoder<'i> { + decoder.base64 + } +} + +#[cfg(feature = "std")] +impl<'i> io::Read for Decoder<'i> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.base64.read(buf) + } + + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.base64.read_to_end(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.base64.read_exact(buf) + } +} + +/// PEM encapsulation parser. +/// +/// This parser performs an initial pass over the data, locating the +/// pre-encapsulation (`---BEGIN [...]---`) and post-encapsulation +/// (`---END [...]`) boundaries while attempting to avoid branching +/// on the potentially secret Base64-encoded data encapsulated between +/// the two boundaries. +/// +/// It only supports a single encapsulated message at present. Future work +/// could potentially include extending it provide an iterator over a series +/// of encapsulated messages. +#[derive(Copy, Clone, Debug)] +struct Encapsulation<'a> { + /// Type label extracted from the pre/post-encapsulation boundaries. + /// + /// From RFC 7468 Section 2: + /// + /// > The type of data encoded is labeled depending on the type label in + /// > the "-----BEGIN " line (pre-encapsulation boundary). For example, + /// > the line may be "-----BEGIN CERTIFICATE-----" to indicate that the + /// > content is a PKIX certificate (see further below). Generators MUST + /// > put the same label on the "-----END " line (post-encapsulation + /// > boundary) as the corresponding "-----BEGIN " line. Labels are + /// > formally case-sensitive, uppercase, and comprised of zero or more + /// > characters; they do not contain consecutive spaces or hyphen-minuses, + /// > nor do they contain spaces or hyphen-minuses at either end. Parsers + /// > MAY disregard the label in the post-encapsulation boundary instead of + /// > signaling an error if there is a label mismatch: some extant + /// > implementations require the labels to match; others do not. + label: &'a str, + + /// Encapsulated text portion contained between the boundaries. + /// + /// This data should be encoded as Base64, however this type performs no + /// validation of it so it can be handled in constant-time. + encapsulated_text: &'a [u8], +} + +impl<'a> Encapsulation<'a> { + /// Parse the type label and encapsulated text from between the + /// pre/post-encapsulation boundaries. + pub fn parse(data: &'a [u8]) -> Result<Self> { + // Strip the "preamble": optional text occurring before the pre-encapsulation boundary + let data = grammar::strip_preamble(data)?; + + // Parse pre-encapsulation boundary (including label) + let data = data + .strip_prefix(PRE_ENCAPSULATION_BOUNDARY) + .ok_or(Error::PreEncapsulationBoundary)?; + + let (label, body) = grammar::split_label(data).ok_or(Error::Label)?; + + let mut body = match grammar::strip_trailing_eol(body).unwrap_or(body) { + [head @ .., b'-', b'-', b'-', b'-', b'-'] => head, + _ => return Err(Error::PreEncapsulationBoundary), + }; + + // Ensure body ends with a properly labeled post-encapsulation boundary + for &slice in [POST_ENCAPSULATION_BOUNDARY, label.as_bytes()].iter().rev() { + // Ensure the input ends with the post encapsulation boundary as + // well as a matching label + if !body.ends_with(slice) { + return Err(Error::PostEncapsulationBoundary); + } + + let len = body.len().checked_sub(slice.len()).ok_or(Error::Length)?; + body = body.get(..len).ok_or(Error::PostEncapsulationBoundary)?; + } + + let encapsulated_text = + grammar::strip_trailing_eol(body).ok_or(Error::PostEncapsulationBoundary)?; + + Ok(Self { + label, + encapsulated_text, + }) + } + + /// Get the label parsed from the encapsulation boundaries. + pub fn label(self) -> &'a str { + self.label + } +} + +impl<'a> TryFrom<&'a [u8]> for Encapsulation<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result<Self> { + Self::parse(bytes) + } +} + +/// Check for PEM headers in the input, as they are disallowed by RFC7468. +/// +/// Returns `Error::HeaderDisallowed` if headers are encountered. +fn check_for_headers(pem: &[u8], err: Error) -> Error { + if err == Error::Base64(base64ct::Error::InvalidEncoding) + && pem.iter().any(|&b| b == grammar::CHAR_COLON) + { + Error::HeaderDisallowed + } else { + err + } +} + +#[cfg(test)] +mod tests { + use super::Encapsulation; + + #[test] + fn pkcs8_example() { + let pem = include_bytes!("../tests/examples/pkcs8.pem"); + let encapsulation = Encapsulation::parse(pem).unwrap(); + assert_eq!(encapsulation.label, "PRIVATE KEY"); + + assert_eq!( + encapsulation.encapsulated_text, + &[ + 77, 67, 52, 67, 65, 81, 65, 119, 66, 81, 89, 68, 75, 50, 86, 119, 66, 67, 73, 69, + 73, 66, 102, 116, 110, 72, 80, 112, 50, 50, 83, 101, 119, 89, 109, 109, 69, 111, + 77, 99, 88, 56, 86, 119, 73, 52, 73, 72, 119, 97, 113, 100, 43, 57, 76, 70, 80, + 106, 47, 49, 53, 101, 113, 70 + ] + ); + } +} diff --git a/vendor/pem-rfc7468/src/encoder.rs b/vendor/pem-rfc7468/src/encoder.rs new file mode 100644 index 0000000..c48df1f --- /dev/null +++ b/vendor/pem-rfc7468/src/encoder.rs @@ -0,0 +1,299 @@ +//! PEM encoder. + +use crate::{ + grammar, Base64Encoder, Error, LineEnding, Result, BASE64_WRAP_WIDTH, + ENCAPSULATION_BOUNDARY_DELIMITER, POST_ENCAPSULATION_BOUNDARY, PRE_ENCAPSULATION_BOUNDARY, +}; +use base64ct::{Base64, Encoding}; +use core::str; + +#[cfg(feature = "alloc")] +use alloc::string::String; + +#[cfg(feature = "std")] +use std::io; + +/// Compute the length of a PEM encoded document which encapsulates a +/// Base64-encoded body including line endings every 64 characters. +/// +/// The `input_len` parameter specifies the length of the raw input +/// bytes prior to Base64 encoding. +/// +/// Note that the current implementation of this function computes an upper +/// bound of the length and the actual encoded document may be slightly shorter +/// (typically 1-byte). Downstream consumers of this function should check the +/// actual encoded length and potentially truncate buffers allocated using this +/// function to estimate the encapsulated size. +/// +/// Use [`encoded_len`] (when possible) to obtain a precise length. +/// +/// ## Returns +/// - `Ok(len)` on success +/// - `Err(Error::Length)` on length overflow +pub fn encapsulated_len(label: &str, line_ending: LineEnding, input_len: usize) -> Result<usize> { + encapsulated_len_wrapped(label, BASE64_WRAP_WIDTH, line_ending, input_len) +} + +/// Compute the length of a PEM encoded document with the Base64 body +/// line wrapped at the specified `width`. +/// +/// This is the same as [`encapsulated_len`], which defaults to a width of 64. +/// +/// Note that per [RFC7468 § 2] encoding PEM with any other wrap width besides +/// 64 is technically non-compliant: +/// +/// > Generators MUST wrap the base64-encoded lines so that each line +/// > consists of exactly 64 characters except for the final line, which +/// > will encode the remainder of the data (within the 64-character line +/// > boundary) +/// +/// [RFC7468 § 2]: https://datatracker.ietf.org/doc/html/rfc7468#section-2 +pub fn encapsulated_len_wrapped( + label: &str, + line_width: usize, + line_ending: LineEnding, + input_len: usize, +) -> Result<usize> { + if line_width < 4 { + return Err(Error::Length); + } + + let base64_len = input_len + .checked_mul(4) + .and_then(|n| n.checked_div(3)) + .and_then(|n| n.checked_add(3)) + .ok_or(Error::Length)? + & !3; + + let base64_len_wrapped = base64_len_wrapped(base64_len, line_width, line_ending)?; + encapsulated_len_inner(label, line_ending, base64_len_wrapped) +} + +/// Get the length of a PEM encoded document with the given bytes and label. +/// +/// This function computes a precise length of the PEM encoding of the given +/// `input` data. +/// +/// ## Returns +/// - `Ok(len)` on success +/// - `Err(Error::Length)` on length overflow +pub fn encoded_len(label: &str, line_ending: LineEnding, input: &[u8]) -> Result<usize> { + let base64_len = Base64::encoded_len(input); + let base64_len_wrapped = base64_len_wrapped(base64_len, BASE64_WRAP_WIDTH, line_ending)?; + encapsulated_len_inner(label, line_ending, base64_len_wrapped) +} + +/// Encode a PEM document according to RFC 7468's "Strict" grammar. +pub fn encode<'o>( + type_label: &str, + line_ending: LineEnding, + input: &[u8], + buf: &'o mut [u8], +) -> Result<&'o str> { + let mut encoder = Encoder::new(type_label, line_ending, buf)?; + encoder.encode(input)?; + let encoded_len = encoder.finish()?; + let output = &buf[..encoded_len]; + + // Sanity check + debug_assert!(str::from_utf8(output).is_ok()); + + // Ensure `output` contains characters from the lower 7-bit ASCII set + if output.iter().fold(0u8, |acc, &byte| acc | (byte & 0x80)) == 0 { + // Use unchecked conversion to avoid applying UTF-8 checks to potentially + // secret PEM documents (and therefore introducing a potential timing + // sidechannel) + // + // SAFETY: contents of this buffer are controlled entirely by the encoder, + // which ensures the contents are always a valid (ASCII) subset of UTF-8. + // It's also additionally sanity checked by two assertions above to ensure + // the validity (with the always-on runtime check implemented in a + // constant time-ish manner. + #[allow(unsafe_code)] + Ok(unsafe { str::from_utf8_unchecked(output) }) + } else { + Err(Error::CharacterEncoding) + } +} + +/// Encode a PEM document according to RFC 7468's "Strict" grammar, returning +/// the result as a [`String`]. +#[cfg(feature = "alloc")] +pub fn encode_string(label: &str, line_ending: LineEnding, input: &[u8]) -> Result<String> { + let expected_len = encoded_len(label, line_ending, input)?; + let mut buf = vec![0u8; expected_len]; + let actual_len = encode(label, line_ending, input, &mut buf)?.len(); + debug_assert_eq!(expected_len, actual_len); + String::from_utf8(buf).map_err(|_| Error::CharacterEncoding) +} + +/// Compute the encapsulated length of Base64 data of the given length. +fn encapsulated_len_inner( + label: &str, + line_ending: LineEnding, + base64_len: usize, +) -> Result<usize> { + [ + PRE_ENCAPSULATION_BOUNDARY.len(), + label.as_bytes().len(), + ENCAPSULATION_BOUNDARY_DELIMITER.len(), + line_ending.len(), + base64_len, + line_ending.len(), + POST_ENCAPSULATION_BOUNDARY.len(), + label.as_bytes().len(), + ENCAPSULATION_BOUNDARY_DELIMITER.len(), + line_ending.len(), + ] + .into_iter() + .try_fold(0usize, |acc, len| acc.checked_add(len)) + .ok_or(Error::Length) +} + +/// Compute Base64 length line-wrapped at the specified width with the given +/// line ending. +fn base64_len_wrapped( + base64_len: usize, + line_width: usize, + line_ending: LineEnding, +) -> Result<usize> { + base64_len + .saturating_sub(1) + .checked_div(line_width) + .and_then(|lines| lines.checked_mul(line_ending.len())) + .and_then(|len| len.checked_add(base64_len)) + .ok_or(Error::Length) +} + +/// Buffered PEM encoder. +/// +/// Stateful buffered encoder type which encodes an input PEM document according +/// to RFC 7468's "Strict" grammar. +pub struct Encoder<'l, 'o> { + /// PEM type label. + type_label: &'l str, + + /// Line ending used to wrap Base64. + line_ending: LineEnding, + + /// Buffered Base64 encoder. + base64: Base64Encoder<'o>, +} + +impl<'l, 'o> Encoder<'l, 'o> { + /// Create a new PEM [`Encoder`] with the default options which + /// writes output into the provided buffer. + /// + /// Uses the default 64-character line wrapping. + pub fn new(type_label: &'l str, line_ending: LineEnding, out: &'o mut [u8]) -> Result<Self> { + Self::new_wrapped(type_label, BASE64_WRAP_WIDTH, line_ending, out) + } + + /// Create a new PEM [`Encoder`] which wraps at the given line width. + /// + /// Note that per [RFC7468 § 2] encoding PEM with any other wrap width besides + /// 64 is technically non-compliant: + /// + /// > Generators MUST wrap the base64-encoded lines so that each line + /// > consists of exactly 64 characters except for the final line, which + /// > will encode the remainder of the data (within the 64-character line + /// > boundary) + /// + /// This method is provided with the intended purpose of implementing the + /// OpenSSH private key format, which uses a non-standard wrap width of 70. + /// + /// [RFC7468 § 2]: https://datatracker.ietf.org/doc/html/rfc7468#section-2 + pub fn new_wrapped( + type_label: &'l str, + line_width: usize, + line_ending: LineEnding, + mut out: &'o mut [u8], + ) -> Result<Self> { + grammar::validate_label(type_label.as_bytes())?; + + for boundary_part in [ + PRE_ENCAPSULATION_BOUNDARY, + type_label.as_bytes(), + ENCAPSULATION_BOUNDARY_DELIMITER, + line_ending.as_bytes(), + ] { + if out.len() < boundary_part.len() { + return Err(Error::Length); + } + + let (part, rest) = out.split_at_mut(boundary_part.len()); + out = rest; + + part.copy_from_slice(boundary_part); + } + + let base64 = Base64Encoder::new_wrapped(out, line_width, line_ending)?; + + Ok(Self { + type_label, + line_ending, + base64, + }) + } + + /// Get the PEM type label used for this document. + pub fn type_label(&self) -> &'l str { + self.type_label + } + + /// Encode the provided input data. + /// + /// This method can be called as many times as needed with any sized input + /// to write data encoded data into the output buffer, so long as there is + /// sufficient space in the buffer to handle the resulting Base64 encoded + /// data. + pub fn encode(&mut self, input: &[u8]) -> Result<()> { + self.base64.encode(input)?; + Ok(()) + } + + /// Borrow the inner [`Base64Encoder`]. + pub fn base64_encoder(&mut self) -> &mut Base64Encoder<'o> { + &mut self.base64 + } + + /// Finish encoding PEM, writing the post-encapsulation boundary. + /// + /// On success, returns the total number of bytes written to the output + /// buffer. + pub fn finish(self) -> Result<usize> { + let (base64, mut out) = self.base64.finish_with_remaining()?; + + for boundary_part in [ + self.line_ending.as_bytes(), + POST_ENCAPSULATION_BOUNDARY, + self.type_label.as_bytes(), + ENCAPSULATION_BOUNDARY_DELIMITER, + self.line_ending.as_bytes(), + ] { + if out.len() < boundary_part.len() { + return Err(Error::Length); + } + + let (part, rest) = out.split_at_mut(boundary_part.len()); + out = rest; + + part.copy_from_slice(boundary_part); + } + + encapsulated_len_inner(self.type_label, self.line_ending, base64.len()) + } +} + +#[cfg(feature = "std")] +impl<'l, 'o> io::Write for Encoder<'l, 'o> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.encode(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + // TODO(tarcieri): return an error if there's still data remaining in the buffer? + Ok(()) + } +} diff --git a/vendor/pem-rfc7468/src/error.rs b/vendor/pem-rfc7468/src/error.rs new file mode 100644 index 0000000..6a93465 --- /dev/null +++ b/vendor/pem-rfc7468/src/error.rs @@ -0,0 +1,107 @@ +//! Error types + +use core::fmt; + +/// Result type with the `pem-rfc7468` crate's [`Error`] type. +pub type Result<T> = core::result::Result<T, Error>; + +/// PEM errors. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum Error { + /// Base64-related errors. + Base64(base64ct::Error), + + /// Character encoding-related errors. + CharacterEncoding, + + /// Errors in the encapsulated text (which aren't specifically Base64-related). + EncapsulatedText, + + /// Header detected in the encapsulated text. + HeaderDisallowed, + + /// Invalid label. + Label, + + /// Invalid length. + Length, + + /// "Preamble" (text before pre-encapsulation boundary) contains invalid data. + Preamble, + + /// Errors in the pre-encapsulation boundary. + PreEncapsulationBoundary, + + /// Errors in the post-encapsulation boundary. + PostEncapsulationBoundary, + + /// Unexpected PEM type label. + UnexpectedTypeLabel { + /// Type label that was expected. + expected: &'static str, + }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Base64(err) => write!(f, "PEM Base64 error: {}", err), + Error::CharacterEncoding => f.write_str("PEM character encoding error"), + Error::EncapsulatedText => f.write_str("PEM error in encapsulated text"), + Error::HeaderDisallowed => f.write_str("PEM headers disallowed by RFC7468"), + Error::Label => f.write_str("PEM type label invalid"), + Error::Length => f.write_str("PEM length invalid"), + Error::Preamble => f.write_str("PEM preamble contains invalid data (NUL byte)"), + Error::PreEncapsulationBoundary => { + f.write_str("PEM error in pre-encapsulation boundary") + } + Error::PostEncapsulationBoundary => { + f.write_str("PEM error in post-encapsulation boundary") + } + Error::UnexpectedTypeLabel { expected } => { + write!(f, "unexpected PEM type label: expecting \"{}\"", expected) + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl From<base64ct::Error> for Error { + fn from(err: base64ct::Error) -> Error { + Error::Base64(err) + } +} + +impl From<base64ct::InvalidLengthError> for Error { + fn from(_: base64ct::InvalidLengthError) -> Error { + Error::Length + } +} + +impl From<core::str::Utf8Error> for Error { + fn from(_: core::str::Utf8Error) -> Error { + Error::CharacterEncoding + } +} + +#[cfg(feature = "std")] +impl From<Error> for std::io::Error { + fn from(err: Error) -> std::io::Error { + let kind = match err { + Error::Base64(err) => return err.into(), // Use existing conversion + Error::CharacterEncoding + | Error::EncapsulatedText + | Error::Label + | Error::Preamble + | Error::PreEncapsulationBoundary + | Error::PostEncapsulationBoundary => std::io::ErrorKind::InvalidData, + Error::Length => std::io::ErrorKind::UnexpectedEof, + _ => std::io::ErrorKind::Other, + }; + + std::io::Error::new(kind, err) + } +} diff --git a/vendor/pem-rfc7468/src/grammar.rs b/vendor/pem-rfc7468/src/grammar.rs new file mode 100644 index 0000000..91085fe --- /dev/null +++ b/vendor/pem-rfc7468/src/grammar.rs @@ -0,0 +1,232 @@ +//! Helper functions and rules for enforcing the ABNF grammar for +//! RFC 7468-flavored PEM as described in Section 3. +//! +//! The grammar described below is intended to follow the "ABNF (Strict)" +//! subset of the grammar as described in Section 3 Figure 3. + +use crate::{Error, Result, PRE_ENCAPSULATION_BOUNDARY}; +use core::str; + +/// NUL char +pub(crate) const CHAR_NUL: u8 = 0x00; + +/// Horizontal tab +pub(crate) const CHAR_HT: u8 = 0x09; + +/// Space +pub(crate) const CHAR_SP: u8 = 0x20; + +/// Carriage return +pub(crate) const CHAR_CR: u8 = 0x0d; + +/// Line feed +pub(crate) const CHAR_LF: u8 = 0x0a; + +/// Colon ':' +pub(crate) const CHAR_COLON: u8 = 0x3A; + +/// Any printable character except hyphen-minus, as defined in the +/// 'labelchar' production in the RFC 7468 ABNF grammar +pub(crate) fn is_labelchar(char: u8) -> bool { + matches!(char, 0x21..=0x2C | 0x2E..=0x7E) +} + +/// Does the provided byte match a character allowed in a label? +// TODO: allow hyphen-minus to match the 'label' production in the ABNF grammar +pub(crate) fn is_allowed_in_label(char: u8) -> bool { + is_labelchar(char) || matches!(char, CHAR_HT | CHAR_SP) +} + +/// Does the provided byte match the "WSP" ABNF production from Section 3? +/// +/// > The common ABNF production WSP is congruent with "blank"; +/// > a new production W is used for "whitespace" +pub(crate) fn is_wsp(char: u8) -> bool { + matches!(char, CHAR_HT | CHAR_SP) +} + +/// Strip the "preamble", i.e. data that appears before the PEM +/// pre-encapsulation boundary. +/// +/// Presently no attempt is made to ensure the preamble decodes successfully +/// under any particular character encoding. The only byte which is disallowed +/// is the NUL byte. This restriction does not appear in RFC7468, but rather +/// is inspired by the OpenSSL PEM decoder. +/// +/// Returns a slice which starts at the beginning of the encapsulated text. +/// +/// From RFC7468: +/// > Data before the encapsulation boundaries are permitted, and +/// > parsers MUST NOT malfunction when processing such data. +pub(crate) fn strip_preamble(mut bytes: &[u8]) -> Result<&[u8]> { + if bytes.starts_with(PRE_ENCAPSULATION_BOUNDARY) { + return Ok(bytes); + } + + while let Some((byte, remaining)) = bytes.split_first() { + match *byte { + CHAR_NUL => { + return Err(Error::Preamble); + } + CHAR_LF if remaining.starts_with(PRE_ENCAPSULATION_BOUNDARY) => { + return Ok(remaining); + } + _ => (), + } + + bytes = remaining; + } + + Err(Error::Preamble) +} + +/// Strip a newline (`eol`) from the beginning of the provided byte slice. +/// +/// The newline is considered mandatory and a decoding error will occur if it +/// is not present. +/// +/// From RFC 7468 Section 3: +/// > lines are divided with CRLF, CR, or LF. +pub(crate) fn strip_leading_eol(bytes: &[u8]) -> Option<&[u8]> { + match bytes { + [CHAR_LF, rest @ ..] => Some(rest), + [CHAR_CR, CHAR_LF, rest @ ..] => Some(rest), + [CHAR_CR, rest @ ..] => Some(rest), + _ => None, + } +} + +/// Strip a newline (`eol`) from the end of the provided byte slice. +/// +/// The newline is considered mandatory and a decoding error will occur if it +/// is not present. +/// +/// From RFC 7468 Section 3: +/// > lines are divided with CRLF, CR, or LF. +pub(crate) fn strip_trailing_eol(bytes: &[u8]) -> Option<&[u8]> { + match bytes { + [head @ .., CHAR_CR, CHAR_LF] => Some(head), + [head @ .., CHAR_LF] => Some(head), + [head @ .., CHAR_CR] => Some(head), + _ => None, + } +} + +/// Split a slice beginning with a type label as located in an encapsulation +/// boundary. Returns the label as a `&str`, and slice beginning with the +/// encapsulated text with leading `-----` and newline removed. +/// +/// This implementation follows the rules put forth in Section 2, which are +/// stricter than those found in the ABNF grammar: +/// +/// > Labels are formally case-sensitive, uppercase, and comprised of zero or more +/// > characters; they do not contain consecutive spaces or hyphen-minuses, +/// > nor do they contain spaces or hyphen-minuses at either end. +/// +/// We apply a slightly stricter interpretation: +/// - Labels MAY be empty +/// - Non-empty labels MUST start with an upper-case letter: `'A'..='Z'` +/// - The only allowable characters subsequently are `'A'..='Z'` or WSP. +/// (NOTE: this is an overly strict initial implementation and should be relaxed) +/// - Whitespace MUST NOT contain more than one consecutive WSP character +// TODO(tarcieri): evaluate whether this is too strict; support '-' +pub(crate) fn split_label(bytes: &[u8]) -> Option<(&str, &[u8])> { + let mut n = 0usize; + + // TODO(tarcieri): handle hyphens in labels as well as spaces + let mut last_was_wsp = false; + + for &char in bytes { + // Validate character + if is_labelchar(char) { + last_was_wsp = false; + } else if char == b'-' { + // Possible start of encapsulation boundary delimiter + break; + } else if n != 0 && is_wsp(char) { + // Repeated whitespace disallowed + if last_was_wsp { + return None; + } + + last_was_wsp = true; + } else { + return None; + } + + n = n.checked_add(1)?; + } + + let (raw_label, rest) = bytes.split_at(n); + let label = str::from_utf8(raw_label).ok()?; + + match rest { + [b'-', b'-', b'-', b'-', b'-', body @ ..] => Some((label, strip_leading_eol(body)?)), + _ => None, + } +} + +/// Validate that the given bytes are allowed as a PEM type label, i.e. the +/// label encoded in the `BEGIN` and `END` encapsulation boundaries. +pub(crate) fn validate_label(label: &[u8]) -> Result<()> { + // TODO(tarcieri): handle hyphens in labels as well as spaces + let mut last_was_wsp = false; + + for &char in label { + if !is_allowed_in_label(char) { + return Err(Error::Label); + } + + if is_wsp(char) { + // Double sequential whitespace characters disallowed + if last_was_wsp { + return Err(Error::Label); + } + + last_was_wsp = true; + } else { + last_was_wsp = false; + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Empty label is OK. + #[test] + fn split_label_empty() { + let (label, body) = split_label(b"-----\nBODY").unwrap(); + assert_eq!(label, ""); + assert_eq!(body, b"BODY"); + } + + /// Label containing text. + #[test] + fn split_label_with_text() { + let (label, body) = split_label(b"PRIVATE KEY-----\nBODY").unwrap(); + assert_eq!(label, "PRIVATE KEY"); + assert_eq!(body, b"BODY"); + } + + /// Reject labels containing repeated spaces + #[test] + fn split_label_with_repeat_wsp_is_err() { + assert!(split_label(b"PRIVATE KEY-----\nBODY").is_none()); + } + + /// Basic validation of a label + #[test] + fn validate_private_key_label() { + assert_eq!(validate_label(b"PRIVATE KEY"), Ok(())); + } + + /// Reject labels with double spaces + #[test] + fn validate_private_key_label_reject_double_space() { + assert_eq!(validate_label(b"PRIVATE KEY"), Err(Error::Label)); + } +} diff --git a/vendor/pem-rfc7468/src/lib.rs b/vendor/pem-rfc7468/src/lib.rs new file mode 100644 index 0000000..45f4877 --- /dev/null +++ b/vendor/pem-rfc7468/src/lib.rs @@ -0,0 +1,122 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" +)] +#![deny(unsafe_code)] +#![warn( + clippy::integer_arithmetic, + clippy::mod_module_files, + clippy::panic, + clippy::panic_in_result_fn, + clippy::unwrap_used, + missing_docs, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] + +//! # Usage +//! +#![cfg_attr(feature = "std", doc = " ```")] +#![cfg_attr(not(feature = "std"), doc = " ```ignore")] +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! /// Example PEM document +//! /// NOTE: do not actually put private key literals into your source code!!! +//! let example_pem = "\ +//! -----BEGIN PRIVATE KEY----- +//! MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF +//! -----END PRIVATE KEY----- +//! "; +//! +//! // Decode PEM +//! let (type_label, data) = pem_rfc7468::decode_vec(example_pem.as_bytes())?; +//! assert_eq!(type_label, "PRIVATE KEY"); +//! assert_eq!( +//! data, +//! &[ +//! 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 23, 237, 156, 115, 233, 219, +//! 100, 158, 193, 137, 166, 18, 131, 28, 95, 197, 112, 35, 130, 7, 193, 170, 157, 251, +//! 210, 197, 62, 63, 245, 229, 234, 133 +//! ] +//! ); +//! +//! // Encode PEM +//! use pem_rfc7468::LineEnding; +//! let encoded_pem = pem_rfc7468::encode_string(type_label, LineEnding::default(), &data)?; +//! assert_eq!(&encoded_pem, example_pem); +//! # Ok(()) +//! # } +//! ``` + +#[cfg(feature = "alloc")] +#[macro_use] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +mod decoder; +mod encoder; +mod error; +mod grammar; + +pub use crate::{ + decoder::{decode, decode_label, Decoder}, + encoder::{encapsulated_len, encapsulated_len_wrapped, encode, encoded_len, Encoder}, + error::{Error, Result}, +}; +pub use base64ct::LineEnding; + +#[cfg(feature = "alloc")] +pub use crate::{decoder::decode_vec, encoder::encode_string}; + +/// The pre-encapsulation boundary appears before the encapsulated text. +/// +/// From RFC 7468 Section 2: +/// > There are exactly five hyphen-minus (also known as dash) characters ("-") +/// > on both ends of the encapsulation boundaries, no more, no less. +const PRE_ENCAPSULATION_BOUNDARY: &[u8] = b"-----BEGIN "; + +/// The post-encapsulation boundary appears immediately after the encapsulated text. +const POST_ENCAPSULATION_BOUNDARY: &[u8] = b"-----END "; + +/// Delimiter of encapsulation boundaries. +const ENCAPSULATION_BOUNDARY_DELIMITER: &[u8] = b"-----"; + +/// Width at which the Base64 body of RFC7468-compliant PEM is wrapped. +/// +/// From [RFC7468 § 2]: +/// +/// > Generators MUST wrap the base64-encoded lines so that each line +/// > consists of exactly 64 characters except for the final line, which +/// > will encode the remainder of the data (within the 64-character line +/// > boundary), and they MUST NOT emit extraneous whitespace. Parsers MAY +/// > handle other line sizes. +/// +/// [RFC7468 § 2]: https://datatracker.ietf.org/doc/html/rfc7468#section-2 +pub const BASE64_WRAP_WIDTH: usize = 64; + +/// Buffered Base64 decoder type. +pub type Base64Decoder<'i> = base64ct::Decoder<'i, base64ct::Base64>; + +/// Buffered Base64 encoder type. +pub type Base64Encoder<'o> = base64ct::Encoder<'o, base64ct::Base64>; + +/// Marker trait for types with an associated PEM type label. +pub trait PemLabel { + /// Expected PEM type label for a given document, e.g. `"PRIVATE KEY"` + const PEM_LABEL: &'static str; + + /// Validate that a given label matches the expected label. + fn validate_pem_label(actual: &str) -> Result<()> { + if Self::PEM_LABEL == actual { + Ok(()) + } else { + Err(Error::UnexpectedTypeLabel { + expected: Self::PEM_LABEL, + }) + } + } +} diff --git a/vendor/pem-rfc7468/tests/decode.rs b/vendor/pem-rfc7468/tests/decode.rs new file mode 100644 index 0000000..dc51528 --- /dev/null +++ b/vendor/pem-rfc7468/tests/decode.rs @@ -0,0 +1,112 @@ +//! PEM decoding tests + +#[test] +fn pkcs1_example() { + let pem = include_bytes!("examples/pkcs1.pem"); + let mut buf = [0u8; 2048]; + let (label, decoded) = pem_rfc7468::decode(pem, &mut buf).unwrap(); + assert_eq!(label, "RSA PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs1.der")); +} + +#[test] +fn binary_example() { + let der = include_bytes!("examples/pkcs1.der"); + let mut buf = [0u8; 2048]; + match pem_rfc7468::decode(der, &mut buf) { + Err(pem_rfc7468::Error::Preamble) => (), + _ => panic!("Expected Preamble error"), + } +} + +#[test] +fn pkcs1_example_with_preceeding_junk() { + let pem = include_bytes!("examples/pkcs1_with_preceeding_junk.pem"); + let mut buf = [0u8; 2048]; + let (label, decoded) = pem_rfc7468::decode(pem, &mut buf).unwrap(); + assert_eq!(label, "RSA PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs1.der")); +} + +#[test] +fn pkcs1_enc_example() { + let pem = include_bytes!("examples/ssh_rsa_pem_password.pem"); + let mut buf = [0u8; 2048]; + let result = pem_rfc7468::decode(pem, &mut buf); + assert_eq!(result, Err(pem_rfc7468::Error::HeaderDisallowed)); + + let label = pem_rfc7468::decode_label(pem).unwrap(); + assert_eq!(label, "RSA PRIVATE KEY"); +} + +#[test] +#[cfg(feature = "alloc")] +fn pkcs1_enc_example_with_vec() { + let pem = include_bytes!("examples/ssh_rsa_pem_password.pem"); + let result = pem_rfc7468::decode_vec(pem); + assert_eq!(result, Err(pem_rfc7468::Error::HeaderDisallowed)); +} + +#[test] +fn header_of_length_64() { + let pem = include_bytes!("examples/chosen_header.pem"); + let mut buf = [0u8; 2048]; + let result = pem_rfc7468::decode(pem, &mut buf); + assert_eq!(result, Err(pem_rfc7468::Error::HeaderDisallowed)); + + let label = pem_rfc7468::decode_label(pem).unwrap(); + assert_eq!(label, "RSA PRIVATE KEY"); +} + +#[test] +#[cfg(feature = "alloc")] +fn header_of_length_64_with_vec() { + let pem = include_bytes!("examples/chosen_header.pem"); + match pem_rfc7468::decode_vec(pem) { + Err(pem_rfc7468::Error::HeaderDisallowed) => (), + res => panic!("Expected HeaderDisallowed error; Found {:?}", res), + } +} + +#[test] +fn pkcs8_example() { + let pem = include_bytes!("examples/pkcs8.pem"); + let mut buf = [0u8; 2048]; + let (label, decoded) = pem_rfc7468::decode(pem, &mut buf).unwrap(); + assert_eq!(label, "PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs8.der")); +} + +#[test] +fn pkcs8_enc_example() { + let pem = include_bytes!("examples/pkcs8-enc.pem"); + let mut buf = [0u8; 2048]; + let (label, decoded) = pem_rfc7468::decode(pem, &mut buf).unwrap(); + assert_eq!(label, "ENCRYPTED PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs8-enc.der")); +} + +#[test] +#[cfg(feature = "alloc")] +fn pkcs1_example_with_vec() { + let pem = include_bytes!("examples/pkcs1.pem"); + let (label, decoded) = pem_rfc7468::decode_vec(pem).unwrap(); + assert_eq!(label, "RSA PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs1.der")); +} + +#[test] +#[cfg(feature = "alloc")] +fn pkcs8_enc_example_with_vec() { + let pem = include_bytes!("examples/pkcs8-enc.pem"); + let (label, decoded) = pem_rfc7468::decode_vec(pem).unwrap(); + assert_eq!(label, "ENCRYPTED PRIVATE KEY"); + assert_eq!(decoded, include_bytes!("examples/pkcs8-enc.der")); +} + +#[test] +fn ed25519_example() { + let pem = include_bytes!("examples/ed25519_id.pem"); + let label = pem_rfc7468::decode_label(pem).unwrap(); + assert_eq!(label, "ED25519 CERT"); +} diff --git a/vendor/pem-rfc7468/tests/encode.rs b/vendor/pem-rfc7468/tests/encode.rs new file mode 100644 index 0000000..8f2ac94 --- /dev/null +++ b/vendor/pem-rfc7468/tests/encode.rs @@ -0,0 +1,21 @@ +//! PEM decoding tests + +#![cfg(feature = "alloc")] + +use pem_rfc7468::LineEnding; + +#[test] +fn pkcs1_example() { + let label = "RSA PRIVATE KEY"; + let bytes = include_bytes!("examples/pkcs1.der"); + let encoded = pem_rfc7468::encode_string(label, LineEnding::LF, bytes).unwrap(); + assert_eq!(&encoded, include_str!("examples/pkcs1.pem")); +} + +#[test] +fn pkcs8_example() { + let label = "PRIVATE KEY"; + let bytes = include_bytes!("examples/pkcs8.der"); + let encoded = pem_rfc7468::encode_string(label, LineEnding::LF, bytes).unwrap(); + assert_eq!(&encoded, include_str!("examples/pkcs8.pem")); +} diff --git a/vendor/pem-rfc7468/tests/examples/chosen_header.pem b/vendor/pem-rfc7468/tests/examples/chosen_header.pem new file mode 100644 index 0000000..f493074 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/chosen_header.pem @@ -0,0 +1,31 @@ +-----BEGIN RSA PRIVATE KEY----- +A-Header-That-Happens-To-Be-Exactly: 64 characters long......... +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,15670D76FD184D46C40C971733E0543F + +/lVaZdZ2a6x5d3b96F6XpFzcnP35pUrwpKxEB07nF15Jc81jwEAg72OpFTp5QRMu +WXbbZ/dKF7ucGHvLQ/VvCNkbl6oqowSme94fzFsa/xuKRHAGDHVi/TQylIOBBJFv +vru/3EZkO8mAQRDTNfuSl0Y5Ir7uqAQy0E/xKfOdY73BO4//KEDEIshRwBxbOm3K +D2sU1Kp8RnnBgSNydG8AH/LrtBnFs9HWrb9JD0Nj5bIxZDzil5CYmTB8PRgb2Qy7 +bckVc+0Y/h8Ai+NjSc/rJVw0smKJbmNSoPyJH1WjDPW8wWtngCFQWaVCTubm08N1 +nqrzIclT3fnq8YFSbFJYZVPaADxjv2HW7dLH7grYqXx/5FTE34ixXTcwQ1KR0ZQX +uaGZhkiDVWf/q82JPREqH5hwbeGL9QwZHF4/74vKIsddEuVFp8EW7jn9INRoVBtK +/OBiVXmVELFhmVBqvQU7GSci7+fCntXIx6W3hGiJL2WyXfuP16u9BhF5kd+c3pAm +tOZ3Lc5XsceBIYq0rKhy7rDhEg0V8wF1jHeeiW0VKDt2cFePSAd4CIbHiRWbvwh+ +zIoNAB34k4cYShmjOHKem9FMHVHSwfRE39Vrwssj0HWVOp7KdXYv64w4Ywmn6wvA +r6p8IZWg7KqA5UApPpiBVs0BAx1KtZk3o1dvXAazklw23icnnZF6XqaH6EmnVsf9 +gbyK1NcH3lIalTYhs+hMwizkw/XDb1uU8G7Rz1QFKBiL56J8ePIA2NWRUwvdMEAv +rZXSq4Icwy566GIqdtMRNLcz6LthNEg9qg+fD5aGLrtTk8ACSQpb/ELMMzqDVTkI +07dB1Nhzx9nd9mUlIuA030I5w7f//5pS6/lGmmPZblygY1PBludl+p/P9OKJ+Jr0 +HTAI4SVxoYdp6YHDBJ9J7Wt6UnIe+/3WarY9d9X1XNGOE4K+nRFihSShtKHDtMY6 +eBEV1sBTXJ1KANG683CU+uDx2XpOVAwDGl5hyRdzOovNC1iWjSu+CvppDvZLuIMj +zIllu5E8PR2Zd1wIT1gnU/7HiVdM0m4jf6ptkGSWNSCLA0ipii0YYarXoyu0kbMY +BKKpp5QRXv6OwmSDMwQTPuRIWyk839X1ABE1XeTKt43Ns+Wtdboi8Cu/aO/Z5AoA +gbJ+CdyKJIJxDXA11cPq9SF2daYmqHV3agrrKmAwWBRwpCKvotv0Hxw2M1+91ZoU +NY52RraoNVQPOAEfhYNS0ltVPzxcDU5bA2WczO6QzmMl7So6dysw+fxtxaEUGt4m +Fj+p+rE64Okq4wWDlEQya/xu4KMZwzyDncgJHHyYahs+vCv9KbQLW8R0iHTbxQzX +Vhomq++Cm8kg5aA/UsLas/l6ZyfNIcA99U8shFFA5urOKMl/jSRd9v1c7H3nOPZ7 ++eN10E7hcRruwOkoBlpd2It3Y2M+1qBDWXLVSHSXmIuzdE+MZ8CZfvxe+FcfpvJU +BFsZbSEF2PQC+zhd1HjV6DUe3jCz88/rjUnXQCvEJ7z7Tuz3C7kKdR3OYYYLwuLW +LTy2VS0p3QuUeMnNRl0HxpB16BZax9mzFr0UvFKp2QQYzOkIghg2sLNEbtaJvHNh +-----END RSA PRIVATE KEY----- diff --git a/vendor/pem-rfc7468/tests/examples/ed25519_id.pem b/vendor/pem-rfc7468/tests/examples/ed25519_id.pem new file mode 100644 index 0000000..0e59899 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/ed25519_id.pem @@ -0,0 +1,5 @@ +-----BEGIN ED25519 CERT----- +AQQABrknAdj5BeHBAd0mq1KD3ABvDzpBvUD0zU88DASbkRuV0WiaAQAgBADPc8aR +rUUolIsrKFMKy7SVCxKvpGrcdFAni+Bah1WZHnac5JP3LnPc2/0G7dTSlSTeBk5k +XqIySdIqtfYbW0kQinA0PaxDzzX5g1q3CclY9lNTAglR5fP71kunXh7ntwk= +-----END ED25519 CERT----- diff --git a/vendor/pem-rfc7468/tests/examples/pkcs1.der b/vendor/pem-rfc7468/tests/examples/pkcs1.der Binary files differnew file mode 100644 index 0000000..bbf1876 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs1.der diff --git a/vendor/pem-rfc7468/tests/examples/pkcs1.pem b/vendor/pem-rfc7468/tests/examples/pkcs1.pem new file mode 100644 index 0000000..3b924f5 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p +78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC +38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ6 +7YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/ +vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+ +CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQABAoIBAH7Mg2LA7bB0EWQh +XiL3SrnZG6BpAHAM9jaQ5RFNjua9z7suP5YUaSpnegg/FopeUuWWjmQHudl8bg5A +ZPgtoLdYoU8XubfUH19I4o1lUXBPVuaeeqn6Yw/HZCjAbSXkVdz8VbesK092ZD/e +0/4V/3irsn5lrMSq0L322yfvYKaRDFxKCF7UMnWrGcHZl6Msbv/OffLRk19uYB7t +4WGhK1zCfKIfgdLJnD0eoI6Q4wU6sJvvpyTe8NDDo8HpdAwNn3YSahSewKp9gHgg +VIQlTZUdsHxM+R+2RUwJZYj9WSTbq+s1nKICUmjQBPnWbrPW963BE5utQPFt3mOe +EWRzdsECgYEA3MBhJC1Okq+u5yrFE8plufdwNvm9fg5uYUYafvdlQiXsFTx+XDGm +FXpuWhP/bheOh1jByzPZ1rvjF57xiZjkIuzcvtePTs/b5fT82K7CydDchkc8qb0W +2dI40h+13e++sUPKYdC9aqjZHzOgl3kOlkDbyRCF3F8mNDujE49rLWcCgYEA0/MU +dX5A6VSDb5K+JCNq8vDaBKNGU8GAr2fpYAhtk/3mXLI+/Z0JN0di9ZgeNhhJr2jN +11OU/2pOButpsgnkIo2y36cOQPf5dQpSgXZke3iNDld3osuLIuPNJn/3C087AtOq ++w4YxZClZLAxiLCqX8SBVrB2IiFCQ70SJ++n8vkCgYEAzmi3rBsNEA1jblVIh1PF +wJhD/bOQ4nBd92iUV8m9jZdl4wl4YX4u/IBI9MMkIG24YIe2VOl7s9Rk5+4/jNg/ +4QQ2998Y6aljxOZJEdZ+3jQELy4m49OhrTRq2ta5t/Z3CMsJTmLe6f9NXWZpr5iK +8iVdHOjtMXxqfYaR2jVNEtsCgYAl9uWUQiAoa037v0I1wO5YQ9IZgJGJUSDWynsg +C4JtPs5zji4ASY+sCipsqWnH8MPKGrC8QClxMr51ONe+30yw78a5jvfbpU9Wqpmq +vOU0xJwnlH1GeMUcY8eMfOFocjG0yOtYeubvBIDLr0/AFzz9WHp+Z69RX7m53nUR +GDlyKQKBgDGZVAbUBiB8rerqNbONBAxfipoa4IJ+ntBrFT2DtoIZNbSzaoK+nVbH +kbWMJycaV5PVOh1lfAiZeWCxQz5RcZh/RS8USnxyMG1j4dP/wLcbdasI8uRaSC6Y +hFHL5HjhLrIo0HRWySS2b2ztBI2FP1M+MaaGFPHDzm2OyZg85yr3 +-----END RSA PRIVATE KEY----- diff --git a/vendor/pem-rfc7468/tests/examples/pkcs1_with_preceeding_junk.pem b/vendor/pem-rfc7468/tests/examples/pkcs1_with_preceeding_junk.pem new file mode 100644 index 0000000..ae229e1 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs1_with_preceeding_junk.pem @@ -0,0 +1,44 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Vestibulum lacinia euismod gravida. +Sed non suscipit mauris. +Sed ac purus sem. +Cras ipsum velit, egestas eu lorem at, viverra pellentesque nulla. +Cras posuere commodo tortor, id viverra velit pellentesque sit amet. +Suspendisse bibendum eleifend lacus, ac venenatis elit commodo vulputate. +Maecenas dapibus libero a nulla aliquet pulvinar. +Vivamus scelerisque elit ac ex rhoncus, ac lacinia enim iaculis. +Vivamus ultrices, sapien vel sodales rhoncus, augue turpis congue turpis, ut laoreet orci tellus sed augue. +Mauris mi tellus, sollicitudin at placerat eu, malesuada nec est. +Curabitur semper ex massa, et laoreet mauris scelerisque non. +In posuere mauris non urna efficitur, id mattis nisi consectetur. +Sed dapibus, ante eget laoreet cursus, risus ante mattis neque, a convallis urna lorem eu tortor. +Curabitur tincidunt justo vitae eros venenatis tincidunt semper vel tellus. + + +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p +78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC +38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ6 +7YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/ +vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+ +CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQABAoIBAH7Mg2LA7bB0EWQh +XiL3SrnZG6BpAHAM9jaQ5RFNjua9z7suP5YUaSpnegg/FopeUuWWjmQHudl8bg5A +ZPgtoLdYoU8XubfUH19I4o1lUXBPVuaeeqn6Yw/HZCjAbSXkVdz8VbesK092ZD/e +0/4V/3irsn5lrMSq0L322yfvYKaRDFxKCF7UMnWrGcHZl6Msbv/OffLRk19uYB7t +4WGhK1zCfKIfgdLJnD0eoI6Q4wU6sJvvpyTe8NDDo8HpdAwNn3YSahSewKp9gHgg +VIQlTZUdsHxM+R+2RUwJZYj9WSTbq+s1nKICUmjQBPnWbrPW963BE5utQPFt3mOe +EWRzdsECgYEA3MBhJC1Okq+u5yrFE8plufdwNvm9fg5uYUYafvdlQiXsFTx+XDGm +FXpuWhP/bheOh1jByzPZ1rvjF57xiZjkIuzcvtePTs/b5fT82K7CydDchkc8qb0W +2dI40h+13e++sUPKYdC9aqjZHzOgl3kOlkDbyRCF3F8mNDujE49rLWcCgYEA0/MU +dX5A6VSDb5K+JCNq8vDaBKNGU8GAr2fpYAhtk/3mXLI+/Z0JN0di9ZgeNhhJr2jN +11OU/2pOButpsgnkIo2y36cOQPf5dQpSgXZke3iNDld3osuLIuPNJn/3C087AtOq ++w4YxZClZLAxiLCqX8SBVrB2IiFCQ70SJ++n8vkCgYEAzmi3rBsNEA1jblVIh1PF +wJhD/bOQ4nBd92iUV8m9jZdl4wl4YX4u/IBI9MMkIG24YIe2VOl7s9Rk5+4/jNg/ +4QQ2998Y6aljxOZJEdZ+3jQELy4m49OhrTRq2ta5t/Z3CMsJTmLe6f9NXWZpr5iK +8iVdHOjtMXxqfYaR2jVNEtsCgYAl9uWUQiAoa037v0I1wO5YQ9IZgJGJUSDWynsg +C4JtPs5zji4ASY+sCipsqWnH8MPKGrC8QClxMr51ONe+30yw78a5jvfbpU9Wqpmq +vOU0xJwnlH1GeMUcY8eMfOFocjG0yOtYeubvBIDLr0/AFzz9WHp+Z69RX7m53nUR +GDlyKQKBgDGZVAbUBiB8rerqNbONBAxfipoa4IJ+ntBrFT2DtoIZNbSzaoK+nVbH +kbWMJycaV5PVOh1lfAiZeWCxQz5RcZh/RS8USnxyMG1j4dP/wLcbdasI8uRaSC6Y +hFHL5HjhLrIo0HRWySS2b2ztBI2FP1M+MaaGFPHDzm2OyZg85yr3 +-----END RSA PRIVATE KEY----- diff --git a/vendor/pem-rfc7468/tests/examples/pkcs8-enc.der b/vendor/pem-rfc7468/tests/examples/pkcs8-enc.der Binary files differnew file mode 100644 index 0000000..5170c06 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs8-enc.der diff --git a/vendor/pem-rfc7468/tests/examples/pkcs8-enc.pem b/vendor/pem-rfc7468/tests/examples/pkcs8-enc.pem new file mode 100644 index 0000000..e5d3207 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs8-enc.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAh52YLnDfkaiAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEELLQLXiy79nf9pTPjgr0CSUEQNDN +bHcPS7hxdkIjBcF0AYCeImZ0znQYXSIb/aqVBpiQyIgvzgKwXUG8v1SwNVlbzUFU +syWTcIRpuGqs+IFaeys= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/vendor/pem-rfc7468/tests/examples/pkcs8.der b/vendor/pem-rfc7468/tests/examples/pkcs8.der Binary files differnew file mode 100644 index 0000000..0cfccc3 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs8.der diff --git a/vendor/pem-rfc7468/tests/examples/pkcs8.pem b/vendor/pem-rfc7468/tests/examples/pkcs8.pem new file mode 100644 index 0000000..0c0ee10 --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/pkcs8.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF +-----END PRIVATE KEY----- diff --git a/vendor/pem-rfc7468/tests/examples/ssh_rsa_pem_password.pem b/vendor/pem-rfc7468/tests/examples/ssh_rsa_pem_password.pem new file mode 100644 index 0000000..92084bd --- /dev/null +++ b/vendor/pem-rfc7468/tests/examples/ssh_rsa_pem_password.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,15670D76FD184D46C40C971733E0543F + +/lVaZdZ2a6x5d3b96F6XpFzcnP35pUrwpKxEB07nF15Jc81jwEAg72OpFTp5QRMu +WXbbZ/dKF7ucGHvLQ/VvCNkbl6oqowSme94fzFsa/xuKRHAGDHVi/TQylIOBBJFv +vru/3EZkO8mAQRDTNfuSl0Y5Ir7uqAQy0E/xKfOdY73BO4//KEDEIshRwBxbOm3K +D2sU1Kp8RnnBgSNydG8AH/LrtBnFs9HWrb9JD0Nj5bIxZDzil5CYmTB8PRgb2Qy7 +bckVc+0Y/h8Ai+NjSc/rJVw0smKJbmNSoPyJH1WjDPW8wWtngCFQWaVCTubm08N1 +nqrzIclT3fnq8YFSbFJYZVPaADxjv2HW7dLH7grYqXx/5FTE34ixXTcwQ1KR0ZQX +uaGZhkiDVWf/q82JPREqH5hwbeGL9QwZHF4/74vKIsddEuVFp8EW7jn9INRoVBtK +/OBiVXmVELFhmVBqvQU7GSci7+fCntXIx6W3hGiJL2WyXfuP16u9BhF5kd+c3pAm +tOZ3Lc5XsceBIYq0rKhy7rDhEg0V8wF1jHeeiW0VKDt2cFePSAd4CIbHiRWbvwh+ +zIoNAB34k4cYShmjOHKem9FMHVHSwfRE39Vrwssj0HWVOp7KdXYv64w4Ywmn6wvA +r6p8IZWg7KqA5UApPpiBVs0BAx1KtZk3o1dvXAazklw23icnnZF6XqaH6EmnVsf9 +gbyK1NcH3lIalTYhs+hMwizkw/XDb1uU8G7Rz1QFKBiL56J8ePIA2NWRUwvdMEAv +rZXSq4Icwy566GIqdtMRNLcz6LthNEg9qg+fD5aGLrtTk8ACSQpb/ELMMzqDVTkI +07dB1Nhzx9nd9mUlIuA030I5w7f//5pS6/lGmmPZblygY1PBludl+p/P9OKJ+Jr0 +HTAI4SVxoYdp6YHDBJ9J7Wt6UnIe+/3WarY9d9X1XNGOE4K+nRFihSShtKHDtMY6 +eBEV1sBTXJ1KANG683CU+uDx2XpOVAwDGl5hyRdzOovNC1iWjSu+CvppDvZLuIMj +zIllu5E8PR2Zd1wIT1gnU/7HiVdM0m4jf6ptkGSWNSCLA0ipii0YYarXoyu0kbMY +BKKpp5QRXv6OwmSDMwQTPuRIWyk839X1ABE1XeTKt43Ns+Wtdboi8Cu/aO/Z5AoA +gbJ+CdyKJIJxDXA11cPq9SF2daYmqHV3agrrKmAwWBRwpCKvotv0Hxw2M1+91ZoU +NY52RraoNVQPOAEfhYNS0ltVPzxcDU5bA2WczO6QzmMl7So6dysw+fxtxaEUGt4m +Fj+p+rE64Okq4wWDlEQya/xu4KMZwzyDncgJHHyYahs+vCv9KbQLW8R0iHTbxQzX +Vhomq++Cm8kg5aA/UsLas/l6ZyfNIcA99U8shFFA5urOKMl/jSRd9v1c7H3nOPZ7 ++eN10E7hcRruwOkoBlpd2It3Y2M+1qBDWXLVSHSXmIuzdE+MZ8CZfvxe+FcfpvJU +BFsZbSEF2PQC+zhd1HjV6DUe3jCz88/rjUnXQCvEJ7z7Tuz3C7kKdR3OYYYLwuLW +LTy2VS0p3QuUeMnNRl0HxpB16BZax9mzFr0UvFKp2QQYzOkIghg2sLNEbtaJvHNh +-----END RSA PRIVATE KEY----- |