From 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:47:55 +0200 Subject: Adding upstream version 0.70.1+ds1. Signed-off-by: Daniel Baumann --- vendor/sec1/.cargo-checksum.json | 1 + vendor/sec1/CHANGELOG.md | 92 ++++ vendor/sec1/Cargo.toml | 120 +++++ vendor/sec1/LICENSE-APACHE | 201 ++++++++ vendor/sec1/LICENSE-MIT | 25 + vendor/sec1/README.md | 58 +++ vendor/sec1/src/error.rs | 82 ++++ vendor/sec1/src/lib.rs | 77 +++ vendor/sec1/src/parameters.rs | 75 +++ vendor/sec1/src/point.rs | 776 +++++++++++++++++++++++++++++++ vendor/sec1/src/private_key.rs | 177 +++++++ vendor/sec1/src/traits.rs | 122 +++++ vendor/sec1/tests/examples/p256-priv.der | Bin 0 -> 121 bytes vendor/sec1/tests/examples/p256-priv.pem | 5 + vendor/sec1/tests/private_key.rs | 43 ++ vendor/sec1/tests/traits.rs | 100 ++++ 16 files changed, 1954 insertions(+) create mode 100644 vendor/sec1/.cargo-checksum.json create mode 100644 vendor/sec1/CHANGELOG.md create mode 100644 vendor/sec1/Cargo.toml create mode 100644 vendor/sec1/LICENSE-APACHE create mode 100644 vendor/sec1/LICENSE-MIT create mode 100644 vendor/sec1/README.md create mode 100644 vendor/sec1/src/error.rs create mode 100644 vendor/sec1/src/lib.rs create mode 100644 vendor/sec1/src/parameters.rs create mode 100644 vendor/sec1/src/point.rs create mode 100644 vendor/sec1/src/private_key.rs create mode 100644 vendor/sec1/src/traits.rs create mode 100644 vendor/sec1/tests/examples/p256-priv.der create mode 100644 vendor/sec1/tests/examples/p256-priv.pem create mode 100644 vendor/sec1/tests/private_key.rs create mode 100644 vendor/sec1/tests/traits.rs (limited to 'vendor/sec1') diff --git a/vendor/sec1/.cargo-checksum.json b/vendor/sec1/.cargo-checksum.json new file mode 100644 index 0000000..0918c18 --- /dev/null +++ b/vendor/sec1/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{},"package":"d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"} \ No newline at end of file diff --git a/vendor/sec1/CHANGELOG.md b/vendor/sec1/CHANGELOG.md new file mode 100644 index 0000000..44a0fc9 --- /dev/null +++ b/vendor/sec1/CHANGELOG.md @@ -0,0 +1,92 @@ +# 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.3 (2023-07-16) +### Added +- Impl `Hash` for `EncodedPoint` ([#1102]) + +[#1102]: https://github.com/RustCrypto/formats/pull/1102 + +## 0.7.2 (2023-04-09) +### Added +- Impl `ModulusSize` for `U24` ([#995]) + +[#995]: https://github.com/RustCrypto/formats/pull/995 + +## 0.7.1 (2023-02-27) +### Fixed +- Encode `ECPrivateKey` version ([#908]) + +[#908]: https://github.com/RustCrypto/formats/pull/908 + +## 0.7.0 (2023-02-26) [YANKED] +### Changed +- MSRV 1.65 ([#805]) +- Bump `serdect` to v0.2 ([#893]) +- Bump `der` dependency to v0.7 ([#899]) +- Bump `spki` dependency to v0.7 ([#900]) +- Bump `pkcs8` to v0.10 ([#902]) + +[#805]: https://github.com/RustCrypto/formats/pull/805 +[#893]: https://github.com/RustCrypto/formats/pull/893 +[#899]: https://github.com/RustCrypto/formats/pull/899 +[#900]: https://github.com/RustCrypto/formats/pull/900 +[#902]: https://github.com/RustCrypto/formats/pull/902 + +## 0.6.0 (Skipped) +- Skipped to synchronize version number with `der` and `spki` + +## 0.5.0 (Skipped) +- Skipped to synchronize version number with `der` and `spki` + +## 0.4.0 (Skipped) +- Skipped to synchronize version number with `der` and `spki` + +## 0.3.0 (2022-05-08) +### Added +- Make `der` feature optional but on-by-default ([#497]) +- Make `point` feature optional but on-by-default ([#516]) + +### Changed +- Use `base16ct` and `serdect` crates ([#648]) +- Bump `der` to v0.6 ([#653]) +- Bump `pkcs8` to v0.9 ([#656]) + +[#497]: https://github.com/RustCrypto/formats/pull/497 +[#516]: https://github.com/RustCrypto/formats/pull/516 +[#648]: https://github.com/RustCrypto/formats/pull/648 +[#653]: https://github.com/RustCrypto/formats/pull/653 +[#656]: https://github.com/RustCrypto/formats/pull/656 + +## 0.2.1 (2021-11-18) +### Added +- `serde` feature ([#248]) +- Hexadecimal serialization/deserialization support for `EncodedPoint` ([#248]) + +[#248]: https://github.com/RustCrypto/formats/pull/248 + +## 0.2.0 (2021-11-17) [YANKED] +### Added +- `pkcs8` feature ([#229]) + +### Changed +- Rename `From/ToEcPrivateKey` => `DecodeEcPrivateKey`/`EncodeEcPrivateKey` ([#122]) +- Use `der::Document` to impl `EcPrivateKeyDocument` ([#133]) +- Rust 2021 edition upgrade; MSRV 1.56 ([#136]) +- Bump `der` crate dependency to v0.5 ([#222]) + +### Removed +- I/O related errors ([#158]) + +[#122]: https://github.com/RustCrypto/formats/pull/122 +[#133]: https://github.com/RustCrypto/formats/pull/133 +[#136]: https://github.com/RustCrypto/formats/pull/136 +[#158]: https://github.com/RustCrypto/formats/pull/158 +[#222]: https://github.com/RustCrypto/formats/pull/222 +[#229]: https://github.com/RustCrypto/formats/pull/229 + +## 0.1.0 (2021-09-22) +- Initial release diff --git a/vendor/sec1/Cargo.toml b/vendor/sec1/Cargo.toml new file mode 100644 index 0000000..c640dea --- /dev/null +++ b/vendor/sec1/Cargo.toml @@ -0,0 +1,120 @@ +# 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.65" +name = "sec1" +version = "0.7.3" +authors = ["RustCrypto Developers"] +description = """ +Pure Rust implementation of SEC1: Elliptic Curve Cryptography encoding formats +including ASN.1 DER-serialized private keys as well as the +Elliptic-Curve-Point-to-Octet-String encoding +""" +readme = "README.md" +keywords = [ + "crypto", + "key", + "elliptic-curve", + "secg", +] +categories = [ + "cryptography", + "data-structures", + "encoding", + "no-std", + "parser-implementations", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/formats/tree/master/sec1" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[dependencies.base16ct] +version = "0.2" +optional = true +default-features = false + +[dependencies.der] +version = "0.7" +features = ["oid"] +optional = true + +[dependencies.generic-array] +version = "0.14.7" +optional = true +default-features = false + +[dependencies.pkcs8] +version = "0.10" +optional = true +default-features = false + +[dependencies.serdect] +version = "0.2" +features = ["alloc"] +optional = true +default-features = false + +[dependencies.subtle] +version = "2" +optional = true +default-features = false + +[dependencies.zeroize] +version = "1" +optional = true +default-features = false + +[dev-dependencies.hex-literal] +version = "0.4" + +[dev-dependencies.tempfile] +version = "3" + +[features] +alloc = [ + "der?/alloc", + "pkcs8?/alloc", + "zeroize?/alloc", +] +default = [ + "der", + "point", +] +der = [ + "dep:der", + "zeroize", +] +pem = [ + "alloc", + "der/pem", + "pkcs8/pem", +] +point = [ + "dep:base16ct", + "dep:generic-array", +] +serde = ["dep:serdect"] +std = [ + "alloc", + "der?/std", +] +zeroize = [ + "dep:zeroize", + "der?/zeroize", +] diff --git a/vendor/sec1/LICENSE-APACHE b/vendor/sec1/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/vendor/sec1/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/sec1/LICENSE-MIT b/vendor/sec1/LICENSE-MIT new file mode 100644 index 0000000..68ddaa3 --- /dev/null +++ b/vendor/sec1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2021-2022 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/sec1/README.md b/vendor/sec1/README.md new file mode 100644 index 0000000..5678de6 --- /dev/null +++ b/vendor/sec1/README.md @@ -0,0 +1,58 @@ +# [RustCrypto]: SEC1 Elliptic Curve Cryptography Formats + +[![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] + +[Documentation][docs-link] + +## About + +Pure Rust implementation of [SEC1: Elliptic Curve Cryptography] encoding +formats including ASN.1 DER-serialized private keys (also described in +[RFC5915]) as well as the `Elliptic-Curve-Point-to-Octet-String` and +`Octet-String-to-Elliptic-Curve-Point` encoding algorithms. + +## Minimum Supported Rust Version + +This crate requires **Rust 1.65** 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/sec1 +[crate-link]: https://crates.io/crates/sec1 +[docs-image]: https://docs.rs/sec1/badge.svg +[docs-link]: https://docs.rs/sec1/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats +[build-image]: https://github.com/RustCrypto/formats/workflows/sec1/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/formats/actions + +[//]: # (links) + +[RustCrypto]: https://github.com/rustcrypto +[SEC1: Elliptic Curve Cryptography]: https://www.secg.org/sec1-v2.pdf +[RFC5915]: https://datatracker.ietf.org/doc/html/rfc5915 diff --git a/vendor/sec1/src/error.rs b/vendor/sec1/src/error.rs new file mode 100644 index 0000000..0d8bc8b --- /dev/null +++ b/vendor/sec1/src/error.rs @@ -0,0 +1,82 @@ +//! Error types + +use core::fmt; + +#[cfg(feature = "pem")] +use der::pem; + +/// Result type with `sec1` crate's [`Error`] type. +pub type Result = core::result::Result; + +/// Error type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum Error { + /// ASN.1 DER-related errors. + #[cfg(feature = "der")] + Asn1(der::Error), + + /// Cryptographic errors. + /// + /// These can be used by EC implementations to signal that a key is + /// invalid for cryptographic reasons. This means the document parsed + /// correctly, but one of the values contained within was invalid, e.g. + /// a number expected to be a prime was not a prime. + Crypto, + + /// PKCS#8 errors. + #[cfg(feature = "pkcs8")] + Pkcs8(pkcs8::Error), + + /// Errors relating to the `Elliptic-Curve-Point-to-Octet-String` or + /// `Octet-String-to-Elliptic-Curve-Point` encodings. + PointEncoding, + + /// Version errors + Version, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + #[cfg(feature = "der")] + Error::Asn1(err) => write!(f, "SEC1 ASN.1 error: {}", err), + Error::Crypto => f.write_str("SEC1 cryptographic error"), + #[cfg(feature = "pkcs8")] + Error::Pkcs8(err) => write!(f, "{}", err), + Error::PointEncoding => f.write_str("elliptic curve point encoding error"), + Error::Version => f.write_str("SEC1 version error"), + } + } +} + +#[cfg(feature = "der")] +impl From for Error { + fn from(err: der::Error) -> Error { + Error::Asn1(err) + } +} + +#[cfg(feature = "pem")] +impl From for Error { + fn from(err: pem::Error) -> Error { + der::Error::from(err).into() + } +} + +#[cfg(feature = "pkcs8")] +impl From for Error { + fn from(err: pkcs8::Error) -> Error { + Error::Pkcs8(err) + } +} + +#[cfg(feature = "pkcs8")] +impl From for Error { + fn from(err: pkcs8::spki::Error) -> Error { + Error::Pkcs8(pkcs8::Error::PublicKey(err)) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} diff --git a/vendor/sec1/src/lib.rs b/vendor/sec1/src/lib.rs new file mode 100644 index 0000000..f6b4190 --- /dev/null +++ b/vendor/sec1/src/lib.rs @@ -0,0 +1,77 @@ +#![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" +)] +#![forbid(unsafe_code)] +#![warn( + clippy::mod_module_files, + clippy::unwrap_used, + missing_docs, + rust_2018_idioms, + unused_qualifications +)] + +//! ## `serde` support +//! +//! When the `serde` feature of this crate is enabled, the [`EncodedPoint`] +//! type receives impls of [`serde::Serialize`] and [`serde::Deserialize`]. +//! +//! Additionally, when both the `alloc` and `serde` features are enabled, the +//! serializers/deserializers will autodetect if a "human friendly" textual +//! encoding is being used, and if so encode the points as hexadecimal. + +#[cfg(feature = "alloc")] +#[allow(unused_extern_crates)] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "point")] +pub mod point; + +mod error; +#[cfg(feature = "der")] +mod parameters; +#[cfg(feature = "der")] +mod private_key; +#[cfg(feature = "der")] +mod traits; + +#[cfg(feature = "der")] +pub use der; + +pub use crate::error::{Error, Result}; + +#[cfg(feature = "point")] +pub use crate::point::EncodedPoint; + +#[cfg(feature = "point")] +pub use generic_array::typenum::consts; + +#[cfg(feature = "der")] +pub use crate::{parameters::EcParameters, private_key::EcPrivateKey, traits::DecodeEcPrivateKey}; + +#[cfg(all(feature = "alloc", feature = "der"))] +pub use crate::traits::EncodeEcPrivateKey; + +#[cfg(feature = "pem")] +pub use der::pem::{self, LineEnding}; + +#[cfg(feature = "pkcs8")] +pub use pkcs8; + +#[cfg(feature = "pkcs8")] +use pkcs8::ObjectIdentifier; + +#[cfg(all(doc, feature = "serde"))] +use serdect::serde; + +/// Algorithm [`ObjectIdentifier`] for elliptic curve public key cryptography +/// (`id-ecPublicKey`). +/// +/// +#[cfg(feature = "pkcs8")] +pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"); diff --git a/vendor/sec1/src/parameters.rs b/vendor/sec1/src/parameters.rs new file mode 100644 index 0000000..20458e6 --- /dev/null +++ b/vendor/sec1/src/parameters.rs @@ -0,0 +1,75 @@ +use der::{ + asn1::{AnyRef, ObjectIdentifier}, + DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, Tag, Writer, +}; + +/// Elliptic curve parameters as described in +/// [RFC5480 Section 2.1.1](https://datatracker.ietf.org/doc/html/rfc5480#section-2.1.1): +/// +/// ```text +/// ECParameters ::= CHOICE { +/// namedCurve OBJECT IDENTIFIER +/// -- implicitCurve NULL +/// -- specifiedCurve SpecifiedECDomain +/// } +/// -- implicitCurve and specifiedCurve MUST NOT be used in PKIX. +/// -- Details for SpecifiedECDomain can be found in [X9.62]. +/// -- Any future additions to this CHOICE should be coordinated +/// -- with ANSI X9. +/// ``` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum EcParameters { + /// Elliptic curve named by a particular OID. + /// + /// > namedCurve identifies all the required values for a particular + /// > set of elliptic curve domain parameters to be represented by an + /// > object identifier. + NamedCurve(ObjectIdentifier), +} + +impl<'a> DecodeValue<'a> for EcParameters { + fn decode_value>(decoder: &mut R, header: Header) -> der::Result { + ObjectIdentifier::decode_value(decoder, header).map(Self::NamedCurve) + } +} + +impl EncodeValue for EcParameters { + fn value_len(&self) -> der::Result { + match self { + Self::NamedCurve(oid) => oid.value_len(), + } + } + + fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { + match self { + Self::NamedCurve(oid) => oid.encode_value(writer), + } + } +} + +impl EcParameters { + /// Obtain the `namedCurve` OID. + pub fn named_curve(self) -> Option { + match self { + Self::NamedCurve(oid) => Some(oid), + } + } +} + +impl<'a> From<&'a EcParameters> for AnyRef<'a> { + fn from(params: &'a EcParameters) -> AnyRef<'a> { + match params { + EcParameters::NamedCurve(oid) => oid.into(), + } + } +} + +impl From for EcParameters { + fn from(oid: ObjectIdentifier) -> EcParameters { + EcParameters::NamedCurve(oid) + } +} + +impl FixedTag for EcParameters { + const TAG: Tag = Tag::ObjectIdentifier; +} diff --git a/vendor/sec1/src/point.rs b/vendor/sec1/src/point.rs new file mode 100644 index 0000000..818f5bd --- /dev/null +++ b/vendor/sec1/src/point.rs @@ -0,0 +1,776 @@ +//! Support for the SEC1 `Elliptic-Curve-Point-to-Octet-String` and +//! `Octet-String-to-Elliptic-Curve-Point` encoding algorithms. +//! +//! Described in [SEC1: Elliptic Curve Cryptography] (Version 2.0) section 2.3.3 (p.10). +//! +//! [SEC1: Elliptic Curve Cryptography]: https://www.secg.org/sec1-v2.pdf + +use crate::{Error, Result}; +use base16ct::HexDisplay; +use core::{ + cmp::Ordering, + fmt::{self, Debug}, + hash::{Hash, Hasher}, + ops::Add, + str, +}; +use generic_array::{ + typenum::{U1, U24, U28, U32, U48, U66}, + ArrayLength, GenericArray, +}; + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +#[cfg(feature = "serde")] +use serdect::serde::{de, ser, Deserialize, Serialize}; + +#[cfg(feature = "subtle")] +use subtle::{Choice, ConditionallySelectable}; + +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + +/// Trait for supported modulus sizes which precomputes the typenums for +/// various point encodings so they don't need to be included as bounds. +// TODO(tarcieri): replace this all with const generic expressions. +pub trait ModulusSize: 'static + ArrayLength + Copy + Debug { + /// Size of a compressed point for the given elliptic curve when encoded + /// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm + /// (including leading `0x02` or `0x03` tag byte). + type CompressedPointSize: 'static + ArrayLength + Copy + Debug; + + /// Size of an uncompressed point for the given elliptic curve when encoded + /// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm + /// (including leading `0x04` tag byte). + type UncompressedPointSize: 'static + ArrayLength + Copy + Debug; + + /// Size of an untagged point for given elliptic curve, i.e. size of two + /// serialized base field elements. + type UntaggedPointSize: 'static + ArrayLength + Copy + Debug; +} + +macro_rules! impl_modulus_size { + ($($size:ty),+) => { + $(impl ModulusSize for $size { + type CompressedPointSize = <$size as Add>::Output; + type UncompressedPointSize = >::Output; + type UntaggedPointSize = <$size as Add>::Output; + })+ + } +} + +impl_modulus_size!(U24, U28, U32, U48, U66); + +/// SEC1 encoded curve point. +/// +/// This type is an enum over the compressed and uncompressed encodings, +/// useful for cases where either encoding can be supported, or conversions +/// between the two forms. +#[derive(Clone, Default)] +pub struct EncodedPoint +where + Size: ModulusSize, +{ + bytes: GenericArray, +} + +#[allow(clippy::len_without_is_empty)] +impl EncodedPoint +where + Size: ModulusSize, +{ + /// Decode elliptic curve point (compressed or uncompressed) from the + /// `Elliptic-Curve-Point-to-Octet-String` encoding described in + /// SEC 1: Elliptic Curve Cryptography (Version 2.0) section + /// 2.3.3 (page 10). + /// + /// + pub fn from_bytes(input: impl AsRef<[u8]>) -> Result { + let input = input.as_ref(); + + // Validate tag + let tag = input + .first() + .cloned() + .ok_or(Error::PointEncoding) + .and_then(Tag::from_u8)?; + + // Validate length + let expected_len = tag.message_len(Size::to_usize()); + + if input.len() != expected_len { + return Err(Error::PointEncoding); + } + + let mut bytes = GenericArray::default(); + bytes[..expected_len].copy_from_slice(input); + Ok(Self { bytes }) + } + + /// Decode elliptic curve point from raw uncompressed coordinates, i.e. + /// encoded as the concatenated `x || y` coordinates with no leading SEC1 + /// tag byte (which would otherwise be `0x04` for an uncompressed point). + pub fn from_untagged_bytes(bytes: &GenericArray) -> Self { + let (x, y) = bytes.split_at(Size::to_usize()); + Self::from_affine_coordinates(x.into(), y.into(), false) + } + + /// Encode an elliptic curve point from big endian serialized coordinates + /// (with optional point compression) + pub fn from_affine_coordinates( + x: &GenericArray, + y: &GenericArray, + compress: bool, + ) -> Self { + let tag = if compress { + Tag::compress_y(y.as_slice()) + } else { + Tag::Uncompressed + }; + + let mut bytes = GenericArray::default(); + bytes[0] = tag.into(); + bytes[1..(Size::to_usize() + 1)].copy_from_slice(x); + + if !compress { + bytes[(Size::to_usize() + 1)..].copy_from_slice(y); + } + + Self { bytes } + } + + /// Return [`EncodedPoint`] representing the additive identity + /// (a.k.a. point at infinity) + pub fn identity() -> Self { + Self::default() + } + + /// Get the length of the encoded point in bytes + pub fn len(&self) -> usize { + self.tag().message_len(Size::to_usize()) + } + + /// Get byte slice containing the serialized [`EncodedPoint`]. + pub fn as_bytes(&self) -> &[u8] { + &self.bytes[..self.len()] + } + + /// Get boxed byte slice containing the serialized [`EncodedPoint`] + #[cfg(feature = "alloc")] + pub fn to_bytes(&self) -> Box<[u8]> { + self.as_bytes().to_vec().into_boxed_slice() + } + + /// Is this [`EncodedPoint`] compact? + pub fn is_compact(&self) -> bool { + self.tag().is_compact() + } + + /// Is this [`EncodedPoint`] compressed? + pub fn is_compressed(&self) -> bool { + self.tag().is_compressed() + } + + /// Is this [`EncodedPoint`] the additive identity? (a.k.a. point at infinity) + pub fn is_identity(&self) -> bool { + self.tag().is_identity() + } + + /// Compress this [`EncodedPoint`], returning a new [`EncodedPoint`]. + pub fn compress(&self) -> Self { + match self.coordinates() { + Coordinates::Compressed { .. } + | Coordinates::Compact { .. } + | Coordinates::Identity => self.clone(), + Coordinates::Uncompressed { x, y } => Self::from_affine_coordinates(x, y, true), + } + } + + /// Get the SEC1 tag for this [`EncodedPoint`] + pub fn tag(&self) -> Tag { + // Tag is ensured valid by the constructor + Tag::from_u8(self.bytes[0]).expect("invalid tag") + } + + /// Get the [`Coordinates`] for this [`EncodedPoint`]. + #[inline] + pub fn coordinates(&self) -> Coordinates<'_, Size> { + if self.is_identity() { + return Coordinates::Identity; + } + + let (x, y) = self.bytes[1..].split_at(Size::to_usize()); + + if self.is_compressed() { + Coordinates::Compressed { + x: x.into(), + y_is_odd: self.tag() as u8 & 1 == 1, + } + } else if self.is_compact() { + Coordinates::Compact { x: x.into() } + } else { + Coordinates::Uncompressed { + x: x.into(), + y: y.into(), + } + } + } + + /// Get the x-coordinate for this [`EncodedPoint`]. + /// + /// Returns `None` if this point is the identity point. + pub fn x(&self) -> Option<&GenericArray> { + match self.coordinates() { + Coordinates::Identity => None, + Coordinates::Compressed { x, .. } => Some(x), + Coordinates::Uncompressed { x, .. } => Some(x), + Coordinates::Compact { x } => Some(x), + } + } + + /// Get the y-coordinate for this [`EncodedPoint`]. + /// + /// Returns `None` if this point is compressed or the identity point. + pub fn y(&self) -> Option<&GenericArray> { + match self.coordinates() { + Coordinates::Compressed { .. } | Coordinates::Identity => None, + Coordinates::Uncompressed { y, .. } => Some(y), + Coordinates::Compact { .. } => None, + } + } +} + +impl AsRef<[u8]> for EncodedPoint +where + Size: ModulusSize, +{ + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +#[cfg(feature = "subtle")] +impl ConditionallySelectable for EncodedPoint +where + Size: ModulusSize, + >::ArrayType: Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut bytes = GenericArray::default(); + + for (i, byte) in bytes.iter_mut().enumerate() { + *byte = u8::conditional_select(&a.bytes[i], &b.bytes[i], choice); + } + + Self { bytes } + } +} + +impl Copy for EncodedPoint +where + Size: ModulusSize, + >::ArrayType: Copy, +{ +} + +impl Debug for EncodedPoint +where + Size: ModulusSize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "EncodedPoint({:?})", self.coordinates()) + } +} + +impl Eq for EncodedPoint {} + +impl PartialEq for EncodedPoint +where + Size: ModulusSize, +{ + fn eq(&self, other: &Self) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl Hash for EncodedPoint +where + Size: ModulusSize, +{ + fn hash(&self, state: &mut H) { + self.as_bytes().hash(state) + } +} + +impl PartialOrd for EncodedPoint +where + Size: ModulusSize, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for EncodedPoint +where + Size: ModulusSize, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.as_bytes().cmp(other.as_bytes()) + } +} + +impl TryFrom<&[u8]> for EncodedPoint +where + Size: ModulusSize, +{ + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result { + Self::from_bytes(bytes) + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for EncodedPoint +where + Size: ModulusSize, +{ + fn zeroize(&mut self) { + self.bytes.zeroize(); + *self = Self::identity(); + } +} + +impl fmt::Display for EncodedPoint +where + Size: ModulusSize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:X}", self) + } +} + +impl fmt::LowerHex for EncodedPoint +where + Size: ModulusSize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:x}", HexDisplay(self.as_bytes())) + } +} + +impl fmt::UpperHex for EncodedPoint +where + Size: ModulusSize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:X}", HexDisplay(self.as_bytes())) + } +} + +/// Decode a SEC1-encoded point from hexadecimal. +/// +/// Upper and lower case hexadecimal are both accepted, however mixed case is +/// rejected. +impl str::FromStr for EncodedPoint +where + Size: ModulusSize, +{ + type Err = Error; + + fn from_str(hex: &str) -> Result { + let mut buf = GenericArray::::default(); + base16ct::mixed::decode(hex, &mut buf) + .map_err(|_| Error::PointEncoding) + .and_then(Self::from_bytes) + } +} + +#[cfg(feature = "serde")] +impl Serialize for EncodedPoint +where + Size: ModulusSize, +{ + fn serialize(&self, serializer: S) -> core::result::Result + where + S: ser::Serializer, + { + serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, Size> Deserialize<'de> for EncodedPoint +where + Size: ModulusSize, +{ + fn deserialize(deserializer: D) -> core::result::Result + where + D: de::Deserializer<'de>, + { + let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; + Self::from_bytes(bytes).map_err(de::Error::custom) + } +} + +/// Enum representing the coordinates of either compressed or uncompressed +/// SEC1-encoded elliptic curve points. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Coordinates<'a, Size: ModulusSize> { + /// Identity point (a.k.a. point at infinity) + Identity, + + /// Compact curve point + Compact { + /// x-coordinate + x: &'a GenericArray, + }, + + /// Compressed curve point + Compressed { + /// x-coordinate + x: &'a GenericArray, + + /// Is the y-coordinate odd? + y_is_odd: bool, + }, + + /// Uncompressed curve point + Uncompressed { + /// x-coordinate + x: &'a GenericArray, + + /// y-coordinate + y: &'a GenericArray, + }, +} + +impl<'a, Size: ModulusSize> Coordinates<'a, Size> { + /// Get the tag octet needed to encode this set of [`Coordinates`] + pub fn tag(&self) -> Tag { + match self { + Coordinates::Compact { .. } => Tag::Compact, + Coordinates::Compressed { y_is_odd, .. } => { + if *y_is_odd { + Tag::CompressedOddY + } else { + Tag::CompressedEvenY + } + } + Coordinates::Identity => Tag::Identity, + Coordinates::Uncompressed { .. } => Tag::Uncompressed, + } + } +} + +/// Tag byte used by the `Elliptic-Curve-Point-to-Octet-String` encoding. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum Tag { + /// Identity point (`0x00`) + Identity = 0, + + /// Compressed point with even y-coordinate (`0x02`) + CompressedEvenY = 2, + + /// Compressed point with odd y-coordinate (`0x03`) + CompressedOddY = 3, + + /// Uncompressed point (`0x04`) + Uncompressed = 4, + + /// Compact point (`0x05`) + Compact = 5, +} + +impl Tag { + /// Parse a tag value from a byte + pub fn from_u8(byte: u8) -> Result { + match byte { + 0 => Ok(Tag::Identity), + 2 => Ok(Tag::CompressedEvenY), + 3 => Ok(Tag::CompressedOddY), + 4 => Ok(Tag::Uncompressed), + 5 => Ok(Tag::Compact), + _ => Err(Error::PointEncoding), + } + } + + /// Is this point compact? + pub fn is_compact(self) -> bool { + matches!(self, Tag::Compact) + } + + /// Is this point compressed? + pub fn is_compressed(self) -> bool { + matches!(self, Tag::CompressedEvenY | Tag::CompressedOddY) + } + + /// Is this point the identity point? + pub fn is_identity(self) -> bool { + self == Tag::Identity + } + + /// Compute the expected total message length for a message prefixed + /// with this tag (including the tag byte), given the field element size + /// (in bytes) for a particular elliptic curve. + pub fn message_len(self, field_element_size: usize) -> usize { + 1 + match self { + Tag::Identity => 0, + Tag::CompressedEvenY | Tag::CompressedOddY => field_element_size, + Tag::Uncompressed => field_element_size * 2, + Tag::Compact => field_element_size, + } + } + + /// Compress the given y-coordinate, returning a `Tag::Compressed*` value + fn compress_y(y: &[u8]) -> Self { + // Is the y-coordinate odd in the SEC1 sense: `self mod 2 == 1`? + if y.as_ref().last().expect("empty y-coordinate") & 1 == 1 { + Tag::CompressedOddY + } else { + Tag::CompressedEvenY + } + } +} + +impl TryFrom for Tag { + type Error = Error; + + fn try_from(byte: u8) -> Result { + Self::from_u8(byte) + } +} + +impl From for u8 { + fn from(tag: Tag) -> u8 { + tag as u8 + } +} + +#[cfg(test)] +mod tests { + use super::{Coordinates, Tag}; + use core::str::FromStr; + use generic_array::{typenum::U32, GenericArray}; + use hex_literal::hex; + + #[cfg(feature = "alloc")] + use alloc::string::ToString; + + #[cfg(feature = "subtle")] + use subtle::ConditionallySelectable; + + type EncodedPoint = super::EncodedPoint; + + /// Identity point + const IDENTITY_BYTES: [u8; 1] = [0]; + + /// Example uncompressed point + const UNCOMPRESSED_BYTES: [u8; 65] = hex!("0411111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222"); + + /// Example compressed point: `UNCOMPRESSED_BYTES` after point compression + const COMPRESSED_BYTES: [u8; 33] = + hex!("021111111111111111111111111111111111111111111111111111111111111111"); + + #[test] + fn decode_compressed_point() { + // Even y-coordinate + let compressed_even_y_bytes = + hex!("020100000000000000000000000000000000000000000000000000000000000000"); + + let compressed_even_y = EncodedPoint::from_bytes(&compressed_even_y_bytes[..]).unwrap(); + + assert!(compressed_even_y.is_compressed()); + assert_eq!(compressed_even_y.tag(), Tag::CompressedEvenY); + assert_eq!(compressed_even_y.len(), 33); + assert_eq!(compressed_even_y.as_bytes(), &compressed_even_y_bytes[..]); + + assert_eq!( + compressed_even_y.coordinates(), + Coordinates::Compressed { + x: &hex!("0100000000000000000000000000000000000000000000000000000000000000").into(), + y_is_odd: false + } + ); + + assert_eq!( + compressed_even_y.x().unwrap(), + &hex!("0100000000000000000000000000000000000000000000000000000000000000").into() + ); + assert_eq!(compressed_even_y.y(), None); + + // Odd y-coordinate + let compressed_odd_y_bytes = + hex!("030200000000000000000000000000000000000000000000000000000000000000"); + + let compressed_odd_y = EncodedPoint::from_bytes(&compressed_odd_y_bytes[..]).unwrap(); + + assert!(compressed_odd_y.is_compressed()); + assert_eq!(compressed_odd_y.tag(), Tag::CompressedOddY); + assert_eq!(compressed_odd_y.len(), 33); + assert_eq!(compressed_odd_y.as_bytes(), &compressed_odd_y_bytes[..]); + + assert_eq!( + compressed_odd_y.coordinates(), + Coordinates::Compressed { + x: &hex!("0200000000000000000000000000000000000000000000000000000000000000").into(), + y_is_odd: true + } + ); + + assert_eq!( + compressed_odd_y.x().unwrap(), + &hex!("0200000000000000000000000000000000000000000000000000000000000000").into() + ); + assert_eq!(compressed_odd_y.y(), None); + } + + #[test] + fn decode_uncompressed_point() { + let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap(); + + assert!(!uncompressed_point.is_compressed()); + assert_eq!(uncompressed_point.tag(), Tag::Uncompressed); + assert_eq!(uncompressed_point.len(), 65); + assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]); + + assert_eq!( + uncompressed_point.coordinates(), + Coordinates::Uncompressed { + x: &hex!("1111111111111111111111111111111111111111111111111111111111111111").into(), + y: &hex!("2222222222222222222222222222222222222222222222222222222222222222").into() + } + ); + + assert_eq!( + uncompressed_point.x().unwrap(), + &hex!("1111111111111111111111111111111111111111111111111111111111111111").into() + ); + assert_eq!( + uncompressed_point.y().unwrap(), + &hex!("2222222222222222222222222222222222222222222222222222222222222222").into() + ); + } + + #[test] + fn decode_identity() { + let identity_point = EncodedPoint::from_bytes(&IDENTITY_BYTES[..]).unwrap(); + assert!(identity_point.is_identity()); + assert_eq!(identity_point.tag(), Tag::Identity); + assert_eq!(identity_point.len(), 1); + assert_eq!(identity_point.as_bytes(), &IDENTITY_BYTES[..]); + assert_eq!(identity_point.coordinates(), Coordinates::Identity); + assert_eq!(identity_point.x(), None); + assert_eq!(identity_point.y(), None); + } + + #[test] + fn decode_invalid_tag() { + let mut compressed_bytes = COMPRESSED_BYTES; + let mut uncompressed_bytes = UNCOMPRESSED_BYTES; + + for bytes in &mut [&mut compressed_bytes[..], &mut uncompressed_bytes[..]] { + for tag in 0..=0xFF { + // valid tags + if tag == 2 || tag == 3 || tag == 4 || tag == 5 { + continue; + } + + (*bytes)[0] = tag; + let decode_result = EncodedPoint::from_bytes(&*bytes); + assert!(decode_result.is_err()); + } + } + } + + #[test] + fn decode_truncated_point() { + for bytes in &[&COMPRESSED_BYTES[..], &UNCOMPRESSED_BYTES[..]] { + for len in 0..bytes.len() { + let decode_result = EncodedPoint::from_bytes(&bytes[..len]); + assert!(decode_result.is_err()); + } + } + } + + #[test] + fn from_untagged_point() { + let untagged_bytes = hex!("11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222"); + let uncompressed_point = + EncodedPoint::from_untagged_bytes(GenericArray::from_slice(&untagged_bytes[..])); + assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]); + } + + #[test] + fn from_affine_coordinates() { + let x = hex!("1111111111111111111111111111111111111111111111111111111111111111"); + let y = hex!("2222222222222222222222222222222222222222222222222222222222222222"); + + let uncompressed_point = EncodedPoint::from_affine_coordinates(&x.into(), &y.into(), false); + assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]); + + let compressed_point = EncodedPoint::from_affine_coordinates(&x.into(), &y.into(), true); + assert_eq!(compressed_point.as_bytes(), &COMPRESSED_BYTES[..]); + } + + #[test] + fn compress() { + let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap(); + let compressed_point = uncompressed_point.compress(); + assert_eq!(compressed_point.as_bytes(), &COMPRESSED_BYTES[..]); + } + + #[cfg(feature = "subtle")] + #[test] + fn conditional_select() { + let a = EncodedPoint::from_bytes(&COMPRESSED_BYTES[..]).unwrap(); + let b = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap(); + + let a_selected = EncodedPoint::conditional_select(&a, &b, 0.into()); + assert_eq!(a, a_selected); + + let b_selected = EncodedPoint::conditional_select(&a, &b, 1.into()); + assert_eq!(b, b_selected); + } + + #[test] + fn identity() { + let identity_point = EncodedPoint::identity(); + assert_eq!(identity_point.tag(), Tag::Identity); + assert_eq!(identity_point.len(), 1); + assert_eq!(identity_point.as_bytes(), &IDENTITY_BYTES[..]); + + // identity is default + assert_eq!(identity_point, EncodedPoint::default()); + } + + #[test] + fn decode_hex() { + let point = EncodedPoint::from_str( + "021111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + assert_eq!(point.as_bytes(), COMPRESSED_BYTES); + } + + #[cfg(feature = "alloc")] + #[test] + fn to_bytes() { + let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap(); + assert_eq!(&*uncompressed_point.to_bytes(), &UNCOMPRESSED_BYTES[..]); + } + + #[cfg(feature = "alloc")] + #[test] + fn to_string() { + let point = EncodedPoint::from_bytes(&COMPRESSED_BYTES[..]).unwrap(); + assert_eq!( + point.to_string(), + "021111111111111111111111111111111111111111111111111111111111111111" + ); + } +} diff --git a/vendor/sec1/src/private_key.rs b/vendor/sec1/src/private_key.rs new file mode 100644 index 0000000..5315799 --- /dev/null +++ b/vendor/sec1/src/private_key.rs @@ -0,0 +1,177 @@ +//! SEC1 elliptic curve private key support. +//! +//! Support for ASN.1 DER-encoded elliptic curve private keys as described in +//! SEC1: Elliptic Curve Cryptography (Version 2.0) Appendix C.4 (p.108): +//! +//! + +use crate::{EcParameters, Error, Result}; +use core::fmt; +use der::{ + asn1::{BitStringRef, ContextSpecific, ContextSpecificRef, OctetStringRef}, + Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, TagMode, + TagNumber, Writer, +}; + +#[cfg(all(feature = "alloc", feature = "zeroize"))] +use der::SecretDocument; + +#[cfg(feature = "pem")] +use der::pem::PemLabel; + +/// `ECPrivateKey` version. +/// +/// From [RFC5913 Section 3]: +/// > version specifies the syntax version number of the elliptic curve +/// > private key structure. For this version of the document, it SHALL +/// > be set to ecPrivkeyVer1, which is of type INTEGER and whose value +/// > is one (1). +/// +/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3 +const VERSION: u8 = 1; + +/// Context-specific tag number for the elliptic curve parameters. +const EC_PARAMETERS_TAG: TagNumber = TagNumber::new(0); + +/// Context-specific tag number for the public key. +const PUBLIC_KEY_TAG: TagNumber = TagNumber::new(1); + +/// SEC1 elliptic curve private key. +/// +/// Described in [SEC1: Elliptic Curve Cryptography (Version 2.0)] +/// Appendix C.4 (p.108) and also [RFC5915 Section 3]: +/// +/// ```text +/// ECPrivateKey ::= SEQUENCE { +/// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), +/// privateKey OCTET STRING, +/// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, +/// publicKey [1] BIT STRING OPTIONAL +/// } +/// ``` +/// +/// When encoded as PEM (text), keys in this format begin with the following: +/// +/// ```text +/// -----BEGIN EC PRIVATE KEY----- +/// ``` +/// +/// [SEC1: Elliptic Curve Cryptography (Version 2.0)]: https://www.secg.org/sec1-v2.pdf +/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3 +#[derive(Clone)] +pub struct EcPrivateKey<'a> { + /// Private key data. + pub private_key: &'a [u8], + + /// Elliptic curve parameters. + pub parameters: Option, + + /// Public key data, optionally available if version is V2. + pub public_key: Option<&'a [u8]>, +} + +impl<'a> EcPrivateKey<'a> { + fn context_specific_parameters(&self) -> Option> { + self.parameters.as_ref().map(|params| ContextSpecificRef { + tag_number: EC_PARAMETERS_TAG, + tag_mode: TagMode::Explicit, + value: params, + }) + } + + fn context_specific_public_key( + &self, + ) -> der::Result>>> { + self.public_key + .map(|pk| { + BitStringRef::from_bytes(pk).map(|value| ContextSpecific { + tag_number: PUBLIC_KEY_TAG, + tag_mode: TagMode::Explicit, + value, + }) + }) + .transpose() + } +} + +impl<'a> DecodeValue<'a> for EcPrivateKey<'a> { + fn decode_value>(reader: &mut R, header: Header) -> der::Result { + reader.read_nested(header.length, |reader| { + if u8::decode(reader)? != VERSION { + return Err(der::Tag::Integer.value_error()); + } + + let private_key = OctetStringRef::decode(reader)?.as_bytes(); + let parameters = reader.context_specific(EC_PARAMETERS_TAG, TagMode::Explicit)?; + let public_key = reader + .context_specific::>(PUBLIC_KEY_TAG, TagMode::Explicit)? + .map(|bs| bs.as_bytes().ok_or_else(|| Tag::BitString.value_error())) + .transpose()?; + + Ok(EcPrivateKey { + private_key, + parameters, + public_key, + }) + }) + } +} + +impl EncodeValue for EcPrivateKey<'_> { + fn value_len(&self) -> der::Result { + VERSION.encoded_len()? + + OctetStringRef::new(self.private_key)?.encoded_len()? + + self.context_specific_parameters().encoded_len()? + + self.context_specific_public_key()?.encoded_len()? + } + + fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> { + VERSION.encode(writer)?; + OctetStringRef::new(self.private_key)?.encode(writer)?; + self.context_specific_parameters().encode(writer)?; + self.context_specific_public_key()?.encode(writer)?; + Ok(()) + } +} + +impl<'a> Sequence<'a> for EcPrivateKey<'a> {} + +impl<'a> TryFrom<&'a [u8]> for EcPrivateKey<'a> { + type Error = Error; + + fn try_from(bytes: &'a [u8]) -> Result> { + Ok(Self::from_der(bytes)?) + } +} + +impl<'a> fmt::Debug for EcPrivateKey<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EcPrivateKey") + .field("parameters", &self.parameters) + .field("public_key", &self.public_key) + .finish_non_exhaustive() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for SecretDocument { + type Error = Error; + + fn try_from(private_key: EcPrivateKey<'_>) -> Result { + SecretDocument::try_from(&private_key) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom<&EcPrivateKey<'_>> for SecretDocument { + type Error = Error; + + fn try_from(private_key: &EcPrivateKey<'_>) -> Result { + Ok(Self::encode_msg(private_key)?) + } +} + +#[cfg(feature = "pem")] +impl PemLabel for EcPrivateKey<'_> { + const PEM_LABEL: &'static str = "EC PRIVATE KEY"; +} diff --git a/vendor/sec1/src/traits.rs b/vendor/sec1/src/traits.rs new file mode 100644 index 0000000..304019e --- /dev/null +++ b/vendor/sec1/src/traits.rs @@ -0,0 +1,122 @@ +//! Traits for parsing objects from SEC1 encoded documents + +use crate::Result; + +#[cfg(feature = "alloc")] +use der::SecretDocument; + +#[cfg(feature = "pem")] +use {crate::LineEnding, alloc::string::String, der::pem::PemLabel}; + +#[cfg(feature = "pkcs8")] +use { + crate::{EcPrivateKey, ALGORITHM_OID}, + der::Decode, +}; + +#[cfg(feature = "std")] +use std::path::Path; + +#[cfg(feature = "pem")] +use zeroize::Zeroizing; + +/// Parse an [`EcPrivateKey`] from a SEC1-encoded document. +pub trait DecodeEcPrivateKey: Sized { + /// Deserialize SEC1 private key from ASN.1 DER-encoded data + /// (binary format). + fn from_sec1_der(bytes: &[u8]) -> Result; + + /// Deserialize SEC1-encoded private key from PEM. + /// + /// Keys in this format begin with the following: + /// + /// ```text + /// -----BEGIN EC PRIVATE KEY----- + /// ``` + #[cfg(feature = "pem")] + fn from_sec1_pem(s: &str) -> Result { + let (label, doc) = SecretDocument::from_pem(s)?; + EcPrivateKey::validate_pem_label(label)?; + Self::from_sec1_der(doc.as_bytes()) + } + + /// Load SEC1 private key from an ASN.1 DER-encoded file on the local + /// filesystem (binary format). + #[cfg(feature = "std")] + fn read_sec1_der_file(path: impl AsRef) -> Result { + Self::from_sec1_der(SecretDocument::read_der_file(path)?.as_bytes()) + } + + /// Load SEC1 private key from a PEM-encoded file on the local filesystem. + #[cfg(all(feature = "pem", feature = "std"))] + fn read_sec1_pem_file(path: impl AsRef) -> Result { + let (label, doc) = SecretDocument::read_pem_file(path)?; + EcPrivateKey::validate_pem_label(&label)?; + Self::from_sec1_der(doc.as_bytes()) + } +} + +/// Serialize a [`EcPrivateKey`] to a SEC1 encoded document. +#[cfg(feature = "alloc")] +pub trait EncodeEcPrivateKey { + /// Serialize a [`SecretDocument`] containing a SEC1-encoded private key. + fn to_sec1_der(&self) -> Result; + + /// Serialize this private key as PEM-encoded SEC1 with the given [`LineEnding`]. + /// + /// To use the OS's native line endings, pass `Default::default()`. + #[cfg(feature = "pem")] + fn to_sec1_pem(&self, line_ending: LineEnding) -> Result> { + let doc = self.to_sec1_der()?; + Ok(doc.to_pem(EcPrivateKey::PEM_LABEL, line_ending)?) + } + + /// Write ASN.1 DER-encoded SEC1 private key to the given path. + #[cfg(feature = "std")] + fn write_sec1_der_file(&self, path: impl AsRef) -> Result<()> { + Ok(self.to_sec1_der()?.write_der_file(path)?) + } + + /// Write ASN.1 DER-encoded SEC1 private key to the given path. + #[cfg(all(feature = "pem", feature = "std"))] + fn write_sec1_pem_file(&self, path: impl AsRef, line_ending: LineEnding) -> Result<()> { + let doc = self.to_sec1_der()?; + Ok(doc.write_pem_file(path, EcPrivateKey::PEM_LABEL, line_ending)?) + } +} + +#[cfg(feature = "pkcs8")] +impl DecodeEcPrivateKey for T +where + T: for<'a> TryFrom, Error = pkcs8::Error>, +{ + fn from_sec1_der(private_key: &[u8]) -> Result { + let params_oid = EcPrivateKey::from_der(private_key)? + .parameters + .and_then(|params| params.named_curve()); + + let algorithm = pkcs8::AlgorithmIdentifierRef { + oid: ALGORITHM_OID, + parameters: params_oid.as_ref().map(Into::into), + }; + + Ok(Self::try_from(pkcs8::PrivateKeyInfo { + algorithm, + private_key, + public_key: None, + })?) + } +} + +#[cfg(all(feature = "alloc", feature = "pkcs8"))] +impl EncodeEcPrivateKey for T { + fn to_sec1_der(&self) -> Result { + let doc = self.to_pkcs8_der()?; + let pkcs8_key = pkcs8::PrivateKeyInfo::from_der(doc.as_bytes())?; + pkcs8_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?; + + let mut pkcs1_key = EcPrivateKey::from_der(pkcs8_key.private_key)?; + pkcs1_key.parameters = Some(pkcs8_key.algorithm.parameters_oid()?.into()); + pkcs1_key.try_into() + } +} diff --git a/vendor/sec1/tests/examples/p256-priv.der b/vendor/sec1/tests/examples/p256-priv.der new file mode 100644 index 0000000..c8528c3 Binary files /dev/null and b/vendor/sec1/tests/examples/p256-priv.der differ diff --git a/vendor/sec1/tests/examples/p256-priv.pem b/vendor/sec1/tests/examples/p256-priv.pem new file mode 100644 index 0000000..d5a1c1a --- /dev/null +++ b/vendor/sec1/tests/examples/p256-priv.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGliQXFWGmM0DeDn2GnyoFSSVY4aBIaLap+FSoZniBiNoAoGCCqGSM49 +AwEHoUQDQgAEHKz/tV8vLO/YnYnrN0smgRUkUoAt7qCZFgaBN9g5z3/EgaREkjBN +fvZqwRe+/oOo0I8VXytS+fYY3URwKQSODw== +-----END EC PRIVATE KEY----- diff --git a/vendor/sec1/tests/private_key.rs b/vendor/sec1/tests/private_key.rs new file mode 100644 index 0000000..224a947 --- /dev/null +++ b/vendor/sec1/tests/private_key.rs @@ -0,0 +1,43 @@ +//! SEC1 private key tests + +#![cfg(feature = "der")] + +use der::asn1::ObjectIdentifier; +use hex_literal::hex; +use sec1::{EcParameters, EcPrivateKey}; + +#[cfg(feature = "alloc")] +use der::Encode; + +/// NIST P-256 SEC1 private key encoded as ASN.1 DER. +/// +/// Note: this key is extracted from the corresponding `p256-priv.der` +/// example key in the `pkcs8` crate. +const P256_DER_EXAMPLE: &[u8] = include_bytes!("examples/p256-priv.der"); + +#[test] +fn decode_p256_der() { + let key = EcPrivateKey::try_from(P256_DER_EXAMPLE).unwrap(); + + // Extracted using: + // $ openssl asn1parse -in tests/examples/p256-priv.pem + assert_eq!( + key.private_key, + hex!("69624171561A63340DE0E7D869F2A05492558E1A04868B6A9F854A866788188D") + ); + assert_eq!( + key.parameters, + Some(EcParameters::NamedCurve( + ObjectIdentifier::new("1.2.840.10045.3.1.7").unwrap() + )) + ); + assert_eq!(key.public_key, Some(hex!("041CACFFB55F2F2CEFD89D89EB374B2681152452802DEEA09916068137D839CF7FC481A44492304D7EF66AC117BEFE83A8D08F155F2B52F9F618DD447029048E0F").as_ref())); +} + +#[cfg(feature = "alloc")] +#[test] +fn encode_p256_der() { + let key = EcPrivateKey::try_from(P256_DER_EXAMPLE).unwrap(); + let key_encoded = key.to_der().unwrap(); + assert_eq!(P256_DER_EXAMPLE, key_encoded); +} diff --git a/vendor/sec1/tests/traits.rs b/vendor/sec1/tests/traits.rs new file mode 100644 index 0000000..ab6e09a --- /dev/null +++ b/vendor/sec1/tests/traits.rs @@ -0,0 +1,100 @@ +//! Tests for SEC1 encoding/decoding traits. + +#![cfg(any(feature = "pem", all(feature = "der", feature = "std")))] + +use der::SecretDocument; +use sec1::{DecodeEcPrivateKey, EncodeEcPrivateKey, Result}; + +#[cfg(feature = "pem")] +use sec1::der::pem::LineEnding; + +#[cfg(feature = "std")] +use tempfile::tempdir; + +#[cfg(all(feature = "pem", feature = "std"))] +use std::fs; + +/// SEC1 `EcPrivateKey` encoded as ASN.1 DER +const P256_DER_EXAMPLE: &[u8] = include_bytes!("examples/p256-priv.der"); + +/// SEC1 `EcPrivateKey` encoded as PEM +#[cfg(feature = "pem")] +const P256_PEM_EXAMPLE: &str = include_str!("examples/p256-priv.pem"); + +/// Mock private key type for testing trait impls against. +pub struct MockPrivateKey(Vec); + +impl AsRef<[u8]> for MockPrivateKey { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl DecodeEcPrivateKey for MockPrivateKey { + fn from_sec1_der(bytes: &[u8]) -> Result { + Ok(MockPrivateKey(bytes.to_vec())) + } +} + +impl EncodeEcPrivateKey for MockPrivateKey { + fn to_sec1_der(&self) -> Result { + Ok(SecretDocument::try_from(self.as_ref())?) + } +} + +#[cfg(feature = "pem")] +#[test] +fn from_sec1_pem() { + let key = MockPrivateKey::from_sec1_pem(P256_PEM_EXAMPLE).unwrap(); + assert_eq!(key.as_ref(), P256_DER_EXAMPLE); +} + +#[cfg(feature = "std")] +#[test] +fn read_sec1_der_file() { + let key = MockPrivateKey::read_sec1_der_file("tests/examples/p256-priv.der").unwrap(); + assert_eq!(key.as_ref(), P256_DER_EXAMPLE); +} + +#[cfg(all(feature = "pem", feature = "std"))] +#[test] +fn read_sec1_pem_file() { + let key = MockPrivateKey::read_sec1_pem_file("tests/examples/p256-priv.pem").unwrap(); + assert_eq!(key.as_ref(), P256_DER_EXAMPLE); +} + +#[cfg(feature = "pem")] +#[test] +fn to_sec1_pem() { + let pem = MockPrivateKey(P256_DER_EXAMPLE.to_vec()) + .to_sec1_pem(LineEnding::LF) + .unwrap(); + + assert_eq!(&*pem, P256_PEM_EXAMPLE); +} + +#[cfg(feature = "std")] +#[test] +fn write_sec1_der_file() { + let dir = tempdir().unwrap(); + let path = dir.path().join("example.der"); + MockPrivateKey(P256_DER_EXAMPLE.to_vec()) + .write_sec1_der_file(&path) + .unwrap(); + + let key = MockPrivateKey::read_sec1_der_file(&path).unwrap(); + assert_eq!(key.as_ref(), P256_DER_EXAMPLE); +} + +#[cfg(all(feature = "pem", feature = "std"))] +#[test] +fn write_sec1_pem_file() { + let dir = tempdir().unwrap(); + let path = dir.path().join("example.pem"); + MockPrivateKey(P256_DER_EXAMPLE.to_vec()) + .write_sec1_pem_file(&path, LineEnding::LF) + .unwrap(); + + let pem = fs::read_to_string(path).unwrap(); + assert_eq!(&pem, P256_PEM_EXAMPLE); +} -- cgit v1.2.3