chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"2f011e3d1fa9511b8e130b2394eff79728526e2549807998941a47fffc8c750b","CHANGELOG.md":"c6ad0693c5a6cf17d0e251451b5597ee33ea6e08a0635de8927cda35239ba982","Cargo.toml":"88c9f826dd390af0f176cd074ea3d27be8a63e6b5671f77db699daa7df3b121e","Cargo.toml.orig":"a46adb3039398c7262d7713b30db8ecc308d832e51b3778a61a441e47c047962","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"90c503b61dee04e1449c323ec34c229dfb68d7adcb96c7e140ee55f70fce2d8e","README.md":"4c83d87cb45d8adc72c0cddcf445389e1d1be72b95c27d71dfeb1f937ad2abfc","src/decoder.rs":"0e58986dd9dfb4bd948668efa5fdd3379908c5316e72165db58d1b7f8b87d8a6","src/encoder.rs":"477dc159381c889e5b4523ec7bc51b474965d890eeaf5917e007ebe6e9fd25fd","src/error.rs":"77031fac7c4b4762305e80b044a287dc94d5d93e8e1f5efb0b06bb2794a6e64d","src/grammar.rs":"b2078760aa850738117438e38533cbc17dd96fdc5738fd9cdce3a2c027ebf783","src/lib.rs":"a390b39b5166b526db31a10cae08643e735ba0734f7e92c102608881b7c173b8","tests/decode.rs":"fda063622f13dcf308f3303b796e90bb6579dbbb01a3c64157ce94418a4648da","tests/encode.rs":"7f6479363a92eda9d8ac3fdf427aa5920d6701f7706f2a76c6cb9a36c8087ee8","tests/examples/chosen_header.pem":"76da209faa6a64bf9dfac228a1a3ccac63544a94ef9a16a6c295a35c995c4016","tests/examples/ed25519_id.pem":"01a9f4e326cdc04e0580efc23e8fcaa2c0872b48fede39f7acb7bc0bf99ff129","tests/examples/pkcs1.der":"f94b60300e4877e863b2bea8d5c366a90432794454c05e0ec098ddbf96263614","tests/examples/pkcs1.pem":"026c0bc90e1d06dc828a90dc4fcfb29d5b4cd0eeb93123047035804092569086","tests/examples/pkcs1_with_preceeding_junk.pem":"4502747df2e3816e941989d5d63b6d3dadea50f0d38035992edcc6776e242605","tests/examples/pkcs8-enc.der":"72ea607c5f0e560f68ee914dca8a5a74bfa22a667333656864f7938960acd302","tests/examples/pkcs8-enc.pem":"0c8bd6713af3f28392f684c5ba8b1bd842d8b483d00c136cb1fb24976aab447a","tests/examples/pkcs8.der":"c1c3b09c4d18e096209edf25bc8dfc2f11dc29896085e49e5f4e487fbd97dbb6","tests/examples/pkcs8.pem":"8e39c38052cd63ab3870831a9f6cab76645426ca21ef77e24e2ead445aa4df16","tests/examples/ssh_rsa_pem_password.pem":"e1e2e46d0100cbfc13205c1c8a667d4139ea74d4e664cff9228175ad0e9ded52"},"package":"88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "66e782b01d58aeea7dd57213c2e262211e98a9c0"
},
"path_in_vcs": "pem-rfc7468"
}

115
vendor/pem-rfc7468/CHANGELOG.md vendored Normal file
View File

@@ -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

58
vendor/pem-rfc7468/Cargo.toml vendored Normal file
View File

@@ -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",
]

201
vendor/pem-rfc7468/LICENSE-APACHE vendored Normal file
View File

@@ -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.

25
vendor/pem-rfc7468/LICENSE-MIT vendored Normal file
View File

@@ -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.

101
vendor/pem-rfc7468/README.md vendored Normal file
View File

@@ -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

270
vendor/pem-rfc7468/src/decoder.rs vendored Normal file
View File

@@ -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
]
);
}
}

299
vendor/pem-rfc7468/src/encoder.rs vendored Normal file
View File

@@ -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(())
}
}

107
vendor/pem-rfc7468/src/error.rs vendored Normal file
View File

@@ -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)
}
}

232
vendor/pem-rfc7468/src/grammar.rs vendored Normal file
View File

@@ -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));
}
}

122
vendor/pem-rfc7468/src/lib.rs vendored Normal file
View File

@@ -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,
})
}
}
}

Binary file not shown.

View File

@@ -0,0 +1 @@
{"name":"pem-rfc7468","vers":"0.7.0","deps":[{"name":"base64ct","req":"^1.4","features":[],"optional":false,"default_features":true,"target":null,"kind":"normal","registry":"https://github.com/rust-lang/crates.io-index","package":null,"public":null,"artifact":null,"bindep_target":null,"lib":false}],"features":{"alloc":["base64ct/alloc"],"std":["alloc","base64ct/std"]},"features2":null,"cksum":"cebae415aaec881573b652b478f9cbca99d12de7bd3838a63f51ec63c11fccba","yanked":null,"links":null,"rust_version":null,"v":2}

112
vendor/pem-rfc7468/tests/decode.rs vendored Normal file
View File

@@ -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");
}

21
vendor/pem-rfc7468/tests/encode.rs vendored Normal file
View File

@@ -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"));
}

View File

@@ -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-----

View File

@@ -0,0 +1,5 @@
-----BEGIN ED25519 CERT-----
AQQABrknAdj5BeHBAd0mq1KD3ABvDzpBvUD0zU88DASbkRuV0WiaAQAgBADPc8aR
rUUolIsrKFMKy7SVCxKvpGrcdFAni+Bah1WZHnac5JP3LnPc2/0G7dTSlSTeBk5k
XqIySdIqtfYbW0kQinA0PaxDzzX5g1q3CclY9lNTAglR5fP71kunXh7ntwk=
-----END ED25519 CERT-----

Binary file not shown.

View File

@@ -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-----

View File

@@ -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-----

Binary file not shown.

View File

@@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAh52YLnDfkaiAICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEELLQLXiy79nf9pTPjgr0CSUEQNDN
bHcPS7hxdkIjBcF0AYCeImZ0znQYXSIb/aqVBpiQyIgvzgKwXUG8v1SwNVlbzUFU
syWTcIRpuGqs+IFaeys=
-----END ENCRYPTED PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIBftnHPp22SewYmmEoMcX8VwI4IHwaqd+9LFPj/15eqF
-----END PRIVATE KEY-----

View File

@@ -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-----