summaryrefslogtreecommitdiffstats
path: root/vendor/pem-rfc7468
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/pem-rfc7468')
-rw-r--r--vendor/pem-rfc7468/.cargo-checksum.json1
-rw-r--r--vendor/pem-rfc7468/CHANGELOG.md115
-rw-r--r--vendor/pem-rfc7468/Cargo.toml58
-rw-r--r--vendor/pem-rfc7468/LICENSE-APACHE201
-rw-r--r--vendor/pem-rfc7468/LICENSE-MIT25
-rw-r--r--vendor/pem-rfc7468/README.md101
-rw-r--r--vendor/pem-rfc7468/src/decoder.rs270
-rw-r--r--vendor/pem-rfc7468/src/encoder.rs299
-rw-r--r--vendor/pem-rfc7468/src/error.rs107
-rw-r--r--vendor/pem-rfc7468/src/grammar.rs232
-rw-r--r--vendor/pem-rfc7468/src/lib.rs122
-rw-r--r--vendor/pem-rfc7468/tests/decode.rs112
-rw-r--r--vendor/pem-rfc7468/tests/encode.rs21
-rw-r--r--vendor/pem-rfc7468/tests/examples/chosen_header.pem31
-rw-r--r--vendor/pem-rfc7468/tests/examples/ed25519_id.pem5
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs1.derbin0 -> 1191 bytes
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs1.pem27
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs1_with_preceeding_junk.pem44
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs8-enc.derbin0 -> 158 bytes
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs8-enc.pem6
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs8.derbin0 -> 48 bytes
-rw-r--r--vendor/pem-rfc7468/tests/examples/pkcs8.pem3
-rw-r--r--vendor/pem-rfc7468/tests/examples/ssh_rsa_pem_password.pem30
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
new file mode 100644
index 0000000..bbf1876
--- /dev/null
+++ b/vendor/pem-rfc7468/tests/examples/pkcs1.der
Binary files differ
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
new file mode 100644
index 0000000..5170c06
--- /dev/null
+++ b/vendor/pem-rfc7468/tests/examples/pkcs8-enc.der
Binary files differ
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
new file mode 100644
index 0000000..0cfccc3
--- /dev/null
+++ b/vendor/pem-rfc7468/tests/examples/pkcs8.der
Binary files differ
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-----