diff --git a/.github/workflows/elliptic-curve.yml b/.github/workflows/elliptic-curve.yml new file mode 100644 index 000000000..0a251428b --- /dev/null +++ b/.github/workflows/elliptic-curve.yml @@ -0,0 +1,55 @@ +name: elliptic-curve + +on: + pull_request: + paths: + - "elliptic-curve/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: elliptic-curve + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.41.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.41.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + - run: cargo check --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index 20895ffac..9d551c775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,6 +84,7 @@ dependencies = [ "block-cipher", "crypto-mac", "digest 0.9.0", + "elliptic-curve", "signature", "stream-cipher", "universal-hash", @@ -106,6 +107,17 @@ dependencies = [ "generic-array 0.14.3", ] +[[package]] +name = "elliptic-curve" +version = "0.4.0" +dependencies = [ + "generic-array 0.14.3", + "hex", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -155,6 +167,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "hex-literal" version = "0.2.1" @@ -313,3 +331,9 @@ name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/Cargo.toml b/Cargo.toml index 4b9fdd831..aa9d7d9c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crypto-mac", "cryptography", "digest", + "elliptic-curve", "signature", "stream-cipher", "universal-hash", diff --git a/README.md b/README.md index a5c679232..39e3fce84 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Collection of traits which describe functionality of cryptographic primitives. | [`block‑cipher`] | [Block cipher] | [![crates.io](https://img.shields.io/crates/v/block-cipher.svg)](https://crates.io/crates/block-cipher) | [![Documentation](https://docs.rs/block-cipher/badge.svg)](https://docs.rs/block-cipher) | ![build](https://github.com/RustCrypto/traits/workflows/block-cipher/badge.svg?branch=master&event=push) | | [`crypto‑mac`] | [Message authentication code] | [![crates.io](https://img.shields.io/crates/v/crypto-mac.svg)](https://crates.io/crates/crypto-mac) | [![Documentation](https://docs.rs/crypto-mac/badge.svg)](https://docs.rs/crypto-mac) | ![build](https://github.com/RustCrypto/traits/workflows/crypto-mac/badge.svg?branch=master&event=push) | | [`digest`] | [Cryptographic hash function] | [![crates.io](https://img.shields.io/crates/v/digest.svg)](https://crates.io/crates/digest) | [![Documentation](https://docs.rs/digest/badge.svg)](https://docs.rs/digest) | ![build](https://github.com/RustCrypto/traits/workflows/digest/badge.svg?branch=master&event=push) | +| [`elliptic-curve`] | [Elliptic curve cryptography] | [![crates.io](https://img.shields.io/crates/v/elliptic-curve.svg)](https://crates.io/crates/elliptic-curve) | [![Documentation](https://docs.rs/elliptic-curve/badge.svg)](https://docs.rs/elliptic-curve) | ![build](https://github.com/RustCrypto/traits/workflows/elliptic-curve/badge.svg?branch=master&event=push) | | [`signature`] | [Digital signature] | [![crates.io](https://img.shields.io/crates/v/signature.svg)](https://crates.io/crates/signature) | [![Documentation](https://docs.rs/signature/badge.svg)](https://docs.rs/signature) | ![build](https://github.com/RustCrypto/traits/workflows/signature/badge.svg?branch=master&event=push) | | [`stream‑cipher`] | [Stream cipher] | [![crates.io](https://img.shields.io/crates/v/stream-cipher.svg)](https://crates.io/crates/stream-cipher) | [![Documentation](https://docs.rs/stream-cipher/badge.svg)](https://docs.rs/stream-cipher) | ![build](https://github.com/RustCrypto/traits/workflows/stream-cipher/badge.svg?branch=master&event=push) | | [`universal‑hash`] | [Universal hash function] | [![crates.io](https://img.shields.io/crates/v/universal-hash.svg)](https://crates.io/crates/universal-hash) | [![Documentation](https://docs.rs/universal-hash/badge.svg)](https://docs.rs/universal-hash) | ![build](https://github.com/RustCrypto/traits/workflows/universal-hash/badge.svg?branch=master&event=push) | @@ -53,6 +54,7 @@ dual licensed as above, without any additional terms or conditions. [`crypto‑mac`]: https://github.com/RustCrypto/traits/tree/master/crypto-mac [`cryptography`]: https://github.com/RustCrypto/traits/tree/master/cryptography [`digest`]: https://github.com/RustCrypto/traits/tree/master/digest +[`elliptic-curve`]: https://github.com/RustCrypto/traits/tree/master/elliptic-curve [`signature`]: https://github.com/RustCrypto/traits/tree/master/signature [`stream‑cipher`]: https://github.com/RustCrypto/traits/tree/master/stream-cipher [`universal‑hash`]: https://github.com/RustCrypto/traits/tree/master/universal-hash @@ -64,5 +66,6 @@ dual licensed as above, without any additional terms or conditions. [Message authentication code]: https://en.wikipedia.org/wiki/Message_authentication_code [Cryptographic hash function]: https://en.wikipedia.org/wiki/Cryptographic_hash_function [Digital signature]: https://en.wikipedia.org/wiki/Digital_signature +[Elliptic curve cryptography]: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography [Stream cipher]: https://en.wikipedia.org/wiki/Stream_cipher [Universal hash function]: https://en.wikipedia.org/wiki/Universal_hashing diff --git a/cryptography/Cargo.toml b/cryptography/Cargo.toml index 403f66880..5edcceec6 100644 --- a/cryptography/Cargo.toml +++ b/cryptography/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" aead = { version = "0.3", optional = true, path = "../aead" } block-cipher = { version = "0.8", optional = true, path = "../block-cipher" } digest = { version = "0.9", optional = true, path = "../digest" } +elliptic-curve = { version = "0.4", optional = true, path = "../elliptic-curve" } mac = { version = "0.8", package = "crypto-mac", optional = true, path = "../crypto-mac" } signature = { version = "1.1.0", optional = true, default-features = false, path = "../signature" } stream-cipher = { version = "0.6", optional = true, path = "../stream-cipher" } diff --git a/cryptography/src/lib.rs b/cryptography/src/lib.rs index fdb505fbf..9df174413 100644 --- a/cryptography/src/lib.rs +++ b/cryptography/src/lib.rs @@ -26,6 +26,7 @@ //! | [`aead`](https://docs.rs/aead) | `aead` | Authenticated Encryption with Associated Data (i.e. high-level symmetric encryption) | //! | [`block_cipher`](https://docs.rs/block-cipher) | `block‑cipher` | Block-based cryptographic permutations (i.e. low-level symmetric encryption) | //! | [`digest`](https://docs.rs/digest) | `digest` | Cryptographic hash functions | +//! | [`elliptic-curve`](https://docs.rs/elliptic-curve) | `elliptic-curve` | Elliptic curve cryptography | //! | [`mac`](https://docs.rs/crypto-mac) | `mac` | Message Authentication Codes (i.e. symmetric message authentication) | //! | [`signature`](https://docs.rs/signature) | `signature` | Digital signatures (i.e. public key-based message authentication) | //! | [`stream_cipher`](https://docs.rs/stream-cipher) | `stream‑cipher` | Ciphers based on randomly generated keystreams (i.e. low-level symmetric encryption) | @@ -48,6 +49,9 @@ pub use block_cipher; #[cfg(feature = "digest")] pub use digest; +#[cfg(feature = "elliptic-curve")] +pub use elliptic_curve; + #[cfg(feature = "mac")] pub use mac; diff --git a/elliptic-curve/CHANGELOG.md b/elliptic-curve/CHANGELOG.md new file mode 100644 index 000000000..de475f85b --- /dev/null +++ b/elliptic-curve/CHANGELOG.md @@ -0,0 +1,36 @@ +# 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.4.0 (2020-06-04) +### Changed +- Bump `generic-array` dependency from v0.12 to v0.14 ([#38]) + +[#38]: https://github.com/RustCrypto/elliptic-curves/pull/38 + +## 0.3.0 (2020-01-15) +### Added +- `Scalar` struct type ([#5]) + +### Changed +- Repository moved to + +### Removed +- Curve definitions/arithmetic extracted out into per-curve crates ([#5]) + +[#5]: https://github.com/RustCrypto/elliptic-curves/pull/5 + +## 0.2.0 (2019-12-11) +### Added +- `secp256r1` (P-256) point compression and decompression ([RustCrypto/signatures#63], [RustCrypto/signatures#64]) + +### Changed +- Bump MSRV to 1.37 ([RustCrypto/signatures#63]) + +[RustCrypto/signatures#63]: https://github.com/RustCrypto/signatures/pull/63 +[RustCrypto/signatures#64]: https://github.com/RustCrypto/signatures/pull/64 + +## 0.1.0 (2019-12-06) +- Initial release diff --git a/elliptic-curve/Cargo.toml b/elliptic-curve/Cargo.toml new file mode 100644 index 000000000..54a690344 --- /dev/null +++ b/elliptic-curve/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "elliptic-curve" +description = """ +General purpose Elliptic Curve Cryptography (ECC) support, including types +and traits for representing various elliptic curve forms, scalars, points, +and public/secret keys composed thereof. +""" +version = "0.4.0" # Also update html_root_url in lib.rs when bumping this +authors = ["RustCrypto Developers"] +license = "Apache-2.0 OR MIT" +documentation = "https://docs.rs/elliptic-curve" +repository = "https://github.com/RustCrypto/elliptic-curves/tree/master/elliptic-curve-crate" +readme = "README.md" +edition = "2018" +categories = ["cryptography", "no-std"] +keywords = ["crypto", "ecc", "elliptic", "weierstrass"] + +[dependencies.generic-array] +version = "0.14" +default-features = false + +[dependencies.rand_core] +version = "0.5" +optional = true +default-features = false + +[dependencies.subtle] +version = "2.2.2" +default-features = false + +[dependencies.zeroize] +version = "1" +optional = true +default-features = false + +[dev-dependencies] +hex = "0.4" + +[features] +default = [] +weierstrass = [] +std = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/elliptic-curve/LICENSE-APACHE b/elliptic-curve/LICENSE-APACHE new file mode 100644 index 000000000..78173fa2e --- /dev/null +++ b/elliptic-curve/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/elliptic-curve/LICENSE-MIT b/elliptic-curve/LICENSE-MIT new file mode 100644 index 000000000..f39f9ff82 --- /dev/null +++ b/elliptic-curve/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020 RustCrypto 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/elliptic-curve/README.md b/elliptic-curve/README.md new file mode 100644 index 000000000..5b7e92ba9 --- /dev/null +++ b/elliptic-curve/README.md @@ -0,0 +1,51 @@ +# RustCrypto: Elliptic Curve Traits + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Build Status][build-image]][build-link] + +General purpose Elliptic Curve Cryptography (ECC) support, including types +and traits for representing various elliptic curve forms, scalars, points, +and public/secret keys composed thereof. + +[Documentation][docs-link] + +## Minimum Supported Rust Version + +All crates in this repository support Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +All crates 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://img.shields.io/crates/v/elliptic-curve.svg +[crate-link]: https://crates.io/crates/elliptic-curve +[docs-image]: https://docs.rs/elliptic-curve/badge.svg +[docs-link]: https://docs.rs/elliptic-curve/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[build-image]: https://github.com/RustCrypto/elliptic-curves/workflows/elliptic-curve%20crate/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/elliptic-curves/actions?query=workflow%3A%22elliptic-curve+crate%22 diff --git a/elliptic-curve/src/error.rs b/elliptic-curve/src/error.rs new file mode 100644 index 000000000..0a1ab14a8 --- /dev/null +++ b/elliptic-curve/src/error.rs @@ -0,0 +1,16 @@ +//! Error type + +use core::fmt::{self, Display}; + +/// Elliptic curve errors +#[derive(Copy, Clone, Debug)] +pub struct Error; + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("crypto error") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs new file mode 100644 index 000000000..35cdb5b0c --- /dev/null +++ b/elliptic-curve/src/lib.rs @@ -0,0 +1,41 @@ +//! General purpose Elliptic Curve Cryptography (ECC) support, including types +//! and traits for representing various elliptic curve forms, scalars, points, +//! and public/secret keys composed thereof. +//! +//! ## Minimum Supported Rust Version +//! +//! Rust **1.41** or higher. +//! +//! Minimum supported Rust version can be changed in the future, but it will be +//! done with a minor version bump. + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms, unused_qualifications)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png", + html_root_url = "https://docs.rs/elliptic-curve/0.4.0" +)] + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "rand_core")] +pub use rand_core; + +pub mod error; +pub mod secret_key; + +pub use generic_array::{self, typenum::consts}; +pub use subtle; + +// TODO(tarcieri): other curve forms +#[cfg(feature = "weierstrass")] +#[cfg_attr(docsrs, doc(cfg(feature = "weierstrass")))] +pub mod weierstrass; + +pub use self::{error::Error, secret_key::SecretKey}; + +/// Byte array containing a serialized scalar value (i.e. an integer) +pub type ScalarBytes = generic_array::GenericArray; diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs new file mode 100644 index 000000000..dd88fabdc --- /dev/null +++ b/elliptic-curve/src/secret_key.rs @@ -0,0 +1,88 @@ +//! Secret keys for elliptic curves (i.e. private scalars) +//! +//! The [`SecretKey`] type wraps the [`ScalarBytes`] byte array type with +//! a wrapper designed to prevent unintentional exposure of the scalar +//! value (e.g. via `Debug` or other logging). +//! +//! When the `zeroize` feature of this crate is enabled, it also handles +//! zeroing it out of memory securely on drop. + +use crate::{error::Error, ScalarBytes}; +use core::{ + convert::{TryFrom, TryInto}, + fmt, +}; +use generic_array::ArrayLength; + +/// Secret keys. +/// +/// In elliptic curve cryptography, secret keys are concretely privately known +/// scalar values. +/// +/// This type wraps a (serialized) scalar value, helping to prevent accidental +/// exposure and securely erasing the value from memory when dropped +/// (when the `zeroize` feature of this crate is enabled). +pub struct SecretKey +where + ScalarSize: ArrayLength, +{ + /// Private scalar value + scalar: ScalarBytes, +} + +impl SecretKey +where + ScalarSize: ArrayLength, +{ + /// Create a new secret key from a serialized scalar value + pub fn new(bytes: ScalarBytes) -> Self { + Self { scalar: bytes } + } + + /// Deserialize this secret key from a bytestring + pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result { + bytes.as_ref().try_into() + } + + /// Expose the secret [`ScalarBytes`] value this [`SecretKey`] wraps + pub fn secret_scalar(&self) -> &ScalarBytes { + &self.scalar + } +} + +impl TryFrom<&[u8]> for SecretKey +where + ScalarSize: ArrayLength, +{ + type Error = Error; + + fn try_from(slice: &[u8]) -> Result { + if slice.len() == ScalarSize::to_usize() { + Ok(SecretKey { + scalar: ScalarBytes::clone_from_slice(slice), + }) + } else { + Err(Error) + } + } +} + +impl fmt::Debug for SecretKey +where + ScalarSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SecretKey{{ ... }}", ScalarSize::to_usize()) + } +} + +#[cfg(feature = "zeroize")] +impl Drop for SecretKey +where + ScalarSize: ArrayLength, +{ + fn drop(&mut self) { + use zeroize::Zeroize; + self.scalar.zeroize(); + } +} diff --git a/elliptic-curve/src/weierstrass.rs b/elliptic-curve/src/weierstrass.rs new file mode 100644 index 000000000..23be63769 --- /dev/null +++ b/elliptic-curve/src/weierstrass.rs @@ -0,0 +1,46 @@ +//! Elliptic curves in short Weierstrass form. + +pub mod curve; +pub mod point; +pub mod public_key; + +pub use curve::Curve; +pub use point::{CompressedPoint, CompressedPointSize, UncompressedPoint, UncompressedPointSize}; +pub use public_key::PublicKey; + +use crate::{consts::U1, ScalarBytes}; +use core::ops::Add; +use generic_array::ArrayLength; +use subtle::{ConditionallySelectable, CtOption}; + +#[cfg(feature = "rand_core")] +use crate::secret_key::SecretKey; + +#[cfg(feature = "rand_core")] +use rand_core::{CryptoRng, RngCore}; + +/// Fixed-base scalar multiplication +pub trait FixedBaseScalarMul: Curve +where + ::Output: Add, + CompressedPoint: From, + UncompressedPoint: From, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + /// Elliptic curve point type + type Point: ConditionallySelectable; + + /// Multiply the given scalar by the generator point for this elliptic + /// curve. + fn mul_base(scalar: &ScalarBytes) -> CtOption; +} + +/// Generate a secret key for this elliptic curve +#[cfg(feature = "rand_core")] +#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] +pub trait GenerateSecretKey: Curve { + /// Generate a random [`SecretKey`] for this elliptic curve using the + /// provided [`CryptoRng`] + fn generate_secret_key(rng: &mut (impl CryptoRng + RngCore)) -> SecretKey; +} diff --git a/elliptic-curve/src/weierstrass/curve.rs b/elliptic-curve/src/weierstrass/curve.rs new file mode 100644 index 000000000..c7fdf623a --- /dev/null +++ b/elliptic-curve/src/weierstrass/curve.rs @@ -0,0 +1,20 @@ +//! Elliptic curves in short Weierstrass form + +use core::{fmt::Debug, ops::Add}; +use generic_array::{ + typenum::{Unsigned, U1}, + ArrayLength, +}; + +#[cfg(docsrs)] +use crate::ScalarBytes; + +/// Elliptic curve in short Weierstrass form +pub trait Curve: Clone + Debug + Default + Eq + Ord + Send + Sync { + /// Size of [`ScalarBytes`] for this curve, i.e. a serialized integer + /// modulo p (i.e. the curve's order). + type ScalarSize: ArrayLength + Add + Add + Eq + Ord + Unsigned; +} + +/// Alias for [`SecretKey`] type for a given Weierstrass curve +pub type SecretKey = crate::secret_key::SecretKey<::ScalarSize>; diff --git a/elliptic-curve/src/weierstrass/point.rs b/elliptic-curve/src/weierstrass/point.rs new file mode 100644 index 000000000..eb5d5b765 --- /dev/null +++ b/elliptic-curve/src/weierstrass/point.rs @@ -0,0 +1,201 @@ +//! Compressed and uncompressed Weierstrass elliptic curve points. +//! +//! Serialized according to the `Elliptic-Curve-Point-to-Octet-String` +//! algorithm described in SEC 1: Elliptic Curve Cryptography (Version 2.0) +//! section 2.3.3 (page 10): +//! +//! + +use super::Curve; +use crate::ScalarBytes; +use core::ops::Add; +use generic_array::{ + typenum::{Unsigned, U1}, + ArrayLength, GenericArray, +}; + +/// Size of a compressed elliptic curve point for the given curve when +/// serialized using `Elliptic-Curve-Point-to-Octet-String` encoding +/// (including leading `0x02` or `0x03` tag byte). +pub type CompressedPointSize = >::Output; + +/// Size of an uncompressed elliptic curve point for the given curve when +/// serialized using the `Elliptic-Curve-Point-to-Octet-String` encoding +/// (including leading `0x04` tag byte). +pub type UncompressedPointSize = <::Output as Add>::Output; + +/// Compressed elliptic curve points serialized according to the +/// `Elliptic-Curve-Point-to-Octet-String` algorithm. +/// +/// See section 2.3.3 of SEC 1: Elliptic Curve Cryptography (Version 2.0): +/// +/// +#[derive(Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct CompressedPoint +where + CompressedPointSize: ArrayLength, +{ + /// Raw serialized bytes of the compressed point + bytes: GenericArray>, +} + +impl CompressedPoint +where + CompressedPointSize: ArrayLength, +{ + /// Compress and serialize an elliptic curve point from its affine coordinates + pub fn from_affine_coords( + x: &ScalarBytes, + y: &ScalarBytes, + ) -> Self { + // Is the y-coordinate odd in the SEC-1 sense: `self mod 2 == 1`? + let is_y_odd = y.as_ref().last().expect("last byte") & 1 == 1; + let mut bytes = GenericArray::default(); + bytes[0] = if is_y_odd { 0x03 } else { 0x02 }; + bytes[1..].copy_from_slice(x); + Self { bytes } + } + + /// Create a new compressed elliptic curve point + pub fn from_bytes(into_bytes: B) -> Option + where + B: Into>>, + { + let bytes = into_bytes.into(); + let tag_byte = bytes.as_ref()[0]; + + if tag_byte == 0x02 || tag_byte == 0x03 { + Some(Self { bytes }) + } else { + None + } + } + + /// Borrow byte slice containing compressed curve point + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + + /// Obtain owned array containing compressed curve point + #[inline] + pub fn into_bytes(self) -> GenericArray> { + self.bytes + } +} + +impl AsRef<[u8]> for CompressedPoint +where + CompressedPointSize: ArrayLength, +{ + #[inline] + fn as_ref(&self) -> &[u8] { + self.bytes.as_ref() + } +} + +impl Copy for CompressedPoint +where + CompressedPointSize: ArrayLength, + as ArrayLength>::ArrayType: Copy, +{ +} + +impl Clone for CompressedPoint +where + CompressedPointSize: ArrayLength, +{ + fn clone(&self) -> Self { + Self::from_bytes(self.bytes.clone()).unwrap() + } +} + +/// Uncompressed elliptic curve points serialized according to the +/// `Elliptic-Curve-Point-to-Octet-String` algorithm. +/// +/// See section 2.3.3 of SEC 1: Elliptic Curve Cryptography (Version 2.0): +/// +/// +#[derive(Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct UncompressedPoint +where + ::Output: Add, + UncompressedPointSize: ArrayLength, +{ + /// Raw serialized bytes of the uncompressed point + bytes: GenericArray>, +} + +impl UncompressedPoint +where + ::Output: Add, + UncompressedPointSize: ArrayLength, +{ + /// Serialize an elliptic curve point from its affine coordinates + pub fn from_affine_coords( + x: &ScalarBytes, + y: &ScalarBytes, + ) -> Self { + let scalar_size = C::ScalarSize::to_usize(); + let mut bytes = GenericArray::default(); + bytes[0] = 0x04; + bytes[1..(scalar_size + 1)].copy_from_slice(x); + bytes[(scalar_size + 1)..].copy_from_slice(y); + Self { bytes } + } + + /// Create a new uncompressed elliptic curve point + pub fn from_bytes(into_bytes: B) -> Option + where + B: Into>>, + { + let bytes = into_bytes.into(); + + if bytes.get(0) == Some(&0x04) { + Some(Self { bytes }) + } else { + None + } + } + + /// Borrow byte slice containing uncompressed curve point + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + + /// Convert public key into owned byte array + #[inline] + pub fn into_bytes(self) -> GenericArray> { + self.bytes + } +} + +impl AsRef<[u8]> for UncompressedPoint +where + ::Output: Add, + UncompressedPointSize: ArrayLength, +{ + #[inline] + fn as_ref(&self) -> &[u8] { + self.bytes.as_ref() + } +} + +impl Copy for UncompressedPoint +where + ::Output: Add, + UncompressedPointSize: ArrayLength, + as ArrayLength>::ArrayType: Copy, +{ +} + +impl Clone for UncompressedPoint +where + ::Output: Add, + UncompressedPointSize: ArrayLength, +{ + fn clone(&self) -> Self { + Self::from_bytes(self.bytes.clone()).unwrap() + } +} diff --git a/elliptic-curve/src/weierstrass/public_key.rs b/elliptic-curve/src/weierstrass/public_key.rs new file mode 100644 index 000000000..d882a9580 --- /dev/null +++ b/elliptic-curve/src/weierstrass/public_key.rs @@ -0,0 +1,187 @@ +//! Public keys for Weierstrass curves: wrapper around compressed or +//! uncompressed elliptic curve points. + +use super::{ + point::{CompressedPoint, CompressedPointSize, UncompressedPoint, UncompressedPointSize}, + Curve, FixedBaseScalarMul, +}; +use crate::SecretKey; +use core::fmt::{self, Debug}; +use core::ops::Add; +use generic_array::{ + typenum::{Unsigned, U1}, + ArrayLength, GenericArray, +}; + +/// Size of an untagged point for given elliptic curve. +// TODO(tarcieri): const generics +pub type UntaggedPointSize = ::Output; + +/// Public keys for Weierstrass curves +#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] +pub enum PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + /// Compressed Weierstrass elliptic curve point + Compressed(CompressedPoint), + + /// Uncompressed Weierstrass elliptic curve point + Uncompressed(UncompressedPoint), +} + +impl PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + /// Decode public key from an elliptic curve point + /// (compressed or uncompressed) encoded using the + /// `Elliptic-Curve-Point-to-Octet-String` algorithm described in + /// SEC 1: Elliptic Curve Cryptography (Version 2.0) section + /// 2.3.3 (page 10). + /// + /// + pub fn from_bytes>(bytes: B) -> Option { + let slice = bytes.as_ref(); + let length = slice.len(); + + if length == >::to_usize() { + let array = GenericArray::clone_from_slice(slice); + let point = CompressedPoint::from_bytes(array)?; + Some(PublicKey::Compressed(point)) + } else if length == >::to_usize() { + let array = GenericArray::clone_from_slice(slice); + let point = UncompressedPoint::from_bytes(array)?; + Some(PublicKey::Uncompressed(point)) + } else { + None + } + } + + /// Decode public key from an compressed elliptic curve point + /// encoded using the `Elliptic-Curve-Point-to-Octet-String` algorithm + /// described in SEC 1: Elliptic Curve Cryptography (Version 2.0) section + /// 2.3.3 (page 10). + /// + /// + pub fn from_compressed_point(into_bytes: B) -> Option + where + B: Into>>, + { + CompressedPoint::from_bytes(into_bytes).map(PublicKey::Compressed) + } + + /// Decode public key from a raw uncompressed point serialized + /// as a bytestring, without a `0x04`-byte tag. + /// + /// This will be twice the modulus size, or 1-byte smaller than the + /// `Elliptic-Curve-Point-to-Octet-String` encoding i.e + /// with the leading `0x04` byte in that encoding removed. + pub fn from_untagged_point(bytes: &GenericArray>) -> Self + where + ::Output: ArrayLength, + { + let mut tagged_bytes = GenericArray::default(); + tagged_bytes.as_mut_slice()[0] = 0x04; + tagged_bytes.as_mut_slice()[1..].copy_from_slice(bytes.as_ref()); + + PublicKey::Uncompressed(UncompressedPoint::from_bytes(tagged_bytes).unwrap()) + } + + /// Obtain public key as a byte array reference + #[inline] + pub fn as_bytes(&self) -> &[u8] { + match self { + PublicKey::Compressed(ref point) => point.as_bytes(), + PublicKey::Uncompressed(ref point) => point.as_bytes(), + } + } +} + +impl PublicKey +where + C: FixedBaseScalarMul, + ::Output: Add, + CompressedPoint: From, + UncompressedPoint: From, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + /// Compute the [`PublicKey`] for the provided [`SecretKey`]. + /// + /// The `compress` flag requests point compression. + pub fn from_secret_key(secret_key: &SecretKey, compress: bool) -> Option { + let ct_option = C::mul_base(secret_key.secret_scalar()); + + if ct_option.is_some().into() { + let affine_point = ct_option.unwrap(); + + if compress { + Some(PublicKey::Compressed(affine_point.into())) + } else { + Some(PublicKey::Uncompressed(affine_point.into())) + } + } else { + None + } + } +} + +impl AsRef<[u8]> for PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl Copy for PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, + as ArrayLength>::ArrayType: Copy, + as ArrayLength>::ArrayType: Copy, +{ +} + +impl Debug for PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PublicKey<{:?}>({:?})", C::default(), self.as_ref()) + } +} + +impl From> for PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + fn from(point: CompressedPoint) -> Self { + PublicKey::Compressed(point) + } +} + +impl From> for PublicKey +where + ::Output: Add, + CompressedPointSize: ArrayLength, + UncompressedPointSize: ArrayLength, +{ + fn from(point: UncompressedPoint) -> Self { + PublicKey::Uncompressed(point) + } +}