From 3702a7c5980be2c69ab9daad12352c4f318c202e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 14 Sep 2017 00:25:34 +0000 Subject: [PATCH 001/160] Initial commit. --- .gitignore | 14 ++ .travis.yml | 19 ++ CONTRIBUTING.md | 28 +++ Cargo.toml | 32 ++++ LICENSE | 28 +++ README.md | 98 ++++++++++ ...ubblesort-zines-secret-messages-cover.jpeg | Bin 0 -> 74941 bytes src/lib.rs | 140 ++++++++++++++ src/x25519.rs | 172 ++++++++++++++++++ 9 files changed, 531 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 res/bubblesort-zines-secret-messages-cover.jpeg create mode 100644 src/lib.rs create mode 100644 src/x25519.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2328c9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +target/ +**/*.rs.bk +Cargo.lock + +.cargo + +*~ +\#* +.\#* +*.swp +*.orig +*.bak + +*.s diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2080ffd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: rust + +rust: + - nightly + +env: + - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' + - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='bench' + - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='nightly' + +matrix: + include: + - rust: stable + env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std' + - rust: beta + env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std' + +script: + - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b60e709 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing to curve25519-dalek + +If you have questions or comments, please feel free to email the +authors. + +For feature requests, suggestions, and bug reports, please open an issue on +[our Github](https://github.com/isislovecruft/x25519-dalek). (Or, send us +an email if you're opposed to using Github for whatever reason.) + +Patches are welcomed as pull requests on +[our Github](https://github.com/isislovecruft/x25519-dalek), as well as by +email (preferably sent to all of the authors listed in `Cargo.toml`). + +All issues on curve25519-dalek are mentored, if you want help with a bug just +ask @isislovecruft. + +Some issues are easier than others. The `easy` label can be used to find the +easy issues. If you want to work on an issue, please leave a comment so that we +can assign it to you! + +# Code of Conduct + +We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html), +with the following additional clauses: + +* We respect the rights to privacy and anonymity for contributors and people in + the community. If someone wishes to contribute under a pseudonym different to + their primary identity, that wish is to be respected by all contributors. diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bd2d974 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "x25519-dalek" +version = "0.0.0" +authors = ["Isis Lovecruft "] +readme = "README.md" +license = "BSD-3-Clause" +repository = "https://github.com/isislovecruft/x25519-dalek" +documentation = "https://docs.rs/x25519-dalek" +categories = ["cryptography", "no-std"] +keywords = ["cryptography", "curve25519", "key-exchange", "x25519", "diffie-hellman"] +description = "X25519 elliptic curve Diffie-Hellman key exchange in pure-Rust, using curve25519-dalek." +exclude = [ + ".gitignore", + ".travis.yml", + "CONTRIBUTING.md", +] + +[badges] +travis-ci = { repository = "isislovecruft/x25519-dalek", branch = "master"} + +[dependencies.curve25519-dalek] +version = "^0.12" + +[dependencies.rand] +optional = true +version = "^0.3" + +[features] +bench = [] +default = ["std", "nightly"] +std = ["rand", "curve25519-dalek/std"] +nightly = ["curve25519-dalek/nightly"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20dcc41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2017 Isis Agora Lovecruft. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa6eb02 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ + +# x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/isislovecruft/x25519-dalek.svg?branch=master)](https://travis-ci.org/isislovecruft/x25519-dalek) + +A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, +as specified by Mike Hamburg and Adam Langley in +[RFC7748](https://tools.ietf.org/html/rfc7748), using +[curve25519-dalek](https://github.com/isislovecruft/curve25519-dalek). + +## Examples + +[![](https://raw.githubusercontent.com/isislovecruft/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) + +"Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) +copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) + +Alice and Bob are two adorable kittens who have lost their mittens, and they +wish to be able to send secret messages to each other to coordinate finding +them, otherwise—if their caretaker cat finds out—they will surely be called +naughty kittens and be given no pie! + +But the two kittens are quite clever. Even though their paws are still too big +and the rest of them is 90% fuzziness, these clever kittens have been studying +up on modern public key cryptography and have learned a nifty trick called +*elliptic curve Diffie-Hellman key exchange*. With the right incantations, the +kittens will be able to secretly organise to find their mittens, and then spend +the rest of the afternoon nomming some yummy pie! + +First, Alice uses `x25519_dalek::generate_secret()` and then +`x25519_dalek::generate_public()` to produce her secret and public keys: + +```rust +extern crate x25519_dalek; +extern crate rand; + +use x25519_dalek::generate_secret; +use x25519_dalek::generate_public; +use rand::OsRng; + +let mut alice_csprng = OsRng::new().unwrap(); +let alice_secret = generate_secret(&mut alice_csprng); +let alice_public = generate_public(&alice_secret); +``` + +Bob does the same: + +```rust +let mut bob_csprng = OsRng::new().unwrap(); +let bob_secret = generate_secret(&mut bob_csprng); +let bob_public = generate_public(&bob_secret); +``` + +Alice meows across the room, telling `alice_public` to Bob, and Bob +loudly meows `bob_public` back to Alice. Alice now computes her +shared secret with Bob by doing: + +```rust +use x25519_dalek::diffie_hellman; + +let shared_secret = diffie_hellman(&alice_secret, &bob_public.as_bytes()); +``` + +Similarly, Bob computes the same shared secret by doing: + +```rust +let shared_secret = diffie_hellman(&bob_secret, &alice_public.as_bytes()); +``` + +Voilá! Alice and Bob can now use their shared secret to encrypt their +meows, for example, by using it to generate a key and nonce for an +authenticated-encryption cipher. + +# Warnings + +[Our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) +(which this code uses) has received *one* formal cryptographic and security +review. It has not yet received what we would consider *sufficient* peer +review by other qualified cryptographers to be considered in any way, shape, +or form, safe. + +This code matches the test vectors, as specified in +[RFC7748](https://tools.ietf.org/html/rfc7748), however: + +**USE AT YOUR OWN RISK.** + +# Documentation + +Documentation is available [here](https://docs.rs/x25519-dalek). + +# Installation + +To install, add the following to your project's `Cargo.toml`: + + [dependencies.x25519-dalek] + version = "^0.0" + +Then, in your library or executable source, add: + + extern crate x25519_dalek diff --git a/res/bubblesort-zines-secret-messages-cover.jpeg b/res/bubblesort-zines-secret-messages-cover.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ca33298327849319aefb35ab9f8c2424caa0ab5a GIT binary patch literal 74941 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%0DDSW%hZg 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 literal 0 HcmV?d00001 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4297c57 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,140 @@ +// -*- mode: rust; -*- +// +// This file is part of x25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +//! x25519 Diffie-Hellman key exchange +//! +//! A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key +//! exchange as specified by Mike Hamburg and Adam Langley in +//! [RFC7748](https://tools.ietf.org/html/rfc7748). +//! +//! # Examples +//! +//! [![](https://raw.githubusercontent.com/isislovecruft/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) +//! +//! "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) +//! copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) +//! +//! Alice and Bob are two adorable kittens who have lost their mittens, and they +//! wish to be able to send secret messages to each other to coordinate finding +//! them, otherwise—if their caretaker cat finds out—they will surely be called +//! naughty kittens and be given no pie! +//! +//! But the two kittens are quite clever. Even though their paws are still too +//! big and the rest of them is 90% fuzziness, these clever kittens have been +//! studying up on modern public key cryptography and have learned a nifty trick +//! called *elliptic curve Diffie-Hellman key exchange*. With the right +//! incantations, the kittens will be able to secretly organise to find their +//! mittens, and then spend the rest of the afternoon nomming some yummy pie! +//! +//! First, Alice uses `x25519_dalek::generate_secret()` and +//! `x25519_dalek::generate_public()` to produce her secret and public keys: +//! +//! ``` +//! extern crate x25519_dalek; +//! extern crate rand; +//! +//! # fn main() { +//! use x25519_dalek::generate_secret; +//! use x25519_dalek::generate_public; +//! use rand::OsRng; +//! +//! let mut alice_csprng = OsRng::new().unwrap(); +//! let alice_secret = generate_secret(&mut alice_csprng); +//! let alice_public = generate_public(&alice_secret); +//! # } +//! ``` +//! +//! Bob does the same: +//! +//! ``` +//! # extern crate x25519_dalek; +//! # extern crate rand; +//! # +//! # fn main() { +//! # use x25519_dalek::generate_secret; +//! # use x25519_dalek::generate_public; +//! # use rand::OsRng; +//! # +//! let mut bob_csprng = OsRng::new().unwrap(); +//! let bob_secret = generate_secret(&mut bob_csprng); +//! let bob_public = generate_public(&bob_secret); +//! # } +//! ``` +//! +//! Alice meows across the room, telling `alice_public` to Bob, and Bob +//! loudly meows `bob_public` back to Alice. Alice now computes her +//! shared secret with Bob by doing: +//! +//! ``` +//! # extern crate x25519_dalek; +//! # extern crate rand; +//! # +//! # fn main() { +//! # use x25519_dalek::generate_secret; +//! # use x25519_dalek::generate_public; +//! # use rand::OsRng; +//! # +//! # let mut alice_csprng = OsRng::new().unwrap(); +//! # let alice_secret = generate_secret(&mut alice_csprng); +//! # let alice_public = generate_public(&alice_secret); +//! # +//! # let mut bob_csprng = OsRng::new().unwrap(); +//! # let bob_secret = generate_secret(&mut bob_csprng); +//! # let bob_public = generate_public(&bob_secret); +//! # +//! use x25519_dalek::diffie_hellman; +//! +//! let shared_secret = diffie_hellman(&alice_secret, &bob_public.as_bytes()); +//! # } +//! ``` +//! +//! Similarly, Bob computes the same shared secret by doing: +//! +//! ``` +//! # extern crate x25519_dalek; +//! # extern crate rand; +//! # +//! # fn main() { +//! # use x25519_dalek::diffie_hellman; +//! # use x25519_dalek::generate_secret; +//! # use x25519_dalek::generate_public; +//! # use rand::OsRng; +//! # +//! # let mut alice_csprng = OsRng::new().unwrap(); +//! # let alice_secret = generate_secret(&mut alice_csprng); +//! # let alice_public = generate_public(&alice_secret); +//! # +//! # let mut bob_csprng = OsRng::new().unwrap(); +//! # let bob_secret = generate_secret(&mut bob_csprng); +//! # let bob_public = generate_public(&bob_secret); +//! # +//! let shared_secret = diffie_hellman(&bob_secret, &alice_public.as_bytes()); +//! # } +//! ``` +//! +//! Voilá! Alice and Bob can now use their shared secret to encrypt their +//! meows, for example, by using it to generate a key and nonce for an +//! authenticated-encryption cipher. + +#![no_std] +#![cfg_attr(feature = "bench", feature(test))] +#![deny(missing_docs)] + +extern crate curve25519_dalek; + +#[cfg(feature = "std")] +extern crate rand; + +#[cfg(all(test, feature = "bench"))] +extern crate test; + +mod x25519; + +#[allow(missing_docs)] +pub use x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs new file mode 100644 index 0000000..a86fc11 --- /dev/null +++ b/src/x25519.rs @@ -0,0 +1,172 @@ +// -*- mode: rust; -*- +// +// This file is part of x25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +//! x25519 Diffie-Hellman key exchange +//! +//! This implements x25519 key exchange as specified by Mike Hamburg +//! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). + +use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; +use curve25519_dalek::montgomery::CompressedMontgomeryU; +use curve25519_dalek::montgomery::MontgomeryPoint; +use curve25519_dalek::scalar::Scalar; + +#[cfg(feature = "std")] +use rand::Rng; + +/// "Decode" a scalar from a 32-byte array. +/// +/// By "decode" here, what is really meant is applying key clamping by twiddling +/// some bits. +/// +/// # Returns +/// +/// A `Scalar`. +fn decode_scalar(scalar: &[u8; 32]) -> Scalar { + let mut s: [u8; 32] = scalar.clone(); + + s[0] &= 248; + s[31] &= 127; + s[31] |= 64; + + Scalar(s) +} + +/// Generate an x25519 secret key. +#[cfg(feature = "std")] +pub fn generate_secret(csprng: &mut T) -> [u8; 32] { + let mut bytes = [0u8; 32]; + csprng.fill_bytes(&mut bytes); + bytes +} + +/// Given an x25519 secret key, compute its corresponding public key. +pub fn generate_public(secret: &[u8; 32]) -> CompressedMontgomeryU { + (&decode_scalar(secret) * &ED25519_BASEPOINT_TABLE).to_montgomery().compress() +} + +/// The x25519 function, as specified in RFC7748. +pub fn x25519(scalar: &Scalar, point: &CompressedMontgomeryU) -> CompressedMontgomeryU { + let k: Scalar = decode_scalar(scalar.as_bytes()); + let u: MontgomeryPoint = point.decompress(); + + (&k * &u).compress() +} + +/// Utility function to make it easier to call `x25519()` with byte arrays as +/// inputs and outputs. +pub fn diffie_hellman(my_secret: &[u8; 32], their_public: &[u8; 32]) -> [u8; 32] { + x25519(&Scalar(*my_secret), &CompressedMontgomeryU(*their_public)).to_bytes() +} + + +#[cfg(test)] +mod test { + use curve25519_dalek::constants::BASE_COMPRESSED_MONTGOMERY; + use super::*; + + fn do_rfc7748_ladder_test1(input_scalar: &Scalar, + input_point: &CompressedMontgomeryU, + expected: &[u8; 32]) { + let result = x25519(&input_scalar, &input_point); + + assert_eq!(result.0, *expected); + } + + #[test] + fn rfc7748_ladder_test1_vectorset1() { + let input_scalar: Scalar = Scalar([ + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, + 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, + 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, + 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4, ]); + let input_point: CompressedMontgomeryU = CompressedMontgomeryU([ + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, + 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, + 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, + 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, ]); + let expected: [u8; 32] = [ + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, + 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, + 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, + 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52, ]; + + do_rfc7748_ladder_test1(&input_scalar, &input_point, &expected); + } + + #[test] + fn rfc7748_ladder_test1_vectorset2() { + let input_scalar: Scalar = Scalar([ + 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, + 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, + 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, + 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d, ]); + let input_point: CompressedMontgomeryU = CompressedMontgomeryU([ + 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, + 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, + 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, + 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, ]); + let expected: [u8; 32] = [ + 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, + 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, + 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, + 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57, ]; + + do_rfc7748_ladder_test1(&input_scalar, &input_point, &expected); + } + + #[test] + #[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations + fn rfc7748_ladder_test2() { + let mut k: Scalar = Scalar(BASE_COMPRESSED_MONTGOMERY.0); + let mut u: CompressedMontgomeryU = BASE_COMPRESSED_MONTGOMERY; + let mut result: CompressedMontgomeryU; + + macro_rules! do_iterations { + ($n:expr) => ( + for _ in 0..$n { + result = x25519(&k, &u); + // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE + // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS + // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH + // MY LIBRARY: + // + // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. + // + // ↓↓ DON'T DO THIS ↓↓ + u = CompressedMontgomeryU(k.as_bytes().clone()); + k = Scalar(result.to_bytes()); + } + ) + } + + // After one iteration: + // 422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079 + // After 1,000 iterations: + // 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51 + // After 1,000,000 iterations: + // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 + + do_iterations!(1); + assert_eq!(k.as_bytes(), &[ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, + 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, + 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, + 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79, ]); + do_iterations!(999); + assert_eq!(k.as_bytes(), &[ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, + 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, + 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, + 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51, ]); + do_iterations!(999_000); + assert_eq!(k.as_bytes(), &[ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, + 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, + 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, + 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24, ]); + } +} From 2f16d80b26d821dcce8e7f31097582316c189725 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 9 Oct 2017 04:33:04 +0000 Subject: [PATCH 002/160] Bump x25519-dalek version to 0.1.0. --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd2d974..9ab33be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.0.0" +version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index fa6eb02..2699744 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Documentation is available [here](https://docs.rs/x25519-dalek). To install, add the following to your project's `Cargo.toml`: [dependencies.x25519-dalek] - version = "^0.0" + version = "^0.1" Then, in your library or executable source, add: From f8c554dd97526dc51db7ebcbbd84541693cf3b66 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 24 Oct 2017 06:10:27 +0000 Subject: [PATCH 003/160] Move seldom-used import. --- src/x25519.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index a86fc11..0913d8a 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -68,7 +68,6 @@ pub fn diffie_hellman(my_secret: &[u8; 32], their_public: &[u8; 32]) -> [u8; 32] #[cfg(test)] mod test { - use curve25519_dalek::constants::BASE_COMPRESSED_MONTGOMERY; use super::*; fn do_rfc7748_ladder_test1(input_scalar: &Scalar, @@ -124,6 +123,8 @@ mod test { #[test] #[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations fn rfc7748_ladder_test2() { + use curve25519_dalek::constants::BASE_COMPRESSED_MONTGOMERY; + let mut k: Scalar = Scalar(BASE_COMPRESSED_MONTGOMERY.0); let mut u: CompressedMontgomeryU = BASE_COMPRESSED_MONTGOMERY; let mut result: CompressedMontgomeryU; From 86d23006fa6bb422c8642405e48b0a073ff0964c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Apr 2018 20:06:02 +0000 Subject: [PATCH 004/160] Add slack notifications for Travis results. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2080ffd..a4c1b93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,3 +17,8 @@ matrix: script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS + +notifications: + slack: + rooms: + - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots From 031179d111bd2b847c3a0294f57b30e06797b3ab Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Apr 2018 20:07:25 +0000 Subject: [PATCH 005/160] Use new repository links. --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9ab33be..9e8ebd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" -repository = "https://github.com/isislovecruft/x25519-dalek" +repository = "https://github.com/dalek-cryptography/x25519-dalek" +homepage = "https://dalek.rs/" documentation = "https://docs.rs/x25519-dalek" categories = ["cryptography", "no-std"] keywords = ["cryptography", "curve25519", "key-exchange", "x25519", "diffie-hellman"] @@ -16,7 +17,7 @@ exclude = [ ] [badges] -travis-ci = { repository = "isislovecruft/x25519-dalek", branch = "master"} +travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] version = "^0.12" From 7ab614cda4d1f9a722f5d2786dda9fa5b7956191 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 3 Apr 2018 15:00:10 -0700 Subject: [PATCH 006/160] make sure std does not leak through thie dependency --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 9e8ebd6..01da35c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] version = "^0.12" +default-features = false [dependencies.rand] optional = true From 659d6c258e756b52428c8d542dacca9431f1d4f2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Apr 2018 21:53:26 +0000 Subject: [PATCH 007/160] Add criterion benchmark for diffie_hellman() function. --- .travis.yml | 2 +- Cargo.toml | 8 +++++++- benches/x25519.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 benches/x25519.rs diff --git a/.travis.yml b/.travis.yml index a4c1b93..6af9239 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ rust: env: - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' - - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='bench' + - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='' - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='nightly' matrix: diff --git a/Cargo.toml b/Cargo.toml index 9e8ebd6..a547ef0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,14 @@ version = "^0.12" optional = true version = "^0.3" +[dev-dependencies] +criterion = "0.2" + +[[bench]] +name = "x25519" +harness = false + [features] -bench = [] default = ["std", "nightly"] std = ["rand", "curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly"] diff --git a/benches/x25519.rs b/benches/x25519.rs new file mode 100644 index 0000000..8203785 --- /dev/null +++ b/benches/x25519.rs @@ -0,0 +1,46 @@ +// -*- mode: rust; -*- +// +// This file is part of x25519-dalek. +// Copyright (c) 2017 Isis Lovecruft +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft + +//! Benchmark the Diffie-Hellman operation. + +#[macro_use] +extern crate criterion; +extern crate rand; +extern crate x25519_dalek; + +use criterion::Criterion; + +use rand::OsRng; + +use x25519_dalek::generate_public; +use x25519_dalek::generate_secret; +use x25519_dalek::diffie_hellman; + +fn bench_diffie_hellman(c: &mut Criterion) { + let mut csprng: OsRng = OsRng::new().unwrap(); + let alice_secret: [u8; 32] = generate_secret(&mut csprng); + let bob_secret: [u8; 32] = generate_secret(&mut csprng); + let bob_public: [u8; 32] = generate_public(&bob_secret).to_bytes(); + + c.bench_function("diffie_hellman", move |b| { + b.iter(|| + diffie_hellman(&alice_secret, &bob_public) + ) + }); +} + +criterion_group!{ + name = x25519_benches; + config = Criterion::default(); + targets = + bench_diffie_hellman, +} +criterion_main!{ + x25519_benches, +} From cb88ab169a1cc43a658d9f70047d455720f24dff Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Tue, 1 May 2018 20:12:30 -0700 Subject: [PATCH 008/160] Use latest curve25519-dalek. Fixes #5 (allows x25519-dalek to compile on stable). informed by 6748ddd: - sub `CompressedMontgomeryU` with `MontgomeryPoint`, removing (de)compress calls. informed by d32fe97: - sub `Scalar([u8; 32])` with `Scalar::from_bits([u8; 32])` --- Cargo.toml | 4 ++-- src/x25519.rs | 36 +++++++++++++++++------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9ab33be..5f6fa7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,11 +19,11 @@ exclude = [ travis-ci = { repository = "isislovecruft/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.12" +version = "^0.16" [dependencies.rand] optional = true -version = "^0.3" +version = "^0.4" [features] bench = [] diff --git a/src/x25519.rs b/src/x25519.rs index 0913d8a..c93ee4d 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -13,7 +13,6 @@ //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; -use curve25519_dalek::montgomery::CompressedMontgomeryU; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; @@ -35,7 +34,7 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { s[31] &= 127; s[31] |= 64; - Scalar(s) + Scalar::from_bits(s) } /// Generate an x25519 secret key. @@ -47,22 +46,21 @@ pub fn generate_secret(csprng: &mut T) -> [u8; 32] { } /// Given an x25519 secret key, compute its corresponding public key. -pub fn generate_public(secret: &[u8; 32]) -> CompressedMontgomeryU { - (&decode_scalar(secret) * &ED25519_BASEPOINT_TABLE).to_montgomery().compress() +pub fn generate_public(secret: &[u8; 32]) -> MontgomeryPoint { + (&decode_scalar(secret) * &ED25519_BASEPOINT_TABLE).to_montgomery() } /// The x25519 function, as specified in RFC7748. -pub fn x25519(scalar: &Scalar, point: &CompressedMontgomeryU) -> CompressedMontgomeryU { +pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { let k: Scalar = decode_scalar(scalar.as_bytes()); - let u: MontgomeryPoint = point.decompress(); - (&k * &u).compress() + (&k * point) } /// Utility function to make it easier to call `x25519()` with byte arrays as /// inputs and outputs. pub fn diffie_hellman(my_secret: &[u8; 32], their_public: &[u8; 32]) -> [u8; 32] { - x25519(&Scalar(*my_secret), &CompressedMontgomeryU(*their_public)).to_bytes() + x25519(&Scalar::from_bits(*my_secret), &MontgomeryPoint(*their_public)).to_bytes() } @@ -71,7 +69,7 @@ mod test { use super::*; fn do_rfc7748_ladder_test1(input_scalar: &Scalar, - input_point: &CompressedMontgomeryU, + input_point: &MontgomeryPoint, expected: &[u8; 32]) { let result = x25519(&input_scalar, &input_point); @@ -80,12 +78,12 @@ mod test { #[test] fn rfc7748_ladder_test1_vectorset1() { - let input_scalar: Scalar = Scalar([ + let input_scalar: Scalar = Scalar::from_bits([ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4, ]); - let input_point: CompressedMontgomeryU = CompressedMontgomeryU([ + let input_point: MontgomeryPoint = MontgomeryPoint([ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, @@ -101,12 +99,12 @@ mod test { #[test] fn rfc7748_ladder_test1_vectorset2() { - let input_scalar: Scalar = Scalar([ + let input_scalar: Scalar = Scalar::from_bits([ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d, ]); - let input_point: CompressedMontgomeryU = CompressedMontgomeryU([ + let input_point: MontgomeryPoint = MontgomeryPoint([ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, @@ -123,11 +121,11 @@ mod test { #[test] #[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations fn rfc7748_ladder_test2() { - use curve25519_dalek::constants::BASE_COMPRESSED_MONTGOMERY; + use curve25519_dalek::constants::X25519_BASEPOINT; - let mut k: Scalar = Scalar(BASE_COMPRESSED_MONTGOMERY.0); - let mut u: CompressedMontgomeryU = BASE_COMPRESSED_MONTGOMERY; - let mut result: CompressedMontgomeryU; + let mut k: Scalar = Scalar::from_bits(X25519_BASEPOINT.0); + let mut u: MontgomeryPoint = X25519_BASEPOINT; + let mut result: MontgomeryPoint; macro_rules! do_iterations { ($n:expr) => ( @@ -141,8 +139,8 @@ mod test { // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. // // ↓↓ DON'T DO THIS ↓↓ - u = CompressedMontgomeryU(k.as_bytes().clone()); - k = Scalar(result.to_bytes()); + u = MontgomeryPoint(k.as_bytes().clone()); + k = Scalar::from_bits(result.to_bytes()); } ) } From 3e9a5db419291001c32537eeb061b4c229e4a67d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 20:41:39 +0000 Subject: [PATCH 009/160] Update curve25519-dalek dependency to 0.17. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 400c84c..82ccdbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.16" +version = "^0.17" default-features = false [dependencies.rand] From d0c4da85e8d9ae3b3821ca675063c707da6aad8b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 21:00:28 +0000 Subject: [PATCH 010/160] Update rand dependency to 0.5.0-pre.2. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 82ccdbe..818d181 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ default-features = false [dependencies.rand] optional = true -version = "^0.4" +version = "=0.5.0-pre.2" [features] bench = [] From b13417e82c1ec62e3840748a730ea89e90c63ed6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 21:48:01 +0000 Subject: [PATCH 011/160] Expose backend options from curve25519-dalek. --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 27e3926..9ac5b25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ name = "x25519" harness = false [features] -default = ["std", "nightly"] +default = ["std", "nightly", "u64_backend"] std = ["rand", "curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly"] +u64_backend = ["curve25519-dalek/u64_backend"] +u32_backend = ["curve25519-dalek/u32_backend"] From 3a0b35dc38052fb2cb8e7775ca7dba2413e29c6d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 21:51:31 +0000 Subject: [PATCH 012/160] Add backend options to travis.yml build matrix. --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6af9239..a5b5c1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,17 @@ rust: - nightly env: - - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' - - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='' - - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='nightly' + - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default' + - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='default' + - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend nightly' + - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly' matrix: include: - rust: stable - env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std' + env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' - rust: beta - env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std' + env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS From d80bd7094f0ad0b8420c375dd0cad34394b183d0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 22:51:53 +0000 Subject: [PATCH 013/160] Bump x25519-dalek version to 0.2.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9ac5b25..1fb753e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.1.0" +version = "0.2.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From ba0bf474924ea997eff08865fd68d5f58cdabd1f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 23:00:06 +0000 Subject: [PATCH 014/160] Update links in README to point to new github org. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2699744..2594f30 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/isislovecruft/x25519-dalek.svg?branch=master)](https://travis-ci.org/isislovecruft/x25519-dalek) +# x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/isislovecruft/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, as specified by Mike Hamburg and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748), using -[curve25519-dalek](https://github.com/isislovecruft/curve25519-dalek). +[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). ## Examples -[![](https://raw.githubusercontent.com/isislovecruft/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) +[![](https://raw.githubusercontent.com/dalek-cryptography/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) @@ -71,7 +71,7 @@ authenticated-encryption cipher. # Warnings -[Our elliptic curve library](https://github.com/isislovecruft/curve25519-dalek) +[Our elliptic curve library](https://github.com/dalek-cryptography/curve25519-dalek) (which this code uses) has received *one* formal cryptographic and security review. It has not yet received what we would consider *sufficient* peer review by other qualified cryptographers to be considered in any way, shape, From f76e08deeca1a9ccad823e71db54191951ddbf6c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 23:00:27 +0000 Subject: [PATCH 015/160] Add syntax highlighting to README. --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2594f30..86c4c99 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,13 @@ Documentation is available [here](https://docs.rs/x25519-dalek). To install, add the following to your project's `Cargo.toml`: - [dependencies.x25519-dalek] - version = "^0.1" +```toml +[dependencies.x25519-dalek] +version = "^0.2" +``` Then, in your library or executable source, add: - extern crate x25519_dalek +```rust +extern crate x25519_dalek; +``` From 715208e0eba9994799adb674629ec45dcaa9348e Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Tue, 22 May 2018 22:42:58 -0700 Subject: [PATCH 016/160] use production rand 0.5 release --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1fb753e..51167fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ default-features = false [dependencies.rand] optional = true -version = "=0.5.0-pre.2" +version = "^0.5" [dev-dependencies] criterion = "0.2" From f9012cc1b3573bb314b339efa5ebcd3ac6142f18 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 23 Jul 2018 16:35:28 +0000 Subject: [PATCH 017/160] Bump curve25519-dalek dependency to 0.18. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 51167fc..ddd6f8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.17" +version = "^0.18" default-features = false [dependencies.rand] From 2abfb37cad52669cad1e407a0a8b3b019392e1a8 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 23 Jul 2018 16:44:38 +0000 Subject: [PATCH 018/160] Bump x25519-dalek version to 0.2.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ddd6f8f..3309056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.2.0" +version = "0.2.1" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" From 1aec62c6be9cda8d07f885f12109a05d565391a7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 23 Jul 2018 17:15:09 +0000 Subject: [PATCH 019/160] Switch to using traits from rand_core instead of rand. --- Cargo.toml | 9 +++++---- src/lib.rs | 28 +++++++++++++--------------- src/x25519.rs | 9 +++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3309056..e3cc2e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,13 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} version = "^0.18" default-features = false -[dependencies.rand] -optional = true -version = "^0.5" +[dependencies.rand_core] +default-features = false +version = "0.2" [dev-dependencies] criterion = "0.2" +rand = "0.5" [[bench]] name = "x25519" @@ -36,7 +37,7 @@ harness = false [features] default = ["std", "nightly", "u64_backend"] -std = ["rand", "curve25519-dalek/std"] +std = ["curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] diff --git a/src/lib.rs b/src/lib.rs index 4297c57..8119480 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,9 +42,9 @@ //! # fn main() { //! use x25519_dalek::generate_secret; //! use x25519_dalek::generate_public; -//! use rand::OsRng; +//! use rand::thread_rng; //! -//! let mut alice_csprng = OsRng::new().unwrap(); +//! let mut alice_csprng = thread_rng(); //! let alice_secret = generate_secret(&mut alice_csprng); //! let alice_public = generate_public(&alice_secret); //! # } @@ -59,9 +59,9 @@ //! # fn main() { //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::OsRng; +//! # use rand::thread_rng; //! # -//! let mut bob_csprng = OsRng::new().unwrap(); +//! let mut bob_csprng = thread_rng(); //! let bob_secret = generate_secret(&mut bob_csprng); //! let bob_public = generate_public(&bob_secret); //! # } @@ -78,13 +78,13 @@ //! # fn main() { //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::OsRng; +//! # use rand::thread_rng; //! # -//! # let mut alice_csprng = OsRng::new().unwrap(); +//! # let mut alice_csprng = thread_rng(); //! # let alice_secret = generate_secret(&mut alice_csprng); //! # let alice_public = generate_public(&alice_secret); //! # -//! # let mut bob_csprng = OsRng::new().unwrap(); +//! # let mut bob_csprng = thread_rng(); //! # let bob_secret = generate_secret(&mut bob_csprng); //! # let bob_public = generate_public(&bob_secret); //! # @@ -104,13 +104,13 @@ //! # use x25519_dalek::diffie_hellman; //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::OsRng; +//! # use rand::thread_rng; //! # -//! # let mut alice_csprng = OsRng::new().unwrap(); +//! # let mut alice_csprng = thread_rng(); //! # let alice_secret = generate_secret(&mut alice_csprng); //! # let alice_public = generate_public(&alice_secret); //! # -//! # let mut bob_csprng = OsRng::new().unwrap(); +//! # let mut bob_csprng = thread_rng(); //! # let bob_secret = generate_secret(&mut bob_csprng); //! # let bob_public = generate_public(&bob_secret); //! # @@ -128,13 +128,11 @@ extern crate curve25519_dalek; -#[cfg(feature = "std")] -extern crate rand; +extern crate rand_core; -#[cfg(all(test, feature = "bench"))] -extern crate test; +#[cfg(test)] +extern crate rand; mod x25519; -#[allow(missing_docs)] pub use x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs index c93ee4d..921b454 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -16,8 +16,8 @@ use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; -#[cfg(feature = "std")] -use rand::Rng; +use rand_core::RngCore; +use rand_core::CryptoRng; /// "Decode" a scalar from a 32-byte array. /// @@ -38,8 +38,9 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { } /// Generate an x25519 secret key. -#[cfg(feature = "std")] -pub fn generate_secret(csprng: &mut T) -> [u8; 32] { +pub fn generate_secret(csprng: &mut T) -> [u8; 32] + where T: RngCore + CryptoRng +{ let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); bytes From 2e6795c6e7612e2ae14badfc2b7970ee40ec717c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 20:17:01 +0000 Subject: [PATCH 020/160] Bump curve25519-dalek version to 0.19. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e3cc2e7..15db70d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.18" +version = "^0.19" default-features = false [dependencies.rand_core] From 751d28340a9de33f5a967004e6d6461f95c00182 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 20:18:51 +0000 Subject: [PATCH 021/160] Update a link in the README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86c4c99..13bbe1b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/isislovecruft/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) +# x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, as specified by Mike Hamburg and Adam Langley in From 41acc605382cbbde28781992e467c22be9ed2b70 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 27 Jul 2018 20:19:07 +0000 Subject: [PATCH 022/160] Bump x25519-dalek version to 0.3.0. --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 15db70d..4653b04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.2.1" +version = "0.3.0" authors = ["Isis Lovecruft "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index 13bbe1b..dca2a2e 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.x25519-dalek] -version = "^0.2" +version = "^0.3" ``` Then, in your library or executable source, add: From bd13f0728f278731f025dbae06b03af6a85e5c77 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Tue, 6 Nov 2018 14:00:52 -0500 Subject: [PATCH 023/160] add clear_on_drop to toml & add secret key type --- Cargo.toml | 5 ++++- src/lib.rs | 2 ++ src/x25519.rs | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4653b04..cd57d99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,9 @@ default-features = false default-features = false version = "0.2" +[dependencies.clear_on_drop] +version = "0.2" + [dev-dependencies] criterion = "0.2" rand = "0.5" @@ -38,6 +41,6 @@ harness = false [features] default = ["std", "nightly", "u64_backend"] std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly"] +nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] diff --git a/src/lib.rs b/src/lib.rs index 8119480..1dabcaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,8 @@ #![cfg_attr(feature = "bench", feature(test))] #![deny(missing_docs)] +extern crate clear_on_drop; + extern crate curve25519_dalek; extern crate rand_core; diff --git a/src/x25519.rs b/src/x25519.rs index 921b454..ccbcbcd 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -12,6 +12,10 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). +use core::fmt::{Debug}; + +use clear_on_drop::clear::Clear; + use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; @@ -19,6 +23,26 @@ use curve25519_dalek::scalar::Scalar; use rand_core::RngCore; use rand_core::CryptoRng; +/// The length of a curve25519 EdDSA `SecretKey`, in bytes. +pub const SECRET_KEY_LENGTH: usize = 32; + +/// An EdDSA secret key. +#[repr(C)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop +pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); + +impl Debug for SecretKey { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "SecretKey: {:?}", &self.0[..]) + } +} + +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for SecretKey { + fn drop(&mut self) { + self.0.clear(); + } +} /// "Decode" a scalar from a 32-byte array. /// /// By "decode" here, what is really meant is applying key clamping by twiddling From 34abee71f8e111ca429f365fe5e4c6ef7fa3d632 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Tue, 6 Nov 2018 16:51:57 -0500 Subject: [PATCH 024/160] generate secret key in impl & use the type --- src/x25519.rs | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index ccbcbcd..9e661ad 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -43,6 +43,32 @@ impl Drop for SecretKey { self.0.clear(); } } + +impl SecretKey { + /// Convert this secret key to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { + self.0 + } + + /// View this secret key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { + &self.0 + } + + /// Generate an x25519 secret key. + pub fn generate(csprng: &mut T) -> SecretKey + where T: RngCore + CryptoRng + { + let mut sk: SecretKey = SecretKey([0u8; 32]); + + csprng.fill_bytes(&mut sk.0); + + sk + } + +} /// "Decode" a scalar from a 32-byte array. /// /// By "decode" here, what is really meant is applying key clamping by twiddling @@ -61,31 +87,22 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { Scalar::from_bits(s) } -/// Generate an x25519 secret key. -pub fn generate_secret(csprng: &mut T) -> [u8; 32] - where T: RngCore + CryptoRng -{ - let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - bytes -} - /// Given an x25519 secret key, compute its corresponding public key. -pub fn generate_public(secret: &[u8; 32]) -> MontgomeryPoint { - (&decode_scalar(secret) * &ED25519_BASEPOINT_TABLE).to_montgomery() +pub fn generate_public(secret: &SecretKey) -> MontgomeryPoint { + (&decode_scalar(secret.as_bytes()) * &ED25519_BASEPOINT_TABLE).to_montgomery() } /// The x25519 function, as specified in RFC7748. pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { - let k: Scalar = decode_scalar(scalar.as_bytes()); + //let k: Scalar = decode_scalar(scalar); - (&k * point) + (scalar * point) } /// Utility function to make it easier to call `x25519()` with byte arrays as /// inputs and outputs. -pub fn diffie_hellman(my_secret: &[u8; 32], their_public: &[u8; 32]) -> [u8; 32] { - x25519(&Scalar::from_bits(*my_secret), &MontgomeryPoint(*their_public)).to_bytes() +pub fn diffie_hellman(my_secret: &SecretKey, their_public: &[u8; 32]) -> [u8; 32] { + x25519(&decode_scalar(my_secret.as_bytes()), &MontgomeryPoint(*their_public)).to_bytes() } From e93a7125d918e2887b4c3f2d5dce7d6691b59670 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Tue, 6 Nov 2018 17:04:18 -0500 Subject: [PATCH 025/160] revert change to fn x25519 --- src/x25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 9e661ad..812db1f 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -94,9 +94,9 @@ pub fn generate_public(secret: &SecretKey) -> MontgomeryPoint { /// The x25519 function, as specified in RFC7748. pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { - //let k: Scalar = decode_scalar(scalar); + let k: Scalar = decode_scalar(scalar.as_bytes()); - (scalar * point) + (k * point) } /// Utility function to make it easier to call `x25519()` with byte arrays as From 1b64afd83dd6f774aea578d2c2f6debe9ce7bc7d Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 10 Nov 2018 15:29:17 -0500 Subject: [PATCH 026/160] associate diffie_hellman, generate_public, generate_secret with the Ephemeral type; implement Mul for Ephemeral; create SharedSecret type & implement drop; change docs to reflect new methods on the Ephemeral type --- src/lib.rs | 46 ++++++++++++-------------- src/x25519.rs | 89 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1dabcaa..0c82e2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,21 +32,20 @@ //! incantations, the kittens will be able to secretly organise to find their //! mittens, and then spend the rest of the afternoon nomming some yummy pie! //! -//! First, Alice uses `x25519_dalek::generate_secret()` and -//! `x25519_dalek::generate_public()` to produce her secret and public keys: +//! First, Alice uses `x25519_dalek::Ephemeral::generate_secret()` and +//! `x25519_dalek::Ephemeral::generate_public()` to produce her secret and public keys: //! //! ``` //! extern crate x25519_dalek; //! extern crate rand; //! //! # fn main() { -//! use x25519_dalek::generate_secret; -//! use x25519_dalek::generate_public; +//! use x25519_dalek::Ephemeral; //! use rand::thread_rng; //! //! let mut alice_csprng = thread_rng(); -//! let alice_secret = generate_secret(&mut alice_csprng); -//! let alice_public = generate_public(&alice_secret); +//! let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); +//! let alice_public = Ephemeral::generate_public(&alice_secret); //! # } //! ``` //! @@ -57,13 +56,12 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::generate_secret; -//! # use x25519_dalek::generate_public; +//! # use x25519_dalek::Ephemeral; //! # use rand::thread_rng; //! # //! let mut bob_csprng = thread_rng(); -//! let bob_secret = generate_secret(&mut bob_csprng); -//! let bob_public = generate_public(&bob_secret); +//! let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); +//! let bob_public = Ephemeral::generate_public(&bob_secret); //! # } //! ``` //! @@ -76,21 +74,19 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::generate_secret; -//! # use x25519_dalek::generate_public; +//! # use x25519_dalek::Ephemeral; //! # use rand::thread_rng; //! # //! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = generate_secret(&mut alice_csprng); -//! # let alice_public = generate_public(&alice_secret); +//! # let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); +//! # let alice_public = Ephemeral::generate_public(&alice_secret); //! # //! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = generate_secret(&mut bob_csprng); -//! # let bob_public = generate_public(&bob_secret); +//! # let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); +//! # let bob_public = Ephemeral::generate_public(&bob_secret); //! # -//! use x25519_dalek::diffie_hellman; //! -//! let shared_secret = diffie_hellman(&alice_secret, &bob_public.as_bytes()); +//! let shared_secret = Ephemeral::diffie_hellman(&alice_secret, &bob_public); //! # } //! ``` //! @@ -101,20 +97,18 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::diffie_hellman; -//! # use x25519_dalek::generate_secret; -//! # use x25519_dalek::generate_public; +//! # use x25519_dalek::Ephemeral; //! # use rand::thread_rng; //! # //! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = generate_secret(&mut alice_csprng); -//! # let alice_public = generate_public(&alice_secret); +//! # let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); +//! # let alice_public = Ephemeral::generate_public(&alice_secret); //! # //! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = generate_secret(&mut bob_csprng); -//! # let bob_public = generate_public(&bob_secret); +//! # let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); +//! # let bob_public = Ephemeral::generate_public(&bob_secret); //! # -//! let shared_secret = diffie_hellman(&bob_secret, &alice_public.as_bytes()); +//! let shared_secret = Ephemeral::diffie_hellman(&bob_secret, &alice_public); //! # } //! ``` //! diff --git a/src/x25519.rs b/src/x25519.rs index 812db1f..59d4e42 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -12,7 +12,8 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use core::fmt::{Debug}; +use core::mem; +use core::ops::Mul; use clear_on_drop::clear::Clear; @@ -23,52 +24,80 @@ use curve25519_dalek::scalar::Scalar; use rand_core::RngCore; use rand_core::CryptoRng; -/// The length of a curve25519 EdDSA `SecretKey`, in bytes. -pub const SECRET_KEY_LENGTH: usize = 32; - -/// An EdDSA secret key. +/// A DH ephemeral key. #[repr(C)] #[derive(Default)] // we derive Default in order to use the clear() method in Drop -pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); +pub struct Ephemeral(pub (crate) Scalar); -impl Debug for SecretKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "SecretKey: {:?}", &self.0[..]) - } -} - -/// Overwrite secret key material with null bytes when it goes out of scope. -impl Drop for SecretKey { +/// Overwrite ephemeral key material with null bytes when it goes out of scope. +impl Drop for Ephemeral { fn drop(&mut self) { self.0.clear(); } } -impl SecretKey { - /// Convert this secret key to a byte array. +/// Multiply this `Ephemeral` key by a `MontgomeryPoint`. +impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Ephemeral { + type Output = Ephemeral; + + fn mul(self, point: &'b MontgomeryPoint) -> Ephemeral { + Ephemeral(Scalar::from_bits((point * self.to_bytes()).to_bytes())) + } +} + +impl Ephemeral { + /// Convert this `Ephemeral` key to a `Scalar`. #[inline] - pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] { + pub fn to_bytes(&self) -> Scalar { self.0 } - /// View this secret key as a byte array. + /// View this `Ephemeral` key as a `Scalar`. #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; SECRET_KEY_LENGTH] { + pub fn as_bytes<'a>(&'a self) -> &'a Scalar { &self.0 } - /// Generate an x25519 secret key. - pub fn generate(csprng: &mut T) -> SecretKey + /// Utility function to make it easier to call `x25519()` with + /// an ephemeral secret key and montegomery point as input and + /// a shared secret as the output. + pub fn diffie_hellman(&self, their_public: &MontgomeryPoint) -> SharedSecret { + SharedSecret(x25519(self.as_bytes(), &MontgomeryPoint(*their_public.as_bytes()))) + } + + /// Generate an x25519 `Ephemeral` secret key. + pub fn generate_secret(csprng: &mut T) -> Self where T: RngCore + CryptoRng { - let mut sk: SecretKey = SecretKey([0u8; 32]); + let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut sk.0); + csprng.fill_bytes(&mut bytes); - sk + Ephemeral(decode_scalar(&bytes)) + } + + /// Given an x25519 `Ephemeral` secret key, compute its corresponding public key. + pub fn generate_public(&self) -> MontgomeryPoint { + (self.as_bytes() * &ED25519_BASEPOINT_TABLE).to_montgomery() } } + +#[repr(C)] +/// A DH SharedSecret +pub struct SharedSecret(pub (crate) MontgomeryPoint); + +/// Overwrite shared secret material with null bytes when it goes out of scope. +impl Drop for SharedSecret { + fn drop(&mut self) { + let bytes: &mut [u8; 32] = unsafe { + mem::transmute::<&mut MontgomeryPoint, &mut [u8; 32]> + (&mut self.0) + }; + bytes.clear(); + } +} + /// "Decode" a scalar from a 32-byte array. /// /// By "decode" here, what is really meant is applying key clamping by twiddling @@ -87,11 +116,6 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { Scalar::from_bits(s) } -/// Given an x25519 secret key, compute its corresponding public key. -pub fn generate_public(secret: &SecretKey) -> MontgomeryPoint { - (&decode_scalar(secret.as_bytes()) * &ED25519_BASEPOINT_TABLE).to_montgomery() -} - /// The x25519 function, as specified in RFC7748. pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { let k: Scalar = decode_scalar(scalar.as_bytes()); @@ -99,13 +123,6 @@ pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { (k * point) } -/// Utility function to make it easier to call `x25519()` with byte arrays as -/// inputs and outputs. -pub fn diffie_hellman(my_secret: &SecretKey, their_public: &[u8; 32]) -> [u8; 32] { - x25519(&decode_scalar(my_secret.as_bytes()), &MontgomeryPoint(*their_public)).to_bytes() -} - - #[cfg(test)] mod test { use super::*; From cb18af7c1b18b9b4e2f60b196d431adbbd8e923d Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 10 Nov 2018 16:17:19 -0500 Subject: [PATCH 027/160] update readme & fix benches --- README.md | 21 ++++++++++----------- benches/x25519.rs | 15 ++++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index dca2a2e..cbf6a33 100644 --- a/README.md +++ b/README.md @@ -25,28 +25,27 @@ up on modern public key cryptography and have learned a nifty trick called kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie! -First, Alice uses `x25519_dalek::generate_secret()` and then -`x25519_dalek::generate_public()` to produce her secret and public keys: +First, Alice uses `x25519_dalek::Ephemeral::generate_secret()` and then +`x25519_dalek::Ephemeral::generate_public()` to produce her secret and public keys: ```rust extern crate x25519_dalek; extern crate rand; -use x25519_dalek::generate_secret; -use x25519_dalek::generate_public; +use x25519_dalek::Ephemeral; use rand::OsRng; let mut alice_csprng = OsRng::new().unwrap(); -let alice_secret = generate_secret(&mut alice_csprng); -let alice_public = generate_public(&alice_secret); +let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); +let alice_public = Ephemeral::generate_public(&alice_secret); ``` Bob does the same: ```rust let mut bob_csprng = OsRng::new().unwrap(); -let bob_secret = generate_secret(&mut bob_csprng); -let bob_public = generate_public(&bob_secret); +let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); +let bob_public = Ephemeral::generate_public(&bob_secret); ``` Alice meows across the room, telling `alice_public` to Bob, and Bob @@ -54,15 +53,15 @@ loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: ```rust -use x25519_dalek::diffie_hellman; +use x25519_dalek::Ephemeral; -let shared_secret = diffie_hellman(&alice_secret, &bob_public.as_bytes()); +let shared_secret = Ephemeral::diffie_hellman(&alice_secret, &bob_public); ``` Similarly, Bob computes the same shared secret by doing: ```rust -let shared_secret = diffie_hellman(&bob_secret, &alice_public.as_bytes()); +let shared_secret = Ephemeral::diffie_hellman(&bob_secret, &alice_public); ``` Voilá! Alice and Bob can now use their shared secret to encrypt their diff --git a/benches/x25519.rs b/benches/x25519.rs index 8203785..76cc00b 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -11,26 +11,27 @@ #[macro_use] extern crate criterion; +extern crate curve25519_dalek; extern crate rand; extern crate x25519_dalek; use criterion::Criterion; +use curve25519_dalek::montgomery::MontgomeryPoint; + use rand::OsRng; -use x25519_dalek::generate_public; -use x25519_dalek::generate_secret; -use x25519_dalek::diffie_hellman; +use x25519_dalek::Ephemeral; fn bench_diffie_hellman(c: &mut Criterion) { let mut csprng: OsRng = OsRng::new().unwrap(); - let alice_secret: [u8; 32] = generate_secret(&mut csprng); - let bob_secret: [u8; 32] = generate_secret(&mut csprng); - let bob_public: [u8; 32] = generate_public(&bob_secret).to_bytes(); + let alice_secret: Ephemeral = Ephemeral::generate_secret(&mut csprng); + let bob_secret: Ephemeral = Ephemeral::generate_secret(&mut csprng); + let bob_public: MontgomeryPoint = Ephemeral::generate_public(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter(|| - diffie_hellman(&alice_secret, &bob_public) + Ephemeral::diffie_hellman(&alice_secret, &bob_public) ) }); } From 4c6498ec69a1ee570354d6c1ee8aefded29e43ee Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Fri, 16 Nov 2018 12:48:49 -0500 Subject: [PATCH 028/160] change impl Drop for SharedSecret to use clear directly --- src/x25519.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 59d4e42..12b7436 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -12,7 +12,6 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use core::mem; use core::ops::Mul; use clear_on_drop::clear::Clear; @@ -72,7 +71,7 @@ impl Ephemeral { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); - + Ephemeral(decode_scalar(&bytes)) } @@ -83,18 +82,15 @@ impl Ephemeral { } -#[repr(C)] /// A DH SharedSecret +#[repr(C)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SharedSecret(pub (crate) MontgomeryPoint); /// Overwrite shared secret material with null bytes when it goes out of scope. impl Drop for SharedSecret { fn drop(&mut self) { - let bytes: &mut [u8; 32] = unsafe { - mem::transmute::<&mut MontgomeryPoint, &mut [u8; 32]> - (&mut self.0) - }; - bytes.clear(); + self.0.clear(); } } @@ -131,7 +127,7 @@ mod test { input_point: &MontgomeryPoint, expected: &[u8; 32]) { let result = x25519(&input_scalar, &input_point); - + assert_eq!(result.0, *expected); } @@ -210,7 +206,7 @@ mod test { // 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51 // After 1,000,000 iterations: // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 - + do_iterations!(1); assert_eq!(k.as_bytes(), &[ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, From 98826dabf60eba6b704bcf5a5ee7aaf73971523e Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Mon, 19 Nov 2018 12:28:05 -0500 Subject: [PATCH 029/160] change curve dependency to 1.0.0-pre.1 at a minimum --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd57d99..05b1e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^0.19" +version = "^1.0.0-pre.1" default-features = false [dependencies.rand_core] From 7cf01d82d97ea2bc5e5433f03c320c7ec89c7ec0 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Mon, 19 Nov 2018 13:27:29 -0500 Subject: [PATCH 030/160] rand to 0.6 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 05b1e93..4dd0e4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ version = "0.2" [dev-dependencies] criterion = "0.2" -rand = "0.5" +rand = "0.6" [[bench]] name = "x25519" From a59964440684208eb44210973de20d5400725b33 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Tue, 27 Nov 2018 17:02:38 -0500 Subject: [PATCH 031/160] remove pub from x25519 fn --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index 12b7436..6817c09 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -113,7 +113,7 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { } /// The x25519 function, as specified in RFC7748. -pub fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { +fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { let k: Scalar = decode_scalar(scalar.as_bytes()); (k * point) From 7d80f9633a81f4591c0d3589b1e74b2de06058bc Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 28 Nov 2018 02:33:55 -0500 Subject: [PATCH 032/160] Update src/lib.rs Co-Authored-By: DebugSteven --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0c82e2b..93b36bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,7 +85,7 @@ //! # let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); //! # let bob_public = Ephemeral::generate_public(&bob_secret); //! # -//! +//! # //! let shared_secret = Ephemeral::diffie_hellman(&alice_secret, &bob_public); //! # } //! ``` From 734abac70be3256aeea450afbd87341841fdec0b Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sun, 2 Dec 2018 14:23:09 -0500 Subject: [PATCH 033/160] wrap MontgomeryPoint in EphemeralPublic, impl From; remove byte fns for EphemeralSecret --- README.md | 12 ++++----- benches/x25519.rs | 11 ++++---- src/lib.rs | 44 +++++++++++++++++--------------- src/x25519.rs | 65 +++++++++++++++++++++++++---------------------- 4 files changed, 71 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index cbf6a33..3d84aa7 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ up on modern public key cryptography and have learned a nifty trick called kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie! -First, Alice uses `x25519_dalek::Ephemeral::generate_secret()` and then -`x25519_dalek::Ephemeral::generate_public()` to produce her secret and public keys: +First, Alice uses `x25519_dalek::EphemeralSecret::new()` and then +`x25519_dalek::EphemeralPublic::generate_public()` to produce her secret and public keys: ```rust extern crate x25519_dalek; @@ -36,16 +36,16 @@ use x25519_dalek::Ephemeral; use rand::OsRng; let mut alice_csprng = OsRng::new().unwrap(); -let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); -let alice_public = Ephemeral::generate_public(&alice_secret); +let alice_secret = EphemeralSecret::new(&mut alice_csprng); +let alice_public = EphemeralPublic::generate_public(&alice_secret); ``` Bob does the same: ```rust let mut bob_csprng = OsRng::new().unwrap(); -let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); -let bob_public = Ephemeral::generate_public(&bob_secret); +let bob_secret = EphemeralSecret::new(&mut bob_csprng); +let bob_public = EphemeralPublic::generate_public(&bob_secret); ``` Alice meows across the room, telling `alice_public` to Bob, and Bob diff --git a/benches/x25519.rs b/benches/x25519.rs index 76cc00b..69bcda7 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -21,17 +21,18 @@ use curve25519_dalek::montgomery::MontgomeryPoint; use rand::OsRng; -use x25519_dalek::Ephemeral; +use x25519_dalek::EphemeralPublic; +use x25519_dalek::EphemeralSecret; fn bench_diffie_hellman(c: &mut Criterion) { let mut csprng: OsRng = OsRng::new().unwrap(); - let alice_secret: Ephemeral = Ephemeral::generate_secret(&mut csprng); - let bob_secret: Ephemeral = Ephemeral::generate_secret(&mut csprng); - let bob_public: MontgomeryPoint = Ephemeral::generate_public(&bob_secret); + let alice_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); + let bob_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); + let bob_public: EphemeralPublic = EphemeralPublic::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter(|| - Ephemeral::diffie_hellman(&alice_secret, &bob_public) + EphemeralSecret::diffie_hellman(&alice_secret, &bob_public) ) }); } diff --git a/src/lib.rs b/src/lib.rs index 0c82e2b..cd34301 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,20 +32,21 @@ //! incantations, the kittens will be able to secretly organise to find their //! mittens, and then spend the rest of the afternoon nomming some yummy pie! //! -//! First, Alice uses `x25519_dalek::Ephemeral::generate_secret()` and -//! `x25519_dalek::Ephemeral::generate_public()` to produce her secret and public keys: +//! First, Alice uses `x25519_dalek::EphemeralSecret::new()` and +//! `x25519_dalek::EphemeralPublic::from()` to produce her secret and public keys: //! //! ``` //! extern crate x25519_dalek; //! extern crate rand; //! //! # fn main() { -//! use x25519_dalek::Ephemeral; +//! use x25519_dalek::EphemeralPublic; +//! use x25519_dalek::EphemeralSecret; //! use rand::thread_rng; //! //! let mut alice_csprng = thread_rng(); -//! let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); -//! let alice_public = Ephemeral::generate_public(&alice_secret); +//! let alice_secret = EphemeralSecret::new(&mut alice_csprng); +//! let alice_public = EphemeralPublic::from(&alice_secret); //! # } //! ``` //! @@ -56,12 +57,13 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::Ephemeral; +//! # use x25519_dalek::EphemeralPublic; +//! # use x25519_dalek::EphemeralSecret; //! # use rand::thread_rng; //! # //! let mut bob_csprng = thread_rng(); -//! let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); -//! let bob_public = Ephemeral::generate_public(&bob_secret); +//! let bob_secret = EphemeralSecret::new(&mut bob_csprng); +//! let bob_public = EphemeralPublic::from(&bob_secret); //! # } //! ``` //! @@ -74,19 +76,20 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::Ephemeral; +//! # use x25519_dalek::EphemeralPublic; +//! # use x25519_dalek::EphemeralSecret; //! # use rand::thread_rng; //! # //! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); -//! # let alice_public = Ephemeral::generate_public(&alice_secret); +//! # let alice_secret = EphemeralSecret::new(&mut alice_csprng); +//! # let alice_public = EphemeralPublic::from(&alice_secret); //! # //! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); -//! # let bob_public = Ephemeral::generate_public(&bob_secret); +//! # let bob_secret = EphemeralSecret::new(&mut bob_csprng); +//! # let bob_public = EphemeralPublic::from(&bob_secret); //! # //! -//! let shared_secret = Ephemeral::diffie_hellman(&alice_secret, &bob_public); +//! let shared_secret = EphemeralSecret::diffie_hellman(&alice_secret, &bob_public); //! # } //! ``` //! @@ -97,18 +100,19 @@ //! # extern crate rand; //! # //! # fn main() { -//! # use x25519_dalek::Ephemeral; +//! # use x25519_dalek::EphemeralPublic; +//! # use x25519_dalek::EphemeralSecret; //! # use rand::thread_rng; //! # //! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = Ephemeral::generate_secret(&mut alice_csprng); -//! # let alice_public = Ephemeral::generate_public(&alice_secret); +//! # let alice_secret = EphemeralSecret::new(&mut alice_csprng); +//! # let alice_public = EphemeralPublic::from(&alice_secret); //! # //! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = Ephemeral::generate_secret(&mut bob_csprng); -//! # let bob_public = Ephemeral::generate_public(&bob_secret); +//! # let bob_secret = EphemeralSecret::new(&mut bob_csprng); +//! # let bob_public = EphemeralPublic::from(&bob_secret); //! # -//! let shared_secret = Ephemeral::diffie_hellman(&bob_secret, &alice_public); +//! let shared_secret = EphemeralSecret::diffie_hellman(&bob_secret, &alice_public); //! # } //! ``` //! diff --git a/src/x25519.rs b/src/x25519.rs index 6817c09..87cc187 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -23,61 +23,57 @@ use curve25519_dalek::scalar::Scalar; use rand_core::RngCore; use rand_core::CryptoRng; -/// A DH ephemeral key. +/// A DH ephemeral public key. +#[repr(C)] +pub struct EphemeralPublic(pub (crate) MontgomeryPoint); + +/// A DH ephemeral secret key. #[repr(C)] #[derive(Default)] // we derive Default in order to use the clear() method in Drop -pub struct Ephemeral(pub (crate) Scalar); +pub struct EphemeralSecret(pub (crate) Scalar); -/// Overwrite ephemeral key material with null bytes when it goes out of scope. -impl Drop for Ephemeral { +/// Overwrite ephemeral secret key material with null bytes when it goes out of scope. +impl Drop for EphemeralSecret { fn drop(&mut self) { self.0.clear(); } } -/// Multiply this `Ephemeral` key by a `MontgomeryPoint`. -impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Ephemeral { - type Output = Ephemeral; +/// Multiply this `EphemeralPublic` key by a `EphemeralSecret` key. +impl<'a, 'b> Mul<&'b EphemeralSecret> for &'a EphemeralPublic { + type Output = EphemeralPublic; - fn mul(self, point: &'b MontgomeryPoint) -> Ephemeral { - Ephemeral(Scalar::from_bits((point * self.to_bytes()).to_bytes())) + fn mul(self, secret: &'b EphemeralSecret) -> EphemeralPublic { + EphemeralPublic(self.0 * secret.0) } } -impl Ephemeral { - /// Convert this `Ephemeral` key to a `Scalar`. - #[inline] - pub fn to_bytes(&self) -> Scalar { - self.0 - } - - /// View this `Ephemeral` key as a `Scalar`. - #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a Scalar { - &self.0 - } - +impl EphemeralSecret { /// Utility function to make it easier to call `x25519()` with /// an ephemeral secret key and montegomery point as input and /// a shared secret as the output. - pub fn diffie_hellman(&self, their_public: &MontgomeryPoint) -> SharedSecret { - SharedSecret(x25519(self.as_bytes(), &MontgomeryPoint(*their_public.as_bytes()))) + pub fn diffie_hellman(&self, their_public: &EphemeralPublic) -> SharedSecret { + SharedSecret(x25519(&self.0, &MontgomeryPoint(*their_public.0.as_bytes()))) } - /// Generate an x25519 `Ephemeral` secret key. - pub fn generate_secret(csprng: &mut T) -> Self + /// Generate an x25519 `EphemeralSecret` key. + pub fn new(csprng: &mut T) -> Self where T: RngCore + CryptoRng { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); - Ephemeral(decode_scalar(&bytes)) + EphemeralSecret(decode_scalar(&bytes)) } - /// Given an x25519 `Ephemeral` secret key, compute its corresponding public key. - pub fn generate_public(&self) -> MontgomeryPoint { - (self.as_bytes() * &ED25519_BASEPOINT_TABLE).to_montgomery() +} + +impl From<&EphemeralSecret> for EphemeralPublic { + /// Given an x25519 `EphemeralSecret` key, compute its corresponding + /// `EphemeralPublic` key. + fn from(secret: &EphemeralSecret) -> EphemeralPublic { + EphemeralPublic((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } } @@ -94,6 +90,15 @@ impl Drop for SharedSecret { } } +impl SharedSecret { + + /// View this shared secret key as a byte array. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { + &self.0.as_bytes() + } +} + /// "Decode" a scalar from a 32-byte array. /// /// By "decode" here, what is really meant is applying key clamping by twiddling From 7f822ff3dda8bad86491a528696967d3db8100e3 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Mon, 3 Dec 2018 12:20:33 -0500 Subject: [PATCH 034/160] explicit lifetimes --- src/x25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 87cc187..c3dc48d 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -69,10 +69,10 @@ impl EphemeralSecret { } -impl From<&EphemeralSecret> for EphemeralPublic { +impl<'a> From<&'a EphemeralSecret> for EphemeralPublic { /// Given an x25519 `EphemeralSecret` key, compute its corresponding /// `EphemeralPublic` key. - fn from(secret: &EphemeralSecret) -> EphemeralPublic { + fn from(secret: &'a EphemeralSecret) -> EphemeralPublic { EphemeralPublic((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } From ccf7e5bb57dbd6fbfac9f3afe2c17c23ebc4cc9b Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Mon, 3 Dec 2018 13:27:35 -0500 Subject: [PATCH 035/160] remove mul; remove derive default on SharedSecret; rename decode to clamp --- src/x25519.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index c3dc48d..547b6b3 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -12,8 +12,6 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use core::ops::Mul; - use clear_on_drop::clear::Clear; use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; @@ -39,15 +37,6 @@ impl Drop for EphemeralSecret { } } -/// Multiply this `EphemeralPublic` key by a `EphemeralSecret` key. -impl<'a, 'b> Mul<&'b EphemeralSecret> for &'a EphemeralPublic { - type Output = EphemeralPublic; - - fn mul(self, secret: &'b EphemeralSecret) -> EphemeralPublic { - EphemeralPublic(self.0 * secret.0) - } -} - impl EphemeralSecret { /// Utility function to make it easier to call `x25519()` with /// an ephemeral secret key and montegomery point as input and @@ -64,7 +53,7 @@ impl EphemeralSecret { csprng.fill_bytes(&mut bytes); - EphemeralSecret(decode_scalar(&bytes)) + EphemeralSecret(clamp_scalar(&bytes)) } } @@ -80,7 +69,6 @@ impl<'a> From<&'a EphemeralSecret> for EphemeralPublic { /// A DH SharedSecret #[repr(C)] -#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SharedSecret(pub (crate) MontgomeryPoint); /// Overwrite shared secret material with null bytes when it goes out of scope. @@ -94,7 +82,7 @@ impl SharedSecret { /// View this shared secret key as a byte array. #[inline] - pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { + pub fn as_bytes(&self) -> &[u8; 32] { &self.0.as_bytes() } } @@ -107,7 +95,7 @@ impl SharedSecret { /// # Returns /// /// A `Scalar`. -fn decode_scalar(scalar: &[u8; 32]) -> Scalar { +fn clamp_scalar(scalar: &[u8; 32]) -> Scalar { let mut s: [u8; 32] = scalar.clone(); s[0] &= 248; @@ -119,7 +107,7 @@ fn decode_scalar(scalar: &[u8; 32]) -> Scalar { /// The x25519 function, as specified in RFC7748. fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { - let k: Scalar = decode_scalar(scalar.as_bytes()); + let k: Scalar = clamp_scalar(scalar.as_bytes()); (k * point) } From 8730bfbba6d56b675391121e982e86a7c6b399a8 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Fri, 7 Dec 2018 17:07:24 -0500 Subject: [PATCH 036/160] move implementation of x25519 into diffie_hellman --- README.md | 4 ++-- benches/x25519.rs | 7 ++++--- src/lib.rs | 4 ++-- src/x25519.rs | 29 ++++++++++++----------------- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d5b850a..63744eb 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ shared secret with Bob by doing: use x25519_dalek::EphemeralPublic; use x25519_dalek::EphemeralSecret; -let shared_secret = EphemeralSecret::diffie_hellman(&alice_secret, &bob_public); +let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); ``` Similarly, Bob computes the same shared secret by doing: ```rust -let shared_secret = EphemeralSecret::diffie_hellman(&bob_secret, &alice_public); +let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); ``` Voilá! Alice and Bob can now use their shared secret to encrypt their diff --git a/benches/x25519.rs b/benches/x25519.rs index 69bcda7..292393c 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -26,13 +26,14 @@ use x25519_dalek::EphemeralSecret; fn bench_diffie_hellman(c: &mut Criterion) { let mut csprng: OsRng = OsRng::new().unwrap(); - let alice_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); let bob_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); let bob_public: EphemeralPublic = EphemeralPublic::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { - b.iter(|| - EphemeralSecret::diffie_hellman(&alice_secret, &bob_public) + let alice_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); + b.iter_with_setup( + || EphemeralSecret::new(&mut csprng), + |alice_secret| EphemeralSecret::diffie_hellman(alice_secret, &bob_public), ) }); } diff --git a/src/lib.rs b/src/lib.rs index 6fe97b2..1e32b77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ //! # let bob_public = EphemeralPublic::from(&bob_secret); //! # //! # -//! let shared_secret = EphemeralSecret::diffie_hellman(&alice_secret, &bob_public); +//! let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); //! # } //! ``` //! @@ -112,7 +112,7 @@ //! # let bob_secret = EphemeralSecret::new(&mut bob_csprng); //! # let bob_public = EphemeralPublic::from(&bob_secret); //! # -//! let shared_secret = EphemeralSecret::diffie_hellman(&bob_secret, &alice_public); +//! let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); //! # } //! ``` //! diff --git a/src/x25519.rs b/src/x25519.rs index 547b6b3..05590b1 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -38,11 +38,13 @@ impl Drop for EphemeralSecret { } impl EphemeralSecret { - /// Utility function to make it easier to call `x25519()` with - /// an ephemeral secret key and montegomery point as input and - /// a shared secret as the output. - pub fn diffie_hellman(&self, their_public: &EphemeralPublic) -> SharedSecret { - SharedSecret(x25519(&self.0, &MontgomeryPoint(*their_public.0.as_bytes()))) + /// The diffie_hellman function performs scalar multipication on a montegomery point. + /// This is the implementation for the x25519 function, as specified in RFC7748. + pub fn diffie_hellman(self, their_public: &EphemeralPublic) -> SharedSecret { + let k: Scalar = clamp_scalar(self.0.as_bytes()); + let point: MontgomeryPoint = MontgomeryPoint(*their_public.0.as_bytes()); + + SharedSecret(k * point) } /// Generate an x25519 `EphemeralSecret` key. @@ -105,13 +107,6 @@ fn clamp_scalar(scalar: &[u8; 32]) -> Scalar { Scalar::from_bits(s) } -/// The x25519 function, as specified in RFC7748. -fn x25519(scalar: &Scalar, point: &MontgomeryPoint) -> MontgomeryPoint { - let k: Scalar = clamp_scalar(scalar.as_bytes()); - - (k * point) -} - #[cfg(test)] mod test { use super::*; @@ -119,9 +114,9 @@ mod test { fn do_rfc7748_ladder_test1(input_scalar: &Scalar, input_point: &MontgomeryPoint, expected: &[u8; 32]) { - let result = x25519(&input_scalar, &input_point); + let result = EphemeralSecret::diffie_hellman(EphemeralSecret(*input_scalar), &EphemeralPublic(*input_point)); - assert_eq!(result.0, *expected); + assert_eq!(result.0, MontgomeryPoint(*expected)); } #[test] @@ -173,12 +168,12 @@ mod test { let mut k: Scalar = Scalar::from_bits(X25519_BASEPOINT.0); let mut u: MontgomeryPoint = X25519_BASEPOINT; - let mut result: MontgomeryPoint; + let mut result: SharedSecret; macro_rules! do_iterations { ($n:expr) => ( for _ in 0..$n { - result = x25519(&k, &u); + result = EphemeralSecret::diffie_hellman(EphemeralSecret(k), &EphemeralPublic(u)); // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH @@ -188,7 +183,7 @@ mod test { // // ↓↓ DON'T DO THIS ↓↓ u = MontgomeryPoint(k.as_bytes().clone()); - k = Scalar::from_bits(result.to_bytes()); + k = Scalar::from_bits(result.0.to_bytes()); } ) } From ff0e1f286b8d84881f8e6020d953f5ed640af664 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Fri, 7 Dec 2018 17:51:08 -0500 Subject: [PATCH 037/160] From<[u8; 32]> for Ephemeral Public, fix to benches --- benches/x25519.rs | 1 - src/x25519.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index 292393c..9a8238b 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -30,7 +30,6 @@ fn bench_diffie_hellman(c: &mut Criterion) { let bob_public: EphemeralPublic = EphemeralPublic::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { - let alice_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); b.iter_with_setup( || EphemeralSecret::new(&mut csprng), |alice_secret| EphemeralSecret::diffie_hellman(alice_secret, &bob_public), diff --git a/src/x25519.rs b/src/x25519.rs index 05590b1..ec3630f 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -25,6 +25,14 @@ use rand_core::CryptoRng; #[repr(C)] pub struct EphemeralPublic(pub (crate) MontgomeryPoint); +impl From<[u8; 32]> for EphemeralPublic { + /// Given a byte array, construct an x25519 `EphemeralPublic` key + fn from(bytes: [u8; 32]) -> EphemeralPublic { + EphemeralPublic(MontgomeryPoint(bytes)) + } + +} + /// A DH ephemeral secret key. #[repr(C)] #[derive(Default)] // we derive Default in order to use the clear() method in Drop From dd40ae0d97ed086ade3c21a9b4e37511cccbc8d4 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 15 Dec 2018 17:41:12 -0500 Subject: [PATCH 038/160] Update Cargo.toml Co-Authored-By: DebugSteven --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4dd0e4e..3342366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^1.0.0-pre.1" +version = "1" default-features = false [dependencies.rand_core] From f6da70d47b0eb89c0cf1571f6bb1e323132e0c1c Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 15 Dec 2018 17:41:35 -0500 Subject: [PATCH 039/160] Update benches/x25519.rs Co-Authored-By: DebugSteven --- benches/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index 9a8238b..e1e5818 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -32,7 +32,7 @@ fn bench_diffie_hellman(c: &mut Criterion) { c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( || EphemeralSecret::new(&mut csprng), - |alice_secret| EphemeralSecret::diffie_hellman(alice_secret, &bob_public), + |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); } From 0fc69912d9717adce17e13a2e1953a5d8e8b5fbe Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 15 Dec 2018 17:42:25 -0500 Subject: [PATCH 040/160] Update src/x25519.rs Co-Authored-By: DebugSteven --- src/x25519.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index ec3630f..e86e29b 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -35,7 +35,6 @@ impl From<[u8; 32]> for EphemeralPublic { /// A DH ephemeral secret key. #[repr(C)] -#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct EphemeralSecret(pub (crate) Scalar); /// Overwrite ephemeral secret key material with null bytes when it goes out of scope. From d3b7ccf030e3b4aaedcf7b616c64d48b09a722ab Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sun, 16 Dec 2018 11:30:32 -0700 Subject: [PATCH 041/160] byte-oriented x25519 function --- Cargo.toml | 2 +- benches/x25519.rs | 2 +- src/x25519.rs | 89 +++++++++++++++++++++++------------------------ 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4dd0e4e..3342366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "^1.0.0-pre.1" +version = "1" default-features = false [dependencies.rand_core] diff --git a/benches/x25519.rs b/benches/x25519.rs index 9a8238b..e1e5818 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -32,7 +32,7 @@ fn bench_diffie_hellman(c: &mut Criterion) { c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( || EphemeralSecret::new(&mut csprng), - |alice_secret| EphemeralSecret::diffie_hellman(alice_secret, &bob_public), + |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); } diff --git a/src/x25519.rs b/src/x25519.rs index ec3630f..31b3f71 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -22,7 +22,6 @@ use rand_core::RngCore; use rand_core::CryptoRng; /// A DH ephemeral public key. -#[repr(C)] pub struct EphemeralPublic(pub (crate) MontgomeryPoint); impl From<[u8; 32]> for EphemeralPublic { @@ -34,8 +33,6 @@ impl From<[u8; 32]> for EphemeralPublic { } /// A DH ephemeral secret key. -#[repr(C)] -#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct EphemeralSecret(pub (crate) Scalar); /// Overwrite ephemeral secret key material with null bytes when it goes out of scope. @@ -46,13 +43,11 @@ impl Drop for EphemeralSecret { } impl EphemeralSecret { - /// The diffie_hellman function performs scalar multipication on a montegomery point. - /// This is the implementation for the x25519 function, as specified in RFC7748. + /// Utility function to make it easier to call `x25519()` with + /// an ephemeral secret key and montegomery point as input and + /// a shared secret as the output. pub fn diffie_hellman(self, their_public: &EphemeralPublic) -> SharedSecret { - let k: Scalar = clamp_scalar(self.0.as_bytes()); - let point: MontgomeryPoint = MontgomeryPoint(*their_public.0.as_bytes()); - - SharedSecret(k * point) + SharedSecret(self.0 * their_public.0) } /// Generate an x25519 `EphemeralSecret` key. @@ -63,7 +58,7 @@ impl EphemeralSecret { csprng.fill_bytes(&mut bytes); - EphemeralSecret(clamp_scalar(&bytes)) + EphemeralSecret(clamp_scalar(bytes)) } } @@ -78,7 +73,6 @@ impl<'a> From<&'a EphemeralSecret> for EphemeralPublic { } /// A DH SharedSecret -#[repr(C)] pub struct SharedSecret(pub (crate) MontgomeryPoint); /// Overwrite shared secret material with null bytes when it goes out of scope. @@ -105,7 +99,7 @@ impl SharedSecret { /// # Returns /// /// A `Scalar`. -fn clamp_scalar(scalar: &[u8; 32]) -> Scalar { +fn clamp_scalar(scalar: [u8; 32]) -> Scalar { let mut s: [u8; 32] = scalar.clone(); s[0] &= 248; @@ -115,58 +109,63 @@ fn clamp_scalar(scalar: &[u8; 32]) -> Scalar { Scalar::from_bits(s) } +/// The x25519 function, as specified in RFC7748. +pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { + (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() +} + #[cfg(test)] mod test { use super::*; - fn do_rfc7748_ladder_test1(input_scalar: &Scalar, - input_point: &MontgomeryPoint, - expected: &[u8; 32]) { - let result = EphemeralSecret::diffie_hellman(EphemeralSecret(*input_scalar), &EphemeralPublic(*input_point)); + fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], + input_point: [u8; 32], + expected: [u8; 32]) { + let result = x25519(input_scalar, input_point); - assert_eq!(result.0, MontgomeryPoint(*expected)); + assert_eq!(result, expected); } #[test] fn rfc7748_ladder_test1_vectorset1() { - let input_scalar: Scalar = Scalar::from_bits([ + let input_scalar: [u8; 32] = [ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, - 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4, ]); - let input_point: MontgomeryPoint = MontgomeryPoint([ + 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4, ]; + let input_point: [u8; 32] = [ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, - 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, ]); + 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, ]; let expected: [u8; 32] = [ 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52, ]; - do_rfc7748_ladder_test1(&input_scalar, &input_point, &expected); + do_rfc7748_ladder_test1(input_scalar, input_point, expected); } #[test] fn rfc7748_ladder_test1_vectorset2() { - let input_scalar: Scalar = Scalar::from_bits([ + let input_scalar: [u8; 32] = [ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, - 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d, ]); - let input_point: MontgomeryPoint = MontgomeryPoint([ + 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d, ]; + let input_point: [u8; 32] = [ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, - 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, ]); + 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, ]; let expected: [u8; 32] = [ 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57, ]; - do_rfc7748_ladder_test1(&input_scalar, &input_point, &expected); + do_rfc7748_ladder_test1(input_scalar, input_point, expected); } #[test] @@ -174,14 +173,14 @@ mod test { fn rfc7748_ladder_test2() { use curve25519_dalek::constants::X25519_BASEPOINT; - let mut k: Scalar = Scalar::from_bits(X25519_BASEPOINT.0); - let mut u: MontgomeryPoint = X25519_BASEPOINT; - let mut result: SharedSecret; + let mut k: [u8; 32] = X25519_BASEPOINT.0; + let mut u: [u8; 32] = X25519_BASEPOINT.0; + let mut result: [u8; 32]; macro_rules! do_iterations { ($n:expr) => ( for _ in 0..$n { - result = EphemeralSecret::diffie_hellman(EphemeralSecret(k), &EphemeralPublic(u)); + result = x25519(k, u); // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH @@ -190,8 +189,8 @@ mod test { // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. // // ↓↓ DON'T DO THIS ↓↓ - u = MontgomeryPoint(k.as_bytes().clone()); - k = Scalar::from_bits(result.0.to_bytes()); + u = k.clone(); + k = result; } ) } @@ -204,19 +203,19 @@ mod test { // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 do_iterations!(1); - assert_eq!(k.as_bytes(), &[ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, - 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, - 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, - 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79, ]); + assert_eq!(k, [ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, + 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, + 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, + 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79, ]); do_iterations!(999); - assert_eq!(k.as_bytes(), &[ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, - 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, - 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, - 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51, ]); + assert_eq!(k, [ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, + 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, + 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, + 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51, ]); do_iterations!(999_000); - assert_eq!(k.as_bytes(), &[ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, - 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, - 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, - 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24, ]); + assert_eq!(k, [ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, + 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, + 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, + 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24, ]); } } From 5dcc1c17da475140e2d4aa35cf1d27bc35248ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Sat, 5 Jan 2019 16:17:05 +0300 Subject: [PATCH 042/160] use rand_os instead of rand --- Cargo.toml | 2 +- README.md | 4 ++-- benches/x25519.rs | 4 ++-- src/lib.rs | 31 ++++++++++++++----------------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4653b04..27b3244 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ version = "0.2" [dev-dependencies] criterion = "0.2" -rand = "0.5" +rand_os = "0.1" [[bench]] name = "x25519" diff --git a/README.md b/README.md index dca2a2e..2cf795b 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ First, Alice uses `x25519_dalek::generate_secret()` and then ```rust extern crate x25519_dalek; -extern crate rand; +extern crate rand_os; use x25519_dalek::generate_secret; use x25519_dalek::generate_public; -use rand::OsRng; +use rand_os::OsRng; let mut alice_csprng = OsRng::new().unwrap(); let alice_secret = generate_secret(&mut alice_csprng); diff --git a/benches/x25519.rs b/benches/x25519.rs index 8203785..7246380 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -11,12 +11,12 @@ #[macro_use] extern crate criterion; -extern crate rand; +extern crate rand_os; extern crate x25519_dalek; use criterion::Criterion; -use rand::OsRng; +use rand_os::OsRng; use x25519_dalek::generate_public; use x25519_dalek::generate_secret; diff --git a/src/lib.rs b/src/lib.rs index 8119480..1758bc8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,14 +37,14 @@ //! //! ``` //! extern crate x25519_dalek; -//! extern crate rand; +//! extern crate rand_os; //! //! # fn main() { //! use x25519_dalek::generate_secret; //! use x25519_dalek::generate_public; -//! use rand::thread_rng; +//! use rand_os::OsRng; //! -//! let mut alice_csprng = thread_rng(); +//! let mut alice_csprng = OsRng::new().unwrap(); //! let alice_secret = generate_secret(&mut alice_csprng); //! let alice_public = generate_public(&alice_secret); //! # } @@ -54,14 +54,14 @@ //! //! ``` //! # extern crate x25519_dalek; -//! # extern crate rand; +//! # extern crate rand_os; //! # //! # fn main() { //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::thread_rng; +//! # use rand_os::OsRng; //! # -//! let mut bob_csprng = thread_rng(); +//! let mut bob_csprng = OsRng::new().unwrap(); //! let bob_secret = generate_secret(&mut bob_csprng); //! let bob_public = generate_public(&bob_secret); //! # } @@ -73,18 +73,18 @@ //! //! ``` //! # extern crate x25519_dalek; -//! # extern crate rand; +//! # extern crate rand_os; //! # //! # fn main() { //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::thread_rng; +//! # use rand_os::OsRng; //! # -//! # let mut alice_csprng = thread_rng(); +//! # let mut alice_csprng = OsRng::new().unwrap(); //! # let alice_secret = generate_secret(&mut alice_csprng); //! # let alice_public = generate_public(&alice_secret); //! # -//! # let mut bob_csprng = thread_rng(); +//! # let mut bob_csprng = OsRng::new().unwrap(); //! # let bob_secret = generate_secret(&mut bob_csprng); //! # let bob_public = generate_public(&bob_secret); //! # @@ -98,19 +98,19 @@ //! //! ``` //! # extern crate x25519_dalek; -//! # extern crate rand; +//! # extern crate rand_os; //! # //! # fn main() { //! # use x25519_dalek::diffie_hellman; //! # use x25519_dalek::generate_secret; //! # use x25519_dalek::generate_public; -//! # use rand::thread_rng; +//! # use rand_os::OsRng; //! # -//! # let mut alice_csprng = thread_rng(); +//! # let mut alice_csprng = OsRng::new().unwrap(); //! # let alice_secret = generate_secret(&mut alice_csprng); //! # let alice_public = generate_public(&alice_secret); //! # -//! # let mut bob_csprng = thread_rng(); +//! # let mut bob_csprng = OsRng::new().unwrap(); //! # let bob_secret = generate_secret(&mut bob_csprng); //! # let bob_public = generate_public(&bob_secret); //! # @@ -130,9 +130,6 @@ extern crate curve25519_dalek; extern crate rand_core; -#[cfg(test)] -extern crate rand; - mod x25519; pub use x25519::*; From 48fa313e97c0c9b1ea3a43d7fefb910a76b880df Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 10 Jan 2019 10:03:59 -0800 Subject: [PATCH 043/160] Update rand_core version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3342366..62cc899 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ default-features = false [dependencies.rand_core] default-features = false -version = "0.2" +version = "0.3" [dependencies.clear_on_drop] version = "0.2" From 65f94124f4e738ad75d0c1bab5ae6fa0acffd415 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 10 Jan 2019 10:08:08 -0800 Subject: [PATCH 044/160] Reformat dependencies, add docs.rs metadata --- Cargo.toml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 62cc899..f05c2d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,16 +19,14 @@ exclude = [ [badges] travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} -[dependencies.curve25519-dalek] -version = "1" -default-features = false +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] +features = ["nightly"] -[dependencies.rand_core] -default-features = false -version = "0.3" - -[dependencies.clear_on_drop] -version = "0.2" +[dependencies] +curve25519-dalek = { version = "1", default-features = false } +rand_core = { version = "0.3", default-features = false } +clear_on_drop = { version = "0.2" } [dev-dependencies] criterion = "0.2" From b8716bb82b6358369e8d828f73823a0594629732 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 10 Jan 2019 10:30:30 -0800 Subject: [PATCH 045/160] Pull source docs from README --- README.md | 56 +++++++++++------------- src/lib.rs | 124 +++++------------------------------------------------ 2 files changed, 37 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 63744eb..70c1cc5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ - # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, -as specified by Mike Hamburg and Adam Langley in -[RFC7748](https://tools.ietf.org/html/rfc7748), using +with curve operations provided by [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). +This crate provides two levels of API: a bare byte-oriented `x25519` +function which matches the function specified in [RFC7748][rfc7748], as +well as a higher-level Rust API for ephemeral Diffie-Hellman. + ## Examples -[![](https://raw.githubusercontent.com/dalek-cryptography/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) - -"Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) -copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) + + + Alice and Bob are two adorable kittens who have lost their mittens, and they wish to be able to send secret messages to each other to coordinate finding @@ -25,8 +28,8 @@ up on modern public key cryptography and have learned a nifty trick called kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie! -First, Alice uses `x25519_dalek::EphemeralSecret::new()` and then -`x25519_dalek::EphemeralPublic::from()` to produce her secret and public keys: +First, Alice uses `EphemeralSecret::new()` and then +`EphemeralPublic::from()` to produce her secret and public keys: ```rust extern crate x25519_dalek; @@ -70,23 +73,6 @@ Voilá! Alice and Bob can now use their shared secret to encrypt their meows, for example, by using it to generate a key and nonce for an authenticated-encryption cipher. -# Warnings - -[Our elliptic curve library](https://github.com/dalek-cryptography/curve25519-dalek) -(which this code uses) has received *one* formal cryptographic and security -review. It has not yet received what we would consider *sufficient* peer -review by other qualified cryptographers to be considered in any way, shape, -or form, safe. - -This code matches the test vectors, as specified in -[RFC7748](https://tools.ietf.org/html/rfc7748), however: - -**USE AT YOUR OWN RISK.** - -# Documentation - -Documentation is available [here](https://docs.rs/x25519-dalek). - # Installation To install, add the following to your project's `Cargo.toml`: @@ -96,8 +82,18 @@ To install, add the following to your project's `Cargo.toml`: version = "^0.3" ``` -Then, in your library or executable source, add: +# Documentation -```rust -extern crate x25519_dalek; -``` +Documentation is available [here](https://docs.rs/x25519-dalek). + +# Note + +This code matches the [RFC7748][rfc7748] test vectors. +The elliptic curve +operations are provided by `curve25519-dalek`, which makes a best-effort +attempt to prevent software side-channels. + +"Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) +copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) + +[rfc7748]: https://tools.ietf.org/html/rfc7748 diff --git a/src/lib.rs b/src/lib.rs index 1e32b77..b9a1f46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,122 +7,20 @@ // Authors: // - Isis Agora Lovecruft -//! x25519 Diffie-Hellman key exchange -//! -//! A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key -//! exchange as specified by Mike Hamburg and Adam Langley in -//! [RFC7748](https://tools.ietf.org/html/rfc7748). -//! -//! # Examples -//! -//! [![](https://raw.githubusercontent.com/isislovecruft/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg)](https://shop.bubblesort.io) -//! -//! "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) -//! copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) -//! -//! Alice and Bob are two adorable kittens who have lost their mittens, and they -//! wish to be able to send secret messages to each other to coordinate finding -//! them, otherwise—if their caretaker cat finds out—they will surely be called -//! naughty kittens and be given no pie! -//! -//! But the two kittens are quite clever. Even though their paws are still too -//! big and the rest of them is 90% fuzziness, these clever kittens have been -//! studying up on modern public key cryptography and have learned a nifty trick -//! called *elliptic curve Diffie-Hellman key exchange*. With the right -//! incantations, the kittens will be able to secretly organise to find their -//! mittens, and then spend the rest of the afternoon nomming some yummy pie! -//! -//! First, Alice uses `x25519_dalek::EphemeralSecret::new()` and -//! `x25519_dalek::EphemeralPublic::from()` to produce her secret and public keys: -//! -//! ``` -//! extern crate x25519_dalek; -//! extern crate rand; -//! -//! # fn main() { -//! use x25519_dalek::EphemeralPublic; -//! use x25519_dalek::EphemeralSecret; -//! use rand::thread_rng; -//! -//! let mut alice_csprng = thread_rng(); -//! let alice_secret = EphemeralSecret::new(&mut alice_csprng); -//! let alice_public = EphemeralPublic::from(&alice_secret); -//! # } -//! ``` -//! -//! Bob does the same: -//! -//! ``` -//! # extern crate x25519_dalek; -//! # extern crate rand; -//! # -//! # fn main() { -//! # use x25519_dalek::EphemeralPublic; -//! # use x25519_dalek::EphemeralSecret; -//! # use rand::thread_rng; -//! # -//! let mut bob_csprng = thread_rng(); -//! let bob_secret = EphemeralSecret::new(&mut bob_csprng); -//! let bob_public = EphemeralPublic::from(&bob_secret); -//! # } -//! ``` -//! -//! Alice meows across the room, telling `alice_public` to Bob, and Bob -//! loudly meows `bob_public` back to Alice. Alice now computes her -//! shared secret with Bob by doing: -//! -//! ``` -//! # extern crate x25519_dalek; -//! # extern crate rand; -//! # -//! # fn main() { -//! # use x25519_dalek::EphemeralPublic; -//! # use x25519_dalek::EphemeralSecret; -//! # use rand::thread_rng; -//! # -//! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = EphemeralSecret::new(&mut alice_csprng); -//! # let alice_public = EphemeralPublic::from(&alice_secret); -//! # -//! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = EphemeralSecret::new(&mut bob_csprng); -//! # let bob_public = EphemeralPublic::from(&bob_secret); -//! # -//! # -//! let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); -//! # } -//! ``` -//! -//! Similarly, Bob computes the same shared secret by doing: -//! -//! ``` -//! # extern crate x25519_dalek; -//! # extern crate rand; -//! # -//! # fn main() { -//! # use x25519_dalek::EphemeralPublic; -//! # use x25519_dalek::EphemeralSecret; -//! # use rand::thread_rng; -//! # -//! # let mut alice_csprng = thread_rng(); -//! # let alice_secret = EphemeralSecret::new(&mut alice_csprng); -//! # let alice_public = EphemeralPublic::from(&alice_secret); -//! # -//! # let mut bob_csprng = thread_rng(); -//! # let bob_secret = EphemeralSecret::new(&mut bob_csprng); -//! # let bob_public = EphemeralPublic::from(&bob_secret); -//! # -//! let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); -//! # } -//! ``` -//! -//! Voilá! Alice and Bob can now use their shared secret to encrypt their -//! meows, for example, by using it to generate a key and nonce for an -//! authenticated-encryption cipher. +// Refuse to compile if documentation is missing, but only on nightly. +// +// This means that missing docs will still fail CI, but means we can use +// README.md as the crate documentation. #![no_std] #![cfg_attr(feature = "bench", feature(test))] -#![deny(missing_docs)] +#![cfg_attr(feature = "nightly", feature(external_doc))] +#![cfg_attr(feature = "nightly", deny(missing_docs))] +#![cfg_attr(feature = "nightly", doc(include = "../README.md"))] +#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] + +//! Note that docs will only build on nightly Rust until +//! `feature(external_doc)` is stabilized. extern crate clear_on_drop; From a58b4cf1895a5859f4c9341efab1194650bde96f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 12 Jan 2019 23:33:49 +0000 Subject: [PATCH 046/160] Fix two links to repos in CONTRIBUTING.md. --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b60e709..d1561b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,11 @@ If you have questions or comments, please feel free to email the authors. For feature requests, suggestions, and bug reports, please open an issue on -[our Github](https://github.com/isislovecruft/x25519-dalek). (Or, send us +[our Github](https://github.com/dalek-cryptography/x25519-dalek). (Or, send us an email if you're opposed to using Github for whatever reason.) Patches are welcomed as pull requests on -[our Github](https://github.com/isislovecruft/x25519-dalek), as well as by +[our Github](https://github.com/dalek-cryptography/x25519-dalek), as well as by email (preferably sent to all of the authors listed in `Cargo.toml`). All issues on curve25519-dalek are mentored, if you want help with a bug just From c9a1ef6a3656c41696af393f186822a1b1882e42 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 12 Jan 2019 23:34:49 +0000 Subject: [PATCH 047/160] Credit @DebugSteven as an author. --- Cargo.toml | 2 +- LICENSE | 3 ++- benches/x25519.rs | 6 ++++-- src/lib.rs | 6 ++++-- src/x25519.rs | 6 ++++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f05c2d2..08a9752 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "x25519-dalek" version = "0.3.0" -authors = ["Isis Lovecruft "] +authors = ["Isis Lovecruft ", "DebugSteven "] readme = "README.md" license = "BSD-3-Clause" repository = "https://github.com/dalek-cryptography/x25519-dalek" diff --git a/LICENSE b/LICENSE index 20dcc41..0443d91 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ -Copyright (c) 2017 Isis Agora Lovecruft. All rights reserved. +Copyright (c) 2017-2019 isis agora lovecruft. All rights reserved. +Copyright (c) 2019 DebugSteven. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/benches/x25519.rs b/benches/x25519.rs index e1e5818..fe6f91c 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -1,11 +1,13 @@ // -*- mode: rust; -*- // // This file is part of x25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2019 isis agora lovecruft +// Copyright (c) 2019 DebugSteven // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft +// - DebugSteven //! Benchmark the Diffie-Hellman operation. diff --git a/src/lib.rs b/src/lib.rs index b9a1f46..0f3cf29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,13 @@ // -*- mode: rust; -*- // // This file is part of x25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2019 isis lovecruft +// Copyright (c) 2019 DebugSteven // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft +// - DebugSteven // Refuse to compile if documentation is missing, but only on nightly. // diff --git a/src/x25519.rs b/src/x25519.rs index 31b3f71..c265e6c 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -1,11 +1,13 @@ // -*- mode: rust; -*- // // This file is part of x25519-dalek. -// Copyright (c) 2017 Isis Lovecruft +// Copyright (c) 2017-2019 isis lovecruft +// Copyright (c) 2019 DebugSteven // See LICENSE for licensing information. // // Authors: -// - Isis Agora Lovecruft +// - isis agora lovecruft +// - DebugSteven //! x25519 Diffie-Hellman key exchange //! From bab903cb65d305888981a32b8a6bb331d90583fb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sat, 12 Jan 2019 23:35:03 +0000 Subject: [PATCH 048/160] Bump x25519-dalek version to 0.4.0. --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08a9752..e591e55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.3.0" +version = "0.4.0" authors = ["Isis Lovecruft ", "DebugSteven "] readme = "README.md" license = "BSD-3-Clause" diff --git a/README.md b/README.md index 70c1cc5..5edad5d 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.x25519-dalek] -version = "^0.3" +version = "^0.4" ``` # Documentation From 95b12934d728f02cec0889e64a805ba629beeeca Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Jan 2019 02:18:22 +0000 Subject: [PATCH 049/160] Ignore README doctests for now. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 045b68f..d1cbb4c 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ let alice_public = EphemeralPublic::from(&alice_secret); Bob does the same: -```rust +```rust,ignore let mut bob_csprng = OsRng::new().unwrap(); let bob_secret = EphemeralSecret::new(&mut bob_csprng); let bob_public = EphemeralPublic::from(&bob_secret); @@ -56,7 +56,7 @@ Alice meows across the room, telling `alice_public` to Bob, and Bob loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: -```rust +```rust,ignore use x25519_dalek::EphemeralPublic; use x25519_dalek::EphemeralSecret; @@ -65,7 +65,7 @@ let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); Similarly, Bob computes the same shared secret by doing: -```rust +```rust,ignore let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); ``` From 47e8689b2debb901a345b2270355d6408b0a8734 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 16 Jan 2019 02:22:12 +0000 Subject: [PATCH 050/160] Bump x25519-dalek version to 0.4.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f0fc3a1..7c7cd3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.0" +version = "0.4.1" authors = ["Isis Lovecruft ", "DebugSteven "] readme = "README.md" license = "BSD-3-Clause" From dd8b61da4a789300da9c39adbacadfcd4e3b958d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 15 Jan 2019 21:44:56 -0800 Subject: [PATCH 051/160] Add an X25519 basepoint constant --- src/x25519.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index c265e6c..c5e342c 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -111,18 +111,43 @@ fn clamp_scalar(scalar: [u8; 32]) -> Scalar { Scalar::from_bits(s) } -/// The x25519 function, as specified in RFC7748. +/// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. +/// +/// This can be used with [`X25519_BASEPOINT_BYTES`] for people who +/// cannot use the better, safer, and faster ephemeral DH API. pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() } +/// The X25519 basepoint, for use with the bare, byte-oriented x25519 +/// function. This is provided for people who cannot use the typed +/// ephemeral DH API for some reason. +pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + #[cfg(test)] mod test { use super::*; - fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], - input_point: [u8; 32], - expected: [u8; 32]) { + #[test] + fn byte_basepoint_matches_edwards_scalar_mul() { + let mut scalar_bytes = [0x37; 32]; + + for i in 0..32 { + scalar_bytes[i] += 2; + + let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); + + let expected = (&ED25519_BASEPOINT_TABLE * &clamp_scalar(scalar_bytes)) + .to_montgomery() + .to_bytes(); + + assert_eq!(result, expected); + } + } + + fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) { let result = x25519(input_scalar, input_point); assert_eq!(result, expected); From b21ab1324f2ce17e2ede679446da55de7303e290 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 15 Jan 2019 22:47:35 -0800 Subject: [PATCH 052/160] Bump minor version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7c7cd3e..fe51ca2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.1" +version = "0.4.2" authors = ["Isis Lovecruft ", "DebugSteven "] readme = "README.md" license = "BSD-3-Clause" From f5f79f56c825412940723fe742e4fc81fa0c6963 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 18 Jan 2019 06:13:13 +0000 Subject: [PATCH 053/160] Fix link to now-missing katex header. This fixes https://github.com/dalek-cryptography/x25519-dalek/issues/23 as reported by @DebugSteven. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7c7cd3e..58dc2c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [package.metadata.docs.rs] -rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] +rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.0.1/docs/assets/rustdoc-include-katex-header.html"] features = ["nightly"] [dependencies] From ee359f61004f2c3e8b8824df420721a704a7a789 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 20 Jan 2019 00:45:31 +0000 Subject: [PATCH 054/160] Bump x25519-dalek version to 0.4.3. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a65c026..f6b826a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.2" +version = "0.4.3" authors = ["Isis Lovecruft ", "DebugSteven "] readme = "README.md" license = "BSD-3-Clause" From 9e553688c328ddf5ce29a17c764ca76ee7b24c58 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 22 Jan 2019 14:03:00 -0800 Subject: [PATCH 055/160] Docs hotfix: disable KaTeX in docs.rs --- Cargo.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6b826a..6d996f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,11 @@ [package] name = "x25519-dalek" version = "0.4.3" -authors = ["Isis Lovecruft ", "DebugSteven "] +authors = [ + "Isis Lovecruft ", + "DebugSteven ", + "Henry de Valence ", +] readme = "README.md" license = "BSD-3-Clause" repository = "https://github.com/dalek-cryptography/x25519-dalek" @@ -20,7 +24,7 @@ exclude = [ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [package.metadata.docs.rs] -rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.0.1/docs/assets/rustdoc-include-katex-header.html"] +#rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.0.1/docs/assets/rustdoc-include-katex-header.html"] features = ["nightly"] [dependencies] From 1117184a021ff9ad59a6b552af8f31a2073219ff Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 22 Jan 2019 14:11:44 -0800 Subject: [PATCH 056/160] Add EphemeralPublic::as_bytes --- src/x25519.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index c5e342c..f3c6870 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -31,7 +31,14 @@ impl From<[u8; 32]> for EphemeralPublic { fn from(bytes: [u8; 32]) -> EphemeralPublic { EphemeralPublic(MontgomeryPoint(bytes)) } +} +impl EphemeralPublic { + /// View this ephemeral public key as a byte array. + #[inline] + pub fn as_bytes(&self) -> &[u8; 32] { + self.0.as_bytes() + } } /// A DH ephemeral secret key. @@ -85,11 +92,10 @@ impl Drop for SharedSecret { } impl SharedSecret { - /// View this shared secret key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { - &self.0.as_bytes() + self.0.as_bytes() } } From bcc6383d9a8e7b1509ceae3181a39d6f976fb6da Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 22 Jan 2019 14:15:14 -0800 Subject: [PATCH 057/160] Bump version number --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6d996f5..69b3aa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.3" +version = "0.4.4" authors = [ "Isis Lovecruft ", "DebugSteven ", From 1d23a1d30d50dfc5f390b82eb118980a8f933473 Mon Sep 17 00:00:00 2001 From: Philippe Dugre Date: Fri, 25 Jan 2019 17:08:48 -0500 Subject: [PATCH 058/160] Added doc example for x25519() --- src/x25519.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index f3c6870..edd3adf 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -121,6 +121,37 @@ fn clamp_scalar(scalar: [u8; 32]) -> Scalar { /// /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who /// cannot use the better, safer, and faster ephemeral DH API. +/// # Example +/// ``` +/// extern crate rand_os; +/// +/// use x25519_dalek::{ x25519, X25519_BASEPOINT_BYTES }; +/// use rand_os::OsRng; +/// use rand_os::rand_core::RngCore; +/// +/// let mut rng = OsRng::new().unwrap(); +/// +/// // Generate Alice key pair +/// let mut alice_private = [0u8; 32]; +/// rng.fill_bytes(&mut alice_private); +/// +/// let alice_public = x25519(alice_private.clone(), X25519_BASEPOINT_BYTES); +/// +/// // Generate bob key pair +/// let mut bob_private = [0u8; 32]; +/// rng.fill_bytes(&mut bob_private); +/// +/// let bob_public = x25519(bob_private.clone(), X25519_BASEPOINT_BYTES); +/// +/// // Exchange the public keys +/// // ... +/// // Generate shared secret +/// +/// let alice_shared = x25519(alice_private, bob_public); +/// let bob_shared = x25519(bob_private, alice_public); +/// +/// assert_eq!(alice_shared, bob_shared); +/// ``` pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() } @@ -128,6 +159,7 @@ pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { /// The X25519 basepoint, for use with the bare, byte-oriented x25519 /// function. This is provided for people who cannot use the typed /// ephemeral DH API for some reason. +/// See [`x25519`] for example usage. pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; From 474c272945d2e3719d7e8e3ea3aff1eb76fd2b2b Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 3 Feb 2019 11:13:50 -0500 Subject: [PATCH 059/160] Removed "nightly" from default features --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 69b3aa0..23c7742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ name = "x25519" harness = false [features] -default = ["std", "nightly", "u64_backend"] +default = ["std", "u64_backend"] std = ["curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] From 89d474ae5576de453b268b946fc503e73a005284 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 4 Feb 2019 12:31:22 -0800 Subject: [PATCH 060/160] Bump patch version to 0.4.5 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 23c7742..c865172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.4" +version = "0.4.5" authors = [ "Isis Lovecruft ", "DebugSteven ", From 9fe535dcc7fcc2c1c8e6546efc978c0bd190c07e Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 08:50:20 -0700 Subject: [PATCH 061/160] rename EphemeralPublic to PublicKey --- src/x25519.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index f3c6870..fa5e889 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -23,18 +23,18 @@ use curve25519_dalek::scalar::Scalar; use rand_core::RngCore; use rand_core::CryptoRng; -/// A DH ephemeral public key. -pub struct EphemeralPublic(pub (crate) MontgomeryPoint); +/// A DH public key. +pub struct PublicKey(pub (crate) MontgomeryPoint); -impl From<[u8; 32]> for EphemeralPublic { - /// Given a byte array, construct an x25519 `EphemeralPublic` key - fn from(bytes: [u8; 32]) -> EphemeralPublic { - EphemeralPublic(MontgomeryPoint(bytes)) +impl From<[u8; 32]> for PublicKey { + /// Given a byte array, construct a x25519 `PublicKey`. + fn from(bytes: [u8; 32]) -> PublicKey { + PublicKey(MontgomeryPoint(bytes)) } } -impl EphemeralPublic { - /// View this ephemeral public key as a byte array. +impl PublicKey { + /// View this public key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { self.0.as_bytes() @@ -55,7 +55,7 @@ impl EphemeralSecret { /// Utility function to make it easier to call `x25519()` with /// an ephemeral secret key and montegomery point as input and /// a shared secret as the output. - pub fn diffie_hellman(self, their_public: &EphemeralPublic) -> SharedSecret { + pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { SharedSecret(self.0 * their_public.0) } @@ -72,11 +72,11 @@ impl EphemeralSecret { } -impl<'a> From<&'a EphemeralSecret> for EphemeralPublic { +impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Given an x25519 `EphemeralSecret` key, compute its corresponding - /// `EphemeralPublic` key. - fn from(secret: &'a EphemeralSecret) -> EphemeralPublic { - EphemeralPublic((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + /// `PublicKey` key. + fn from(secret: &'a EphemeralSecret) -> PublicKey { + PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } } From 4caffdcca01d8fdbfa6d440ede698788ee87163c Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 09:14:43 -0700 Subject: [PATCH 062/160] impl StaticSecret & create DH method that borrows our StaticSecret --- src/x25519.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index fa5e889..64e2c0e 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -81,6 +81,46 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { } +/// A DH static secret key. +pub struct StaticSecret(pub (crate) Scalar); + +/// Overwrite static secret key material with null bytes when it goes out of scope. +impl Drop for StaticSecret { + fn drop(&mut self) { + self.0.clear(); + } +} + +impl StaticSecret { + /// Utility function to make it easier to call `x25519()` with + /// a static secret key and montegomery point as input and + /// a shared secret as the output. + pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { + SharedSecret(&self.0 * their_public.0) + } + + /// Generate a x25519 `StaticSecret` key. + pub fn new(csprng: &mut T) -> Self + where T: RngCore + CryptoRng + { + let mut bytes = [0u8; 32]; + + csprng.fill_bytes(&mut bytes); + + StaticSecret(clamp_scalar(bytes)) + } + +} + +impl<'a> From<&'a StaticSecret> for PublicKey { + /// Given an x25519 `StaticSecret` key, compute its corresponding + /// `PublicKey` key. + fn from(secret: &'a StaticSecret) -> PublicKey { + PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + } + +} + /// A DH SharedSecret pub struct SharedSecret(pub (crate) MontgomeryPoint); From ad670a4d03bdc95cb34a31dd60e8137c98150787 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 09:42:56 -0700 Subject: [PATCH 063/160] methods to convert a StaticSecret to and from bytes --- src/x25519.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index 64e2c0e..2f2a87e 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -110,6 +110,18 @@ impl StaticSecret { StaticSecret(clamp_scalar(bytes)) } + /// Convert a x25519 `StaticSecret` key to its underlying sequence of bytes. + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + +} + +impl From<[u8; 32]> for StaticSecret { + /// Given a byte array, construct a x25519 `StaticSecret`. + fn from(bytes: [u8; 32]) -> StaticSecret { + StaticSecret(Scalar::from_bits(bytes)) + } } impl<'a> From<&'a StaticSecret> for PublicKey { From 29edaa17ac74d151a97209f9045726de5bd3282a Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 10:22:06 -0700 Subject: [PATCH 064/160] informative doc comments --- src/x25519.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 2f2a87e..bbac39a 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -52,9 +52,8 @@ impl Drop for EphemeralSecret { } impl EphemeralSecret { - /// Utility function to make it easier to call `x25519()` with - /// an ephemeral secret key and montegomery point as input and - /// a shared secret as the output. + /// Perform a Diffie-Hellman key agreement between `self` and + /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { SharedSecret(self.0 * their_public.0) } @@ -81,7 +80,9 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { } -/// A DH static secret key. +/// A static secret key for Diffie-Hellman. Unlike an EphemeralSecret, this key +/// does not enforce that it's used only once, and can be saved and loaded from +/// a byte array. pub struct StaticSecret(pub (crate) Scalar); /// Overwrite static secret key material with null bytes when it goes out of scope. @@ -92,9 +93,8 @@ impl Drop for StaticSecret { } impl StaticSecret { - /// Utility function to make it easier to call `x25519()` with - /// a static secret key and montegomery point as input and - /// a shared secret as the output. + /// Perform a Diffie-Hellman key agreement between `self` and + /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { SharedSecret(&self.0 * their_public.0) } @@ -110,7 +110,7 @@ impl StaticSecret { StaticSecret(clamp_scalar(bytes)) } - /// Convert a x25519 `StaticSecret` key to its underlying sequence of bytes. + /// Save a x25519 `StaticSecret` key's bytes. pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } @@ -118,7 +118,7 @@ impl StaticSecret { } impl From<[u8; 32]> for StaticSecret { - /// Given a byte array, construct a x25519 `StaticSecret`. + /// Load a `StaticSecret` from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { StaticSecret(Scalar::from_bits(bytes)) } @@ -172,14 +172,14 @@ fn clamp_scalar(scalar: [u8; 32]) -> Scalar { /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. /// /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who -/// cannot use the better, safer, and faster ephemeral DH API. +/// cannot use the better, safer, and faster DH API. pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() } /// The X25519 basepoint, for use with the bare, byte-oriented x25519 /// function. This is provided for people who cannot use the typed -/// ephemeral DH API for some reason. +/// DH API for some reason. pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; From 6797c0969fbb9573a2a2599c783b1573ddd672c3 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 10:57:42 -0700 Subject: [PATCH 065/160] summarize how types are used & how they relate to one another --- src/x25519.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index bbac39a..f788207 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -23,7 +23,8 @@ use curve25519_dalek::scalar::Scalar; use rand_core::RngCore; use rand_core::CryptoRng; -/// A DH public key. +/// A `PublicKey` is the corresponding public key converted from +/// an `EphemeralSecret` or a `StaticSecret` key. pub struct PublicKey(pub (crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { @@ -41,7 +42,8 @@ impl PublicKey { } } -/// A DH ephemeral secret key. +/// A `EphemeralSecret` is a short lived Diffie-Hellman secret key +/// used to create a `SharedSecret` when given their `PublicKey`. pub struct EphemeralSecret(pub (crate) Scalar); /// Overwrite ephemeral secret key material with null bytes when it goes out of scope. @@ -80,9 +82,9 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { } -/// A static secret key for Diffie-Hellman. Unlike an EphemeralSecret, this key -/// does not enforce that it's used only once, and can be saved and loaded from -/// a byte array. +/// A `StaticSecret` is a static Diffie-Hellman secret key that +/// can be saved and loaded to create a `SharedSecret` when given +/// their `PublicKey`. pub struct StaticSecret(pub (crate) Scalar); /// Overwrite static secret key material with null bytes when it goes out of scope. @@ -133,7 +135,8 @@ impl<'a> From<&'a StaticSecret> for PublicKey { } -/// A DH SharedSecret +/// A `SharedSecret` is a Diffie-Hellman shared secret that’s generated +/// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`. pub struct SharedSecret(pub (crate) MontgomeryPoint); /// Overwrite shared secret material with null bytes when it goes out of scope. From e25c72b4761b6f3c07b39d97f22f5bd6f0bdd022 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 16 Feb 2019 10:11:27 -0800 Subject: [PATCH 066/160] Update README tests and ensure they're run in Travis. --- .travis.yml | 1 + README.md | 95 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5b5c1f..eceb10e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ rust: env: - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default' + - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='default' - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend nightly' - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly' diff --git a/README.md b/README.md index d1cbb4c..f435094 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ with curve operations provided by This crate provides two levels of API: a bare byte-oriented `x25519` function which matches the function specified in [RFC7748][rfc7748], as -well as a higher-level Rust API for ephemeral Diffie-Hellman. +well as a higher-level Rust API for static and ephemeral Diffie-Hellman. ## Examples @@ -29,50 +29,111 @@ kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie! First, Alice uses `EphemeralSecret::new()` and then -`EphemeralPublic::from()` to produce her secret and public keys: +`PublicKey::from()` to produce her secret and public keys: ```rust -extern crate x25519_dalek; extern crate rand_os; - -use x25519_dalek::EphemeralPublic; -use x25519_dalek::EphemeralSecret; use rand_os::OsRng; +extern crate x25519_dalek; +use x25519_dalek::EphemeralSecret; +use x25519_dalek::PublicKey; + +# fn main() { let mut alice_csprng = OsRng::new().unwrap(); let alice_secret = EphemeralSecret::new(&mut alice_csprng); -let alice_public = EphemeralPublic::from(&alice_secret); +let alice_public = PublicKey::from(&alice_secret); +# } ``` Bob does the same: -```rust,ignore +```rust +# extern crate rand_os; +# use rand_os::OsRng; +# +# extern crate x25519_dalek; +# use x25519_dalek::EphemeralSecret; +# use x25519_dalek::PublicKey; +# fn main() { let mut bob_csprng = OsRng::new().unwrap(); let bob_secret = EphemeralSecret::new(&mut bob_csprng); -let bob_public = EphemeralPublic::from(&bob_secret); +let bob_public = PublicKey::from(&bob_secret); +# } ``` Alice meows across the room, telling `alice_public` to Bob, and Bob loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: -```rust,ignore -use x25519_dalek::EphemeralPublic; -use x25519_dalek::EphemeralSecret; - -let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); +```rust +# extern crate rand_os; +# use rand_os::OsRng; +# +# extern crate x25519_dalek; +# use x25519_dalek::EphemeralSecret; +# use x25519_dalek::PublicKey; +# +# fn main() { +# let mut csprng = OsRng::new().unwrap(); +# let alice_secret = EphemeralSecret::new(&mut csprng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(&mut csprng); +# let bob_public = PublicKey::from(&bob_secret); +let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); +# } ``` -Similarly, Bob computes the same shared secret by doing: +Similarly, Bob computes a shared secret by doing: -```rust,ignore -let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); +```rust +# extern crate rand_os; +# use rand_os::OsRng; +# +# extern crate x25519_dalek; +# use x25519_dalek::EphemeralSecret; +# use x25519_dalek::PublicKey; +# +# fn main() { +# let mut csprng = OsRng::new().unwrap(); +# let alice_secret = EphemeralSecret::new(&mut csprng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(&mut csprng); +# let bob_public = PublicKey::from(&bob_secret); +let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); +# } +``` + +These secrets are the same: + +```rust +# extern crate rand_os; +# use rand_os::OsRng; +# +# extern crate x25519_dalek; +# use x25519_dalek::EphemeralSecret; +# use x25519_dalek::PublicKey; +# +# fn main() { +# let mut csprng = OsRng::new().unwrap(); +# let alice_secret = EphemeralSecret::new(&mut csprng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(&mut csprng); +# let bob_public = PublicKey::from(&bob_secret); +# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); +# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); +assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); +# } ``` Voilá! Alice and Bob can now use their shared secret to encrypt their meows, for example, by using it to generate a key and nonce for an authenticated-encryption cipher. +This example used the ephemeral DH API, which ensures that secret keys +cannot be reused; Alice and Bob could instead use the static DH API +and load a long-term secret key. + # Installation To install, add the following to your project's `Cargo.toml`: From bcea24308fdb6e5b8dc50d630b716b4aa0980c8b Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 16 Feb 2019 10:17:15 -0800 Subject: [PATCH 067/160] Fix benchmark --- benches/x25519.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index 4660a69..cfded70 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -23,13 +23,13 @@ use curve25519_dalek::montgomery::MontgomeryPoint; use rand_os::OsRng; -use x25519_dalek::EphemeralPublic; +use x25519_dalek::PublicKey; use x25519_dalek::EphemeralSecret; fn bench_diffie_hellman(c: &mut Criterion) { let mut csprng: OsRng = OsRng::new().unwrap(); - let bob_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); - let bob_public: EphemeralPublic = EphemeralPublic::from(&bob_secret); + let bob_secret = EphemeralSecret::new(&mut csprng); + let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( From aee783ae82c5b86b77c83b3d1c3178a2e3fbdeb1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 16 Feb 2019 16:31:05 -0500 Subject: [PATCH 068/160] use clamp_scalar for StaticSecret This ensures that the `StaticSecret`'s scalar always has the X25519 bit-twiddles applied. Co-Authored-By: DebugSteven --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index f788207..511a9b5 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -122,7 +122,7 @@ impl StaticSecret { impl From<[u8; 32]> for StaticSecret { /// Load a `StaticSecret` from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { - StaticSecret(Scalar::from_bits(bytes)) + StaticSecret(clamp_scalar(bytes)) } } From ebdd71b54b445316a6c2bb56fe0b87384d814af6 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Sat, 16 Feb 2019 13:46:06 -0800 Subject: [PATCH 069/160] Bump version to 0.5.0 --- CHANGELOG.md | 8 ++++++++ Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d31a81a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +Entries are listed in reverse chronological order. + +## 0.5.0 + +* Adds support for static and ephemeral keys. + diff --git a/Cargo.toml b/Cargo.toml index c865172..7e9d325 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.4.5" +version = "0.5.0" authors = [ "Isis Lovecruft ", "DebugSteven ", From ad8dd8430bebe02e906cf807eca8bfcc9bb69fc8 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sat, 16 Feb 2019 18:46:11 -0700 Subject: [PATCH 070/160] bump installation version to 0.5 in docs --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f435094..954fb72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, -with curve operations provided by +with curve operations provided by [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). This crate provides two levels of API: a bare byte-oriented `x25519` @@ -11,7 +11,7 @@ well as a higher-level Rust API for static and ephemeral Diffie-Hellman. ## Examples - @@ -51,7 +51,7 @@ Bob does the same: ```rust # extern crate rand_os; # use rand_os::OsRng; -# +# # extern crate x25519_dalek; # use x25519_dalek::EphemeralSecret; # use x25519_dalek::PublicKey; @@ -69,11 +69,11 @@ shared secret with Bob by doing: ```rust # extern crate rand_os; # use rand_os::OsRng; -# +# # extern crate x25519_dalek; # use x25519_dalek::EphemeralSecret; # use x25519_dalek::PublicKey; -# +# # fn main() { # let mut csprng = OsRng::new().unwrap(); # let alice_secret = EphemeralSecret::new(&mut csprng); @@ -89,11 +89,11 @@ Similarly, Bob computes a shared secret by doing: ```rust # extern crate rand_os; # use rand_os::OsRng; -# +# # extern crate x25519_dalek; # use x25519_dalek::EphemeralSecret; # use x25519_dalek::PublicKey; -# +# # fn main() { # let mut csprng = OsRng::new().unwrap(); # let alice_secret = EphemeralSecret::new(&mut csprng); @@ -109,11 +109,11 @@ These secrets are the same: ```rust # extern crate rand_os; # use rand_os::OsRng; -# +# # extern crate x25519_dalek; # use x25519_dalek::EphemeralSecret; # use x25519_dalek::PublicKey; -# +# # fn main() { # let mut csprng = OsRng::new().unwrap(); # let alice_secret = EphemeralSecret::new(&mut csprng); @@ -140,7 +140,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.x25519-dalek] -version = "^0.4" +version = "^0.5" ``` # Documentation From 2a58e35b23ba5624c64d006dc5c38451b5267e14 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Wed, 27 Feb 2019 12:40:14 -0500 Subject: [PATCH 071/160] PublicKey now derives Copy, Clone, Debug --- src/x25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/x25519.rs b/src/x25519.rs index 511a9b5..60835b3 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -25,6 +25,7 @@ use rand_core::CryptoRng; /// A `PublicKey` is the corresponding public key converted from /// an `EphemeralSecret` or a `StaticSecret` key. +#[derive(Copy, Clone, Debug)] pub struct PublicKey(pub (crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { From 60f276db7294e68a0805dfb10318fb52e6b6e04d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 27 Feb 2019 20:45:55 +0000 Subject: [PATCH 072/160] Fix README examples and doctests. --- README.md | 57 ++------------------------------------------------- src/lib.rs | 3 +++ src/x25519.rs | 18 ++++++++++++++++ 3 files changed, 23 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 954fb72..4020b4e 100644 --- a/README.md +++ b/README.md @@ -33,33 +33,24 @@ First, Alice uses `EphemeralSecret::new()` and then ```rust extern crate rand_os; +extern crate x25519_dalek; + use rand_os::OsRng; -extern crate x25519_dalek; use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; -# fn main() { let mut alice_csprng = OsRng::new().unwrap(); let alice_secret = EphemeralSecret::new(&mut alice_csprng); let alice_public = PublicKey::from(&alice_secret); -# } ``` Bob does the same: ```rust -# extern crate rand_os; -# use rand_os::OsRng; -# -# extern crate x25519_dalek; -# use x25519_dalek::EphemeralSecret; -# use x25519_dalek::PublicKey; -# fn main() { let mut bob_csprng = OsRng::new().unwrap(); let bob_secret = EphemeralSecret::new(&mut bob_csprng); let bob_public = PublicKey::from(&bob_secret); -# } ``` Alice meows across the room, telling `alice_public` to Bob, and Bob @@ -67,63 +58,19 @@ loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: ```rust -# extern crate rand_os; -# use rand_os::OsRng; -# -# extern crate x25519_dalek; -# use x25519_dalek::EphemeralSecret; -# use x25519_dalek::PublicKey; -# -# fn main() { -# let mut csprng = OsRng::new().unwrap(); -# let alice_secret = EphemeralSecret::new(&mut csprng); -# let alice_public = PublicKey::from(&alice_secret); -# let bob_secret = EphemeralSecret::new(&mut csprng); -# let bob_public = PublicKey::from(&bob_secret); let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); -# } ``` Similarly, Bob computes a shared secret by doing: ```rust -# extern crate rand_os; -# use rand_os::OsRng; -# -# extern crate x25519_dalek; -# use x25519_dalek::EphemeralSecret; -# use x25519_dalek::PublicKey; -# -# fn main() { -# let mut csprng = OsRng::new().unwrap(); -# let alice_secret = EphemeralSecret::new(&mut csprng); -# let alice_public = PublicKey::from(&alice_secret); -# let bob_secret = EphemeralSecret::new(&mut csprng); -# let bob_public = PublicKey::from(&bob_secret); let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); -# } ``` These secrets are the same: ```rust -# extern crate rand_os; -# use rand_os::OsRng; -# -# extern crate x25519_dalek; -# use x25519_dalek::EphemeralSecret; -# use x25519_dalek::PublicKey; -# -# fn main() { -# let mut csprng = OsRng::new().unwrap(); -# let alice_secret = EphemeralSecret::new(&mut csprng); -# let alice_public = PublicKey::from(&alice_secret); -# let bob_secret = EphemeralSecret::new(&mut csprng); -# let bob_public = PublicKey::from(&bob_secret); -# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); -# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); -# } ``` Voilá! Alice and Bob can now use their shared secret to encrypt their diff --git a/src/lib.rs b/src/lib.rs index 11e4471..8c1916f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,9 @@ extern crate curve25519_dalek; extern crate rand_core; +#[cfg(test)] +extern crate rand_os; + mod x25519; pub use x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs index 511a9b5..d56341c 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -191,6 +191,24 @@ pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ mod test { use super::*; + use rand_os::OsRng; + + // This was previously a doctest but it got moved to the README to + // avoid duplication where it then wasn't being run, so now it + // lives here. + #[test] + fn alice_and_bob() { + let mut csprng = OsRng::new().unwrap(); + let alice_secret = EphemeralSecret::new(&mut csprng); + let alice_public = PublicKey::from(&alice_secret); + let bob_secret = EphemeralSecret::new(&mut csprng); + let bob_public = PublicKey::from(&bob_secret); + let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); + let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); + + assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); + } + #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; From ea2f39afa594d74e8301c2cea24871ec62a4e086 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 27 Feb 2019 22:01:43 +0000 Subject: [PATCH 073/160] Ignore doctests since they were moved to actual tests. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4020b4e..e1c37df 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ the rest of the afternoon nomming some yummy pie! First, Alice uses `EphemeralSecret::new()` and then `PublicKey::from()` to produce her secret and public keys: -```rust +```rust,ignore extern crate rand_os; extern crate x25519_dalek; @@ -47,7 +47,7 @@ let alice_public = PublicKey::from(&alice_secret); Bob does the same: -```rust +```rust,ignore let mut bob_csprng = OsRng::new().unwrap(); let bob_secret = EphemeralSecret::new(&mut bob_csprng); let bob_public = PublicKey::from(&bob_secret); @@ -57,19 +57,19 @@ Alice meows across the room, telling `alice_public` to Bob, and Bob loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: -```rust +```rust,ignore let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); ``` Similarly, Bob computes a shared secret by doing: -```rust +```rust,ignore let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); ``` These secrets are the same: -```rust +```rust,ignore assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); ``` From 78b46c4abe4c9e032d7e8e19d00e5618ce74d571 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 27 Feb 2019 23:36:29 +0000 Subject: [PATCH 074/160] Bump x25519-dalek version to 0.5.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7e9d325..9a68726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.5.0" +version = "0.5.1" authors = [ "Isis Lovecruft ", "DebugSteven ", From 935f6e8e3537d6fc4dc8eab8e2ed351ecee225d7 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Fri, 22 Mar 2019 12:50:55 -0400 Subject: [PATCH 075/160] Made StaticSecret cloneable --- src/x25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/x25519.rs b/src/x25519.rs index 11786b1..30f5bdb 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -86,6 +86,7 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// A `StaticSecret` is a static Diffie-Hellman secret key that /// can be saved and loaded to create a `SharedSecret` when given /// their `PublicKey`. +#[derive(Clone)] pub struct StaticSecret(pub (crate) Scalar); /// Overwrite static secret key material with null bytes when it goes out of scope. From 89c58a991bc958534bf28c646e1f0f88ce8333e6 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 29 Mar 2019 12:53:43 -0700 Subject: [PATCH 076/160] Bump version, add changelog entries --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d31a81a..756ecfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Entries are listed in reverse chronological order. +## 0.5.2 + +* Implement `Clone` for `StaticSecret`. + +## 0.5.1 + +* Implement `Copy, Clone, Debug` for `PublicKey`. +* Remove doctests. + ## 0.5.0 * Adds support for static and ephemeral keys. diff --git a/Cargo.toml b/Cargo.toml index 9a68726..41976ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -version = "0.5.1" +version = "0.5.2" authors = [ "Isis Lovecruft ", "DebugSteven ", From 0f187b467054463d673e7ea91e503cab66983937 Mon Sep 17 00:00:00 2001 From: Bodo Junglas Date: Fri, 12 Apr 2019 08:34:41 +0200 Subject: [PATCH 077/160] Ensure that all data of StaticSecret is cleared on drop --- src/x25519.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 30f5bdb..f58e221 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -164,14 +164,12 @@ impl SharedSecret { /// # Returns /// /// A `Scalar`. -fn clamp_scalar(scalar: [u8; 32]) -> Scalar { - let mut s: [u8; 32] = scalar.clone(); +fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { + scalar[0] &= 248; + scalar[31] &= 127; + scalar[31] |= 64; - s[0] &= 248; - s[31] &= 127; - s[31] |= 64; - - Scalar::from_bits(s) + Scalar::from_bits(scalar) } /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. From 1d61e1ba46e40575688cb2c664329d1d0125eb14 Mon Sep 17 00:00:00 2001 From: Peat Bakke Date: Wed, 5 Jun 2019 12:20:02 -0700 Subject: [PATCH 078/160] Add .to_bytes() to PublicKey, so that it has similar capabilities to the ed25519 PublicKey impl. Also to SharedSecret for consistency. --- src/x25519.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index 30f5bdb..5fcc08c 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -36,6 +36,12 @@ impl From<[u8; 32]> for PublicKey { } impl PublicKey { + /// Convert this public key to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + /// View this public key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { @@ -149,6 +155,12 @@ impl Drop for SharedSecret { } impl SharedSecret { + /// Convert this shared secret to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + /// View this shared secret key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { From 9c3d6921d00e6278bc34568d9f48766befd73b0e Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 24 Oct 2019 15:44:57 -0700 Subject: [PATCH 079/160] Use 2018 edition --- Cargo.toml | 1 + benches/x25519.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 41976ff..850188f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "x25519-dalek" +edition = "2018" version = "0.5.2" authors = [ "Isis Lovecruft ", diff --git a/benches/x25519.rs b/benches/x25519.rs index cfded70..a5f6a91 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -19,7 +19,7 @@ extern crate x25519_dalek; use criterion::Criterion; -use curve25519_dalek::montgomery::MontgomeryPoint; + use rand_os::OsRng; diff --git a/src/lib.rs b/src/lib.rs index 8c1916f..de23f56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,4 +35,4 @@ extern crate rand_os; mod x25519; -pub use x25519::*; +pub use crate::x25519::*; From 0c3981d87a71c0879c082a8b1b5d7c7e3fdd05e7 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Thu, 24 Oct 2019 18:02:26 -0400 Subject: [PATCH 080/160] support for serde serialize and deserialize --- Cargo.toml | 8 +- benches/x25519.rs | 8 +- src/x25519.rs | 191 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 151 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 850188f..5a0de68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,16 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly"] [dependencies] -curve25519-dalek = { version = "1", default-features = false } +curve25519-dalek = { version = "2.0.0-alpha.0", default-features = false } rand_core = { version = "0.3", default-features = false } clear_on_drop = { version = "0.2" } +# `serde` is renamed to `our_serde` in order to avoid a name collision between +# importing the serde dependency and enabling the curve25519-dalek/serde feature +our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } +zeroize = { version = "1", default-features = false } [dev-dependencies] +bincode = "1" criterion = "0.2" rand_os = "0.1" @@ -43,6 +48,7 @@ harness = false [features] default = ["std", "u64_backend"] +serde = ["our_serde", "curve25519-dalek/serde"] std = ["curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/benches/x25519.rs b/benches/x25519.rs index a5f6a91..dc06474 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -19,12 +19,10 @@ extern crate x25519_dalek; use criterion::Criterion; - - use rand_os::OsRng; -use x25519_dalek::PublicKey; use x25519_dalek::EphemeralSecret; +use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { let mut csprng: OsRng = OsRng::new().unwrap(); @@ -39,12 +37,12 @@ fn bench_diffie_hellman(c: &mut Criterion) { }); } -criterion_group!{ +criterion_group! { name = x25519_benches; config = Criterion::default(); targets = bench_diffie_hellman, } -criterion_main!{ +criterion_main! { x25519_benches, } diff --git a/src/x25519.rs b/src/x25519.rs index f58e221..2a71a63 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -20,13 +20,18 @@ use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; -use rand_core::RngCore; use rand_core::CryptoRng; +use rand_core::RngCore; /// A `PublicKey` is the corresponding public key converted from /// an `EphemeralSecret` or a `StaticSecret` key. +#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] +#[cfg_attr( + feature = "serde", + derive(our_serde::Serialize, our_serde::Deserialize) +)] #[derive(Copy, Clone, Debug)] -pub struct PublicKey(pub (crate) MontgomeryPoint); +pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { /// Given a byte array, construct a x25519 `PublicKey`. @@ -45,7 +50,7 @@ impl PublicKey { /// A `EphemeralSecret` is a short lived Diffie-Hellman secret key /// used to create a `SharedSecret` when given their `PublicKey`. -pub struct EphemeralSecret(pub (crate) Scalar); +pub struct EphemeralSecret(pub(crate) Scalar); /// Overwrite ephemeral secret key material with null bytes when it goes out of scope. impl Drop for EphemeralSecret { @@ -63,7 +68,8 @@ impl EphemeralSecret { /// Generate an x25519 `EphemeralSecret` key. pub fn new(csprng: &mut T) -> Self - where T: RngCore + CryptoRng + where + T: RngCore + CryptoRng, { let mut bytes = [0u8; 32]; @@ -71,7 +77,6 @@ impl EphemeralSecret { EphemeralSecret(clamp_scalar(bytes)) } - } impl<'a> From<&'a EphemeralSecret> for PublicKey { @@ -80,14 +85,20 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { fn from(secret: &'a EphemeralSecret) -> PublicKey { PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } - } /// A `StaticSecret` is a static Diffie-Hellman secret key that /// can be saved and loaded to create a `SharedSecret` when given /// their `PublicKey`. +#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] +#[cfg_attr( + feature = "serde", + derive(our_serde::Serialize, our_serde::Deserialize) +)] #[derive(Clone)] -pub struct StaticSecret(pub (crate) Scalar); +pub struct StaticSecret( + #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, +); /// Overwrite static secret key material with null bytes when it goes out of scope. impl Drop for StaticSecret { @@ -105,7 +116,8 @@ impl StaticSecret { /// Generate a x25519 `StaticSecret` key. pub fn new(csprng: &mut T) -> Self - where T: RngCore + CryptoRng + where + T: RngCore + CryptoRng, { let mut bytes = [0u8; 32]; @@ -118,7 +130,6 @@ impl StaticSecret { pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } - } impl From<[u8; 32]> for StaticSecret { @@ -134,12 +145,11 @@ impl<'a> From<&'a StaticSecret> for PublicKey { fn from(secret: &'a StaticSecret) -> PublicKey { PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } - } /// A `SharedSecret` is a Diffie-Hellman shared secret that’s generated /// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`. -pub struct SharedSecret(pub (crate) MontgomeryPoint); +pub struct SharedSecret(pub(crate) MontgomeryPoint); /// Overwrite shared secret material with null bytes when it goes out of scope. impl Drop for SharedSecret { @@ -165,7 +175,7 @@ impl SharedSecret { /// /// A `Scalar`. fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { - scalar[0] &= 248; + scalar[0] &= 248; scalar[31] &= 127; scalar[31] |= 64; @@ -187,6 +197,24 @@ pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; +/// Derived serialization methods will not work on a StaticSecret because x25519 requires +/// non-canonical scalars which are rejected by curve25519-dalek. Thus we provide a way to convert +/// the bytes directly to a scalar using Serde's remote derive functionality. +#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] +#[cfg_attr( + feature = "serde", + derive(our_serde::Serialize, our_serde::Deserialize) +)] +#[cfg_attr(feature = "serde", serde(remote = "Scalar"))] +struct AllowUnreducedScalarBytes( + #[cfg_attr(feature = "serde", serde(getter = "Scalar::to_bytes"))] [u8; 32], +); +impl From for Scalar { + fn from(bytes: AllowUnreducedScalarBytes) -> Scalar { + clamp_scalar(bytes.0) + } +} + #[cfg(test)] mod test { use super::*; @@ -226,6 +254,57 @@ mod test { } } + #[test] + #[cfg(feature = "serde")] + fn serde_bincode_public_key_roundtrip() { + use bincode; + + let public_key = PublicKey::from(X25519_BASEPOINT_BYTES); + + let encoded = bincode::serialize(&public_key).unwrap(); + let decoded: PublicKey = bincode::deserialize(&encoded).unwrap(); + + assert_eq!(encoded.len(), 32); + assert_eq!(decoded.as_bytes(), public_key.as_bytes()); + } + + #[test] + #[cfg(feature = "serde")] + fn serde_bincode_public_key_matches_from_bytes() { + use bincode; + + let expected = PublicKey::from(X25519_BASEPOINT_BYTES); + let decoded: PublicKey = bincode::deserialize(&X25519_BASEPOINT_BYTES).unwrap(); + + assert_eq!(decoded.as_bytes(), expected.as_bytes()); + } + + #[test] + #[cfg(feature = "serde")] + fn serde_bincode_static_secret_roundtrip() { + use bincode; + + let static_secret = StaticSecret(clamp_scalar([0x24; 32])); + + let encoded = bincode::serialize(&static_secret).unwrap(); + let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap(); + + assert_eq!(encoded.len(), 32); + assert_eq!(decoded.to_bytes(), static_secret.to_bytes()); + } + + #[test] + #[cfg(feature = "serde")] + fn serde_bincode_static_secret_matches_from_bytes() { + use bincode; + + let expected = StaticSecret(clamp_scalar([0x24; 32])); + let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); + let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); + + assert_eq!(decoded.to_bytes(), expected.to_bytes()); + } + fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) { let result = x25519(input_scalar, input_point); @@ -235,20 +314,20 @@ mod test { #[test] fn rfc7748_ladder_test1_vectorset1() { let input_scalar: [u8; 32] = [ - 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, - 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, - 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, - 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4, ]; + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, + 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, + 0xba, 0x44, 0x9a, 0xc4, + ]; let input_point: [u8; 32] = [ - 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, - 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, - 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, - 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, ]; + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, + 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, + 0xd0, 0xab, 0x1c, 0x4c, + ]; let expected: [u8; 32] = [ - 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, - 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, - 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, - 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52, ]; + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, + 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, + 0x77, 0xa2, 0x85, 0x52, + ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); } @@ -256,20 +335,20 @@ mod test { #[test] fn rfc7748_ladder_test1_vectorset2() { let input_scalar: [u8; 32] = [ - 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, - 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, - 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, - 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d, ]; + 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, + 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, + 0x79, 0x18, 0xba, 0x0d, + ]; let input_point: [u8; 32] = [ - 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, - 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, - 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, - 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, ]; + 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, + 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, + 0xc7, 0x15, 0xa4, 0x93, + ]; let expected: [u8; 32] = [ - 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, - 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, - 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, - 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57, ]; + 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, + 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, + 0x7a, 0xac, 0x79, 0x57, + ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); } @@ -284,7 +363,7 @@ mod test { let mut result: [u8; 32]; macro_rules! do_iterations { - ($n:expr) => ( + ($n:expr) => { for _ in 0..$n { result = x25519(k, u); // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE @@ -298,7 +377,7 @@ mod test { u = k.clone(); k = result; } - ) + }; } // After one iteration: @@ -309,19 +388,31 @@ mod test { // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 do_iterations!(1); - assert_eq!(k, [ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, - 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, - 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, - 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79, ]); + assert_eq!( + k, + [ + 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, + 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, + 0x11, 0xae, 0x30, 0x79, + ] + ); do_iterations!(999); - assert_eq!(k, [ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, - 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, - 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, - 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51, ]); + assert_eq!( + k, + [ + 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, + 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, + 0x99, 0x53, 0x2c, 0x51, + ] + ); do_iterations!(999_000); - assert_eq!(k, [ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, - 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, - 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, - 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24, ]); + assert_eq!( + k, + [ + 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, + 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, + 0x4f, 0x66, 0x54, 0x24, + ] + ); } } From de5d79388bec3a07cbf108bcca887a1fd2ada65c Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Sun, 3 Mar 2019 17:29:59 -0700 Subject: [PATCH 081/160] use zeroize instead of clear_on_drop --- Cargo.toml | 7 +++---- src/lib.rs | 4 ++-- src/x25519.rs | 32 ++++++++------------------------ 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5a0de68..2888195 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,13 +29,12 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly"] [dependencies] -curve25519-dalek = { version = "2.0.0-alpha.0", default-features = false } +curve25519-dalek = { version = "2", default-features = false } rand_core = { version = "0.3", default-features = false } -clear_on_drop = { version = "0.2" } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "1", default-features = false } +zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" @@ -50,6 +49,6 @@ harness = false default = ["std", "u64_backend"] serde = ["our_serde", "curve25519-dalek/serde"] std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly"] +nightly = ["curve25519-dalek/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] diff --git a/src/lib.rs b/src/lib.rs index de23f56..888a806 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,12 +24,12 @@ //! Note that docs will only build on nightly Rust until //! `feature(external_doc)` is stabilized. -extern crate clear_on_drop; - extern crate curve25519_dalek; extern crate rand_core; +extern crate zeroize; + #[cfg(test)] extern crate rand_os; diff --git a/src/x25519.rs b/src/x25519.rs index 2a71a63..0663146 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,8 +14,6 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use clear_on_drop::clear::Clear; - use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; @@ -23,6 +21,8 @@ use curve25519_dalek::scalar::Scalar; use rand_core::CryptoRng; use rand_core::RngCore; +use zeroize::Zeroize; + /// A `PublicKey` is the corresponding public key converted from /// an `EphemeralSecret` or a `StaticSecret` key. #[cfg_attr(feature = "serde", serde(crate = "our_serde"))] @@ -50,15 +50,10 @@ impl PublicKey { /// A `EphemeralSecret` is a short lived Diffie-Hellman secret key /// used to create a `SharedSecret` when given their `PublicKey`. +#[derive(Zeroize)] +#[zeroize(drop)] pub struct EphemeralSecret(pub(crate) Scalar); -/// Overwrite ephemeral secret key material with null bytes when it goes out of scope. -impl Drop for EphemeralSecret { - fn drop(&mut self) { - self.0.clear(); - } -} - impl EphemeralSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. @@ -95,18 +90,12 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { feature = "serde", derive(our_serde::Serialize, our_serde::Deserialize) )] -#[derive(Clone)] +#[derive(Clone, Zeroize)] +#[zeroize(drop)] pub struct StaticSecret( #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, ); -/// Overwrite static secret key material with null bytes when it goes out of scope. -impl Drop for StaticSecret { - fn drop(&mut self) { - self.0.clear(); - } -} - impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. @@ -149,15 +138,10 @@ impl<'a> From<&'a StaticSecret> for PublicKey { /// A `SharedSecret` is a Diffie-Hellman shared secret that’s generated /// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`. +#[derive(Zeroize)] +#[zeroize(drop)] pub struct SharedSecret(pub(crate) MontgomeryPoint); -/// Overwrite shared secret material with null bytes when it goes out of scope. -impl Drop for SharedSecret { - fn drop(&mut self) { - self.0.clear(); - } -} - impl SharedSecret { /// View this shared secret key as a byte array. #[inline] From be82bcb15b57ed6a07e92a0643b8355bd8d653a3 Mon Sep 17 00:00:00 2001 From: DebugSteven Date: Tue, 26 Nov 2019 14:09:03 -0700 Subject: [PATCH 082/160] update rand_core version Co-authored-by: Greg --- Cargo.toml | 3 +-- benches/x25519.rs | 9 ++++----- src/lib.rs | 3 --- src/x25519.rs | 7 +++---- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2888195..69cc1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ features = ["nightly"] [dependencies] curve25519-dalek = { version = "2", default-features = false } -rand_core = { version = "0.3", default-features = false } +rand_core = { version = "0.5", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } @@ -39,7 +39,6 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive [dev-dependencies] bincode = "1" criterion = "0.2" -rand_os = "0.1" [[bench]] name = "x25519" diff --git a/benches/x25519.rs b/benches/x25519.rs index dc06474..e5d77d2 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -14,24 +14,23 @@ #[macro_use] extern crate criterion; extern crate curve25519_dalek; -extern crate rand_os; +extern crate rand_core; extern crate x25519_dalek; use criterion::Criterion; -use rand_os::OsRng; +use rand_core::OsRng; use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { - let mut csprng: OsRng = OsRng::new().unwrap(); - let bob_secret = EphemeralSecret::new(&mut csprng); + let bob_secret = EphemeralSecret::new(&mut OsRng); let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( - || EphemeralSecret::new(&mut csprng), + || EphemeralSecret::new(&mut OsRng), |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); diff --git a/src/lib.rs b/src/lib.rs index 888a806..0f80f7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,9 +30,6 @@ extern crate rand_core; extern crate zeroize; -#[cfg(test)] -extern crate rand_os; - mod x25519; pub use crate::x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs index 0663146..e95d0e3 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -203,17 +203,16 @@ impl From for Scalar { mod test { use super::*; - use rand_os::OsRng; + use rand_core::OsRng; // This was previously a doctest but it got moved to the README to // avoid duplication where it then wasn't being run, so now it // lives here. #[test] fn alice_and_bob() { - let mut csprng = OsRng::new().unwrap(); - let alice_secret = EphemeralSecret::new(&mut csprng); + let alice_secret = EphemeralSecret::new(&mut OsRng); let alice_public = PublicKey::from(&alice_secret); - let bob_secret = EphemeralSecret::new(&mut csprng); + let bob_secret = EphemeralSecret::new(&mut OsRng); let bob_public = PublicKey::from(&bob_secret); let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); From d9c7f2814e3d2fcaf34dcaed54fbab27fcbf9f34 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 26 Nov 2019 14:03:08 -0800 Subject: [PATCH 083/160] Bump version to 0.6.0 and update CHANGELOG. --- CHANGELOG.md | 7 +++++++ Cargo.toml | 3 ++- README.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 756ecfd..66b1ed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Entries are listed in reverse chronological order. +## 0.6.0 + +* Updates `rand_core` version to `0.5`. +* Adds `serde` support. +* Replaces `clear_on_drop` with `zeroize`. +* Use Rust 2018. + ## 0.5.2 * Implement `Clone` for `StaticSecret`. diff --git a/Cargo.toml b/Cargo.toml index 69cc1b1..e86eb01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "x25519-dalek" edition = "2018" -version = "0.5.2" +# Be sure to update the version in README.md +version = "0.6.0" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/README.md b/README.md index e1c37df..be90d5a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies.x25519-dalek] -version = "^0.5" +version = "0.6" ``` # Documentation From bdc6412faa1fbbf06be41f063ae34becbbe7682d Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 25 Feb 2020 12:09:40 -0800 Subject: [PATCH 084/160] README.md: Add "See also" section with link to `crypto_box` crate The `crypto_box` crate provides a pure Rust implementation of the public-key authenticated encryption primitive from NaCl which combines X25519 + XSalsa20Poly1305 (a.k.a. "Curve25519XSalsa20Poly1305") This commit adds a link to it case x25519-dalek users are interested in using it as part of a hybrid cryptosystem. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index be90d5a..ff34263 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,11 @@ attempt to prevent software side-channels. copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) [rfc7748]: https://tools.ietf.org/html/rfc7748 + +# See also + +- [crypto_box]: pure Rust public-key authenticated encryption compatible with + the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses + `x25519-dalek` for key agreement + +[crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box From be420d4ffce2c45e4ba1d3e046cdc7cb8e731c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Wed, 4 Mar 2020 22:35:33 -0500 Subject: [PATCH 085/160] Bump criterion version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e86eb01..a092ec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive [dev-dependencies] bincode = "1" -criterion = "0.2" +criterion = "0.3.0" [[bench]] name = "x25519" From 5d91bd8f22f8316c114a313404975629f337f1e4 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 18:44:48 -0700 Subject: [PATCH 086/160] Make bound on `csprng` more general. `rand_core` defines a blanket impl of `RngCore + CryptoRng` for `&mut T` where `T: RngCore + CryptoRng`, so rather than requiring a borrowed RNG, it's better to require an owned RNG, as this allows passing either owned or borrowed values. In particular, this makes `OsRng` usage much more ergonomic, because the caller is not forced to do `&mut OsRng` on the zero-sized struct. --- src/x25519.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index e95d0e3..6b18781 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -62,10 +62,7 @@ impl EphemeralSecret { } /// Generate an x25519 `EphemeralSecret` key. - pub fn new(csprng: &mut T) -> Self - where - T: RngCore + CryptoRng, - { + pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); @@ -104,10 +101,7 @@ impl StaticSecret { } /// Generate a x25519 `StaticSecret` key. - pub fn new(csprng: &mut T) -> Self - where - T: RngCore + CryptoRng, - { + pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); From 8287798aa12ef2147f785429a8f7325a4f2f0513 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 18:47:31 -0700 Subject: [PATCH 087/160] Update doc examples to remove deprecated code and restore testing. Closes #59. The doc examples have code interspersed with text explaining the API. Because each doctest executes independently, when these code examples are run as doctests, they have to include parts of the previous examples with # lines. These lines are hidden from Rustdoc output and do not appear in the rendered docs, but they do appear when viewing the README.md on Github. In order to hide these on Github, the code blocks were made non-executable, with their content moved to a unit test. However, this meant that the example API usage was not tested, and so when the unit test was updated to remove the deprecated `rand_os`, there was no check that the examples stayed in sync with the test, causing #59. To prevent this from reocurring in the future, go back to executable tests of the API examples. --- README.md | 51 +++++++++++++++++++++++++++++++++------------------ src/x25519.rs | 15 --------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ff34263..28289be 100644 --- a/README.md +++ b/README.md @@ -31,45 +31,60 @@ the rest of the afternoon nomming some yummy pie! First, Alice uses `EphemeralSecret::new()` and then `PublicKey::from()` to produce her secret and public keys: -```rust,ignore -extern crate rand_os; -extern crate x25519_dalek; +```rust +use rand_core::OsRng; +use x25519_dalek::{EphemeralSecret, PublicKey}; -use rand_os::OsRng; - -use x25519_dalek::EphemeralSecret; -use x25519_dalek::PublicKey; - -let mut alice_csprng = OsRng::new().unwrap(); -let alice_secret = EphemeralSecret::new(&mut alice_csprng); -let alice_public = PublicKey::from(&alice_secret); +let alice_secret = EphemeralSecret::new(OsRng); +let alice_public = PublicKey::from(&alice_secret); ``` Bob does the same: -```rust,ignore -let mut bob_csprng = OsRng::new().unwrap(); -let bob_secret = EphemeralSecret::new(&mut bob_csprng); -let bob_public = PublicKey::from(&bob_secret); +```rust +# use rand_core::OsRng; +# use x25519_dalek::{EphemeralSecret, PublicKey}; +let bob_secret = EphemeralSecret::new(OsRng); +let bob_public = PublicKey::from(&bob_secret); ``` Alice meows across the room, telling `alice_public` to Bob, and Bob loudly meows `bob_public` back to Alice. Alice now computes her shared secret with Bob by doing: -```rust,ignore +```rust +# use rand_core::OsRng; +# use x25519_dalek::{EphemeralSecret, PublicKey}; +# let alice_secret = EphemeralSecret::new(OsRng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(OsRng); +# let bob_public = PublicKey::from(&bob_secret); let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); ``` Similarly, Bob computes a shared secret by doing: -```rust,ignore +```rust +# use rand_core::OsRng; +# use x25519_dalek::{EphemeralSecret, PublicKey}; +# let alice_secret = EphemeralSecret::new(OsRng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(OsRng); +# let bob_public = PublicKey::from(&bob_secret); let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); ``` These secrets are the same: -```rust,ignore +```rust +# use rand_core::OsRng; +# use x25519_dalek::{EphemeralSecret, PublicKey}; +# let alice_secret = EphemeralSecret::new(OsRng); +# let alice_public = PublicKey::from(&alice_secret); +# let bob_secret = EphemeralSecret::new(OsRng); +# let bob_public = PublicKey::from(&bob_secret); +# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); +# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); ``` diff --git a/src/x25519.rs b/src/x25519.rs index 6b18781..8866cd5 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -199,21 +199,6 @@ mod test { use rand_core::OsRng; - // This was previously a doctest but it got moved to the README to - // avoid duplication where it then wasn't being run, so now it - // lives here. - #[test] - fn alice_and_bob() { - let alice_secret = EphemeralSecret::new(&mut OsRng); - let alice_public = PublicKey::from(&alice_secret); - let bob_secret = EphemeralSecret::new(&mut OsRng); - let bob_public = PublicKey::from(&bob_secret); - let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); - let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); - - assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); - } - #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; From fb92cd82da40bf97aa905eed413794e27069c6e3 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 19:24:49 -0700 Subject: [PATCH 088/160] Clarify Ephemeral/StaticSecret docs. Also does a pass through the docs converting `TypeNames` to Rustdoc links, and making sure that all the items have consistent summaries. Closes #58 Closes $56 --- src/x25519.rs | 53 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 0d6b557..9fbda07 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -23,8 +23,7 @@ use rand_core::RngCore; use zeroize::Zeroize; -/// A `PublicKey` is the corresponding public key converted from -/// an `EphemeralSecret` or a `StaticSecret` key. +/// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or [`StaticSecret`] key. #[cfg_attr(feature = "serde", serde(crate = "our_serde"))] #[cfg_attr( feature = "serde", @@ -54,20 +53,26 @@ impl PublicKey { } } -/// A `EphemeralSecret` is a short lived Diffie-Hellman secret key -/// used to create a `SharedSecret` when given their `PublicKey`. +/// A short-lived Diffie-Hellman secret key that can only be used to compute a single +/// [`SharedSecret`]. +/// +/// This type is identical to the [`StaticSecret`] type, except that the +/// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there +/// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be +/// generated from fresh randomness by [`EphemeralSecret::new`] and the compiler statically checks +/// that the resulting secret is used at most once. #[derive(Zeroize)] #[zeroize(drop)] pub struct EphemeralSecret(pub(crate) Scalar); impl EphemeralSecret { /// Perform a Diffie-Hellman key agreement between `self` and - /// `their_public` key to produce a `SharedSecret`. + /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { SharedSecret(self.0 * their_public.0) } - /// Generate an x25519 `EphemeralSecret` key. + /// Generate an x25519 [`EphemeralSecret`] key. pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; @@ -78,16 +83,27 @@ impl EphemeralSecret { } impl<'a> From<&'a EphemeralSecret> for PublicKey { - /// Given an x25519 `EphemeralSecret` key, compute its corresponding - /// `PublicKey` key. + /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a EphemeralSecret) -> PublicKey { PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } } -/// A `StaticSecret` is a static Diffie-Hellman secret key that -/// can be saved and loaded to create a `SharedSecret` when given -/// their `PublicKey`. +/// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s. +/// +/// This type is identical to the [`EphemeralSecret`] type, except that the +/// [`StaticSecret::diffie_hellman`] method does not consume the secret key, and the type provides +/// serialization methods to save and load key material. This means that the secret may be used +/// multiple times (but does not *have to be*). +/// +/// Some protocols, such as Noise, already handle the static/ephemeral distinction, so the +/// additional guarantees provided by [`EphemeralSecret`] are not helpful or would cause duplicate +/// code paths. In this case, it may be useful to +/// ```rust,ignore +/// use x25519_dalek::StaticSecret as SecretKey; +/// ``` +/// since the only difference between the two is that [`StaticSecret`] does not enforce at +/// compile-time that the key is only used once. #[cfg_attr(feature = "serde", serde(crate = "our_serde"))] #[cfg_attr( feature = "serde", @@ -106,7 +122,7 @@ impl StaticSecret { SharedSecret(&self.0 * their_public.0) } - /// Generate a x25519 `StaticSecret` key. + /// Generate an x25519 key. pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; @@ -115,29 +131,30 @@ impl StaticSecret { StaticSecret(clamp_scalar(bytes)) } - /// Save a x25519 `StaticSecret` key's bytes. + /// Extract this key's bytes for serialization. pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } } impl From<[u8; 32]> for StaticSecret { - /// Load a `StaticSecret` from a byte array. + /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { StaticSecret(clamp_scalar(bytes)) } } impl<'a> From<&'a StaticSecret> for PublicKey { - /// Given an x25519 `StaticSecret` key, compute its corresponding - /// `PublicKey` key. + /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } } -/// A `SharedSecret` is a Diffie-Hellman shared secret that’s generated -/// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`. +/// The result of a Diffie-Hellman key exchange. +/// +/// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their +/// counterparty's [`PublicKey`]. #[derive(Zeroize)] #[zeroize(drop)] pub struct SharedSecret(pub(crate) MontgomeryPoint); From 1b01d597ca0e7e3c1182d1642c31303b387f62e1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 19:43:22 -0700 Subject: [PATCH 089/160] Bump version to 1.0.0 and update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 7 +++++-- README.md | 4 ++-- src/lib.rs | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b1ed1..6313216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Entries are listed in reverse chronological order. +## 1.0.0 + +* Widen generic bound on `EphemeralSecret::new` and `StaticSecret::new` to + allow owned as well as borrowed RNGs. +* Add `PublicKey::to_bytes` and `SharedSecret::to_bytes`, returning owned byte + arrays, complementing the existing `as_bytes` methods returning references. +* Remove mention of deprecated `rand_os` crate from examples. +* Clarify `EphemeralSecret`/`StaticSecret` distinction in documentation. + ## 0.6.0 * Updates `rand_core` version to `0.5`. diff --git a/Cargo.toml b/Cargo.toml index a092ec0..31e4457 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,11 @@ [package] name = "x25519-dalek" edition = "2018" -# Be sure to update the version in README.md -version = "0.6.0" +# Before changing this: +# - update version in README.md +# - update html_root_url +# - update CHANGELOG +version = "1.0.0" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/README.md b/README.md index 28289be..a05c1f5 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,8 @@ and load a long-term secret key. To install, add the following to your project's `Cargo.toml`: ```toml -[dependencies.x25519-dalek] -version = "0.6" +[dependencies] +x25519-dalek = "1" ``` # Documentation diff --git a/src/lib.rs b/src/lib.rs index 0f80f7e..8a16049 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.0.0")] //! Note that docs will only build on nightly Rust until //! `feature(external_doc)` is stabilized. From 2408e6b1797daa3c2c399804b2f6d498cd3e6255 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 20:14:51 -0700 Subject: [PATCH 090/160] Update curve25519-dalek to version 3.0.0. This allows unifying dependencies with other crates using the `3.x` series of the curve library. It is a semver patch-level change, because the x25519-dalek API does not expose any details of the underlying curve implementation. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 31e4457..7c66c2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly"] [dependencies] -curve25519-dalek = { version = "2", default-features = false } +curve25519-dalek = { version = "3", default-features = false } rand_core = { version = "0.5", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature From af6d5bd5bf0993a742c0c970a90ebf28250ffc34 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 20:37:20 -0700 Subject: [PATCH 091/160] Bump version to 1.0.1 --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6313216..29d2eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Entries are listed in reverse chronological order. +## 1.0.1 + +* Update underlying `curve25519_dalek` library to `3.0`. + ## 1.0.0 * Widen generic bound on `EphemeralSecret::new` and `StaticSecret::new` to diff --git a/Cargo.toml b/Cargo.toml index 7c66c2e..57e92bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" # - update version in README.md # - update html_root_url # - update CHANGELOG -version = "1.0.0" +version = "1.0.1" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/src/lib.rs b/src/lib.rs index 8a16049..0d9b299 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.0.0")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.0.1")] //! Note that docs will only build on nightly Rust until //! `feature(external_doc)` is stabilized. From 48df927d7b774dd34f3726fa5cf4c9fdd0f1d71d Mon Sep 17 00:00:00 2001 From: Jack Michaud Date: Sun, 30 Aug 2020 17:41:31 -0700 Subject: [PATCH 092/160] Add PartialEq derive to PublicKey Derives from the PartialEq impl for MontgomeryPoint --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index 9fbda07..ba877d0 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -29,7 +29,7 @@ use zeroize::Zeroize; feature = "serde", derive(our_serde::Serialize, our_serde::Deserialize) )] -#[derive(Copy, Clone, Debug)] +#[derive(PartialEq, Copy, Clone, Debug)] pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { From 3c0966411297448c38c5a9ee5d04556371ec2509 Mon Sep 17 00:00:00 2001 From: Jack Michaud Date: Mon, 31 Aug 2020 12:13:22 -0700 Subject: [PATCH 093/160] Feedback from @hdevalence - Added derive for Eq and Hash --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index ba877d0..3244899 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -29,7 +29,7 @@ use zeroize::Zeroize; feature = "serde", derive(our_serde::Serialize, our_serde::Deserialize) )] -#[derive(PartialEq, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { From e8615a932617c80414f55f7cc0eb2714e8bc4320 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 31 Aug 2020 12:56:34 -0700 Subject: [PATCH 094/160] bump version to 1.1.0 and update CHANGELOG --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- README.md | 2 +- src/lib.rs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d2eeb..1ae653f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Entries are listed in reverse chronological order. +## 1.1.0 + +* Add impls of `PartialEq`, `Eq`, and `Hash` for `PublicKey` (by @jack-michaud) + ## 1.0.1 * Update underlying `curve25519_dalek` library to `3.0`. diff --git a/Cargo.toml b/Cargo.toml index 57e92bb..78a2004 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" # - update version in README.md # - update html_root_url # - update CHANGELOG -version = "1.0.1" +version = "1.1.0" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/README.md b/README.md index a05c1f5..bc25a84 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies] -x25519-dalek = "1" +x25519-dalek = "1.1" ``` # Documentation diff --git a/src/lib.rs b/src/lib.rs index 0d9b299..4ffc1bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.0.1")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.0")] //! Note that docs will only build on nightly Rust until //! `feature(external_doc)` is stabilized. From 9717eb8d526c3d6593524fdfdf45e1f9c96d1a95 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Mon, 14 Dec 2020 18:07:06 +0000 Subject: [PATCH 095/160] Update rand_core to 0.6 https://github.com/rust-random/rand/blob/master/rand_core/CHANGELOG.md This new version makes using rand_core on wasm seamless (thanks to the update of getrandom to v0.2) The crate compiles well with this PR, but since some rand_core traits are publicly exposed in this crate's API, this is strictly speaking a breaking change. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 78a2004..70cef40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ features = ["nightly"] [dependencies] curve25519-dalek = { version = "3", default-features = false } -rand_core = { version = "0.5", default-features = false } +rand_core = { version = "0.6", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } From 050034c98c84bfc6ed185147f3e769d0c38c702b Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Mon, 21 Dec 2020 22:53:43 -0300 Subject: [PATCH 096/160] Fix rand_core test by enabling "getrandom" feature --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 70cef40..6cc07cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ features = ["nightly"] [dependencies] curve25519-dalek = { version = "3", default-features = false } -rand_core = { version = "0.6", default-features = false } +rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } From f40a0a060d5b605dd0671d14b036567fbbe1554f Mon Sep 17 00:00:00 2001 From: Olivier Blazy Date: Fri, 12 Feb 2021 10:53:34 +0100 Subject: [PATCH 097/160] Update README.md There was acute typo, not agrave mistake. ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc25a84..dee9acc 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ These secrets are the same: assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); ``` -Voilá! Alice and Bob can now use their shared secret to encrypt their +Voilà! Alice and Bob can now use their shared secret to encrypt their meows, for example, by using it to generate a key and nonce for an authenticated-encryption cipher. From 1c39ff92e0dfc0b24aa02d694f26f3b9539322a5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 04:31:12 +0000 Subject: [PATCH 098/160] Update copyright years. --- LICENSE | 4 ++-- src/lib.rs | 4 ++-- src/x25519.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index 0443d91..6577d97 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2017-2019 isis agora lovecruft. All rights reserved. -Copyright (c) 2019 DebugSteven. All rights reserved. +Copyright (c) 2017-2021 isis agora lovecruft. All rights reserved. +Copyright (c) 2019-2021 DebugSteven. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/src/lib.rs b/src/lib.rs index 4ffc1bf..494ec45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ // -*- mode: rust; -*- // // This file is part of x25519-dalek. -// Copyright (c) 2017-2019 isis lovecruft -// Copyright (c) 2019 DebugSteven +// Copyright (c) 2017-2021 isis lovecruft +// Copyright (c) 2019-2021 DebugSteven // See LICENSE for licensing information. // // Authors: diff --git a/src/x25519.rs b/src/x25519.rs index 3244899..1de4454 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -1,8 +1,8 @@ // -*- mode: rust; -*- // // This file is part of x25519-dalek. -// Copyright (c) 2017-2019 isis lovecruft -// Copyright (c) 2019 DebugSteven +// Copyright (c) 2017-2021 isis lovecruft +// Copyright (c) 2019-2021 DebugSteven // See LICENSE for licensing information. // // Authors: From f616c8b2c545d5405d497c0ba5939829c06d6e70 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 04:38:37 +0000 Subject: [PATCH 099/160] Update CHANGELOG and README; bump version to 1.1.1. --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae653f..588b31f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Entries are listed in reverse chronological order. +## 1.1.1 + +* Fix a typo in the README. + ## 1.1.0 * Add impls of `PartialEq`, `Eq`, and `Hash` for `PublicKey` (by @jack-michaud) diff --git a/Cargo.toml b/Cargo.toml index 78a2004..16aa97c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" # - update version in README.md # - update html_root_url # - update CHANGELOG -version = "1.1.0" +version = "1.1.1" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/src/lib.rs b/src/lib.rs index 494ec45..de5ef19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.0")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.1")] //! Note that docs will only build on nightly Rust until //! `feature(external_doc)` is stabilized. From fc945778ab43428b9efdff61b8a963d01615c6ec Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 05:05:54 +0000 Subject: [PATCH 100/160] Fixups for doctest from #33. --- src/x25519.rs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 064cb60..1c0c46f 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -193,34 +193,31 @@ fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { /// /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who /// cannot use the better, safer, and faster ephemeral DH API. +/// /// # Example /// ``` -/// extern crate rand_os; +/// # extern crate rand_core; +/// # +/// use rand_core::OsRng; +/// use rand_core::RngCore; /// -/// use x25519_dalek::{ x25519, X25519_BASEPOINT_BYTES }; -/// use rand_os::OsRng; -/// use rand_os::rand_core::RngCore; +/// use x25519_dalek::x25519; +/// use x25519_dalek::StaticSecret; +/// use x25519_dalek::PublicKey; /// -/// let mut rng = OsRng::new().unwrap(); +/// // Generate Alice's key pair. +/// let alice_secret = StaticSecret::new(&mut OsRng); +/// let alice_public = PublicKey::from(&alice_secret); /// -/// // Generate Alice key pair -/// let mut alice_private = [0u8; 32]; -/// rng.fill_bytes(&mut alice_private); +/// // Generate Bob's key pair. +/// let bob_secret = StaticSecret::new(&mut OsRng); +/// let bob_public = PublicKey::from(&bob_secret); /// -/// let alice_public = x25519(alice_private.clone(), X25519_BASEPOINT_BYTES); +/// // Alice and Bob should now exchange their public keys. /// -/// // Generate bob key pair -/// let mut bob_private = [0u8; 32]; -/// rng.fill_bytes(&mut bob_private); -/// -/// let bob_public = x25519(bob_private.clone(), X25519_BASEPOINT_BYTES); -/// -/// // Exchange the public keys -/// // ... -/// // Generate shared secret -/// -/// let alice_shared = x25519(alice_private, bob_public); -/// let bob_shared = x25519(bob_private, alice_public); +/// // Once they've done so, they may generate a shared secret. +/// let alice_shared = x25519(alice_secret.to_bytes(), bob_public.to_bytes()); +/// let bob_shared = x25519(bob_secret.to_bytes(), alice_public.to_bytes()); /// /// assert_eq!(alice_shared, bob_shared); /// ``` From 0cca7977fcc9c6ecf7a8d75d70d7ce3e75f9a87f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 19:29:09 +0000 Subject: [PATCH 101/160] Derive Zeroize for PublicKey. --- src/x25519.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 1c0c46f..12a262c 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -23,13 +23,19 @@ use rand_core::RngCore; use zeroize::Zeroize; -/// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or [`StaticSecret`] key. +/// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or +/// [`StaticSecret`] key. +/// +/// We implement `Zeroize` so that downstream consumers may derive it for `Drop` +/// should they wish to erase public keys from memory. Note that this erasure +/// (in this crate) does *not* automatically happen, but either must be derived +/// for Drop or explicitly called. #[cfg_attr(feature = "serde", serde(crate = "our_serde"))] #[cfg_attr( feature = "serde", derive(our_serde::Serialize, our_serde::Deserialize) )] -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Zeroize)] pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { From 9e387372e62177caa6c0b41aa2ec72655d9d0f6d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 19:33:11 +0000 Subject: [PATCH 102/160] Remove unused import from test suite. --- src/x25519.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 12a262c..4043076 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -260,8 +260,6 @@ impl From for Scalar { mod test { use super::*; - use rand_core::OsRng; - #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; From 2333b526981fd3db7b0cc55f826028d45a8425b2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 19:45:36 +0000 Subject: [PATCH 103/160] Move tests to a separate directory. --- src/x25519.rs | 184 ------------------------------------------- test/x25519_tests.rs | 182 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 184 deletions(-) create mode 100644 test/x25519_tests.rs diff --git a/src/x25519.rs b/src/x25519.rs index 4043076..538af36 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -255,187 +255,3 @@ impl From for Scalar { clamp_scalar(bytes.0) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn byte_basepoint_matches_edwards_scalar_mul() { - let mut scalar_bytes = [0x37; 32]; - - for i in 0..32 { - scalar_bytes[i] += 2; - - let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); - - let expected = (&ED25519_BASEPOINT_TABLE * &clamp_scalar(scalar_bytes)) - .to_montgomery() - .to_bytes(); - - assert_eq!(result, expected); - } - } - - #[test] - #[cfg(feature = "serde")] - fn serde_bincode_public_key_roundtrip() { - use bincode; - - let public_key = PublicKey::from(X25519_BASEPOINT_BYTES); - - let encoded = bincode::serialize(&public_key).unwrap(); - let decoded: PublicKey = bincode::deserialize(&encoded).unwrap(); - - assert_eq!(encoded.len(), 32); - assert_eq!(decoded.as_bytes(), public_key.as_bytes()); - } - - #[test] - #[cfg(feature = "serde")] - fn serde_bincode_public_key_matches_from_bytes() { - use bincode; - - let expected = PublicKey::from(X25519_BASEPOINT_BYTES); - let decoded: PublicKey = bincode::deserialize(&X25519_BASEPOINT_BYTES).unwrap(); - - assert_eq!(decoded.as_bytes(), expected.as_bytes()); - } - - #[test] - #[cfg(feature = "serde")] - fn serde_bincode_static_secret_roundtrip() { - use bincode; - - let static_secret = StaticSecret(clamp_scalar([0x24; 32])); - - let encoded = bincode::serialize(&static_secret).unwrap(); - let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap(); - - assert_eq!(encoded.len(), 32); - assert_eq!(decoded.to_bytes(), static_secret.to_bytes()); - } - - #[test] - #[cfg(feature = "serde")] - fn serde_bincode_static_secret_matches_from_bytes() { - use bincode; - - let expected = StaticSecret(clamp_scalar([0x24; 32])); - let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); - let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); - - assert_eq!(decoded.to_bytes(), expected.to_bytes()); - } - - fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) { - let result = x25519(input_scalar, input_point); - - assert_eq!(result, expected); - } - - #[test] - fn rfc7748_ladder_test1_vectorset1() { - let input_scalar: [u8; 32] = [ - 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, - 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, - 0xba, 0x44, 0x9a, 0xc4, - ]; - let input_point: [u8; 32] = [ - 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, - 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, - 0xd0, 0xab, 0x1c, 0x4c, - ]; - let expected: [u8; 32] = [ - 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, - 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, - 0x77, 0xa2, 0x85, 0x52, - ]; - - do_rfc7748_ladder_test1(input_scalar, input_point, expected); - } - - #[test] - fn rfc7748_ladder_test1_vectorset2() { - let input_scalar: [u8; 32] = [ - 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, - 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, - 0x79, 0x18, 0xba, 0x0d, - ]; - let input_point: [u8; 32] = [ - 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, - 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, - 0xc7, 0x15, 0xa4, 0x93, - ]; - let expected: [u8; 32] = [ - 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, - 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, - 0x7a, 0xac, 0x79, 0x57, - ]; - - do_rfc7748_ladder_test1(input_scalar, input_point, expected); - } - - #[test] - #[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations - fn rfc7748_ladder_test2() { - use curve25519_dalek::constants::X25519_BASEPOINT; - - let mut k: [u8; 32] = X25519_BASEPOINT.0; - let mut u: [u8; 32] = X25519_BASEPOINT.0; - let mut result: [u8; 32]; - - macro_rules! do_iterations { - ($n:expr) => { - for _ in 0..$n { - result = x25519(k, u); - // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE - // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS - // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH - // MY LIBRARY: - // - // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. - // - // ↓↓ DON'T DO THIS ↓↓ - u = k.clone(); - k = result; - } - }; - } - - // After one iteration: - // 422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079 - // After 1,000 iterations: - // 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51 - // After 1,000,000 iterations: - // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 - - do_iterations!(1); - assert_eq!( - k, - [ - 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, - 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, - 0x11, 0xae, 0x30, 0x79, - ] - ); - do_iterations!(999); - assert_eq!( - k, - [ - 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, - 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, - 0x99, 0x53, 0x2c, 0x51, - ] - ); - do_iterations!(999_000); - assert_eq!( - k, - [ - 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, - 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, - 0x4f, 0x66, 0x54, 0x24, - ] - ); - } -} diff --git a/test/x25519_tests.rs b/test/x25519_tests.rs new file mode 100644 index 0000000..c7cc6e2 --- /dev/null +++ b/test/x25519_tests.rs @@ -0,0 +1,182 @@ + +use x25519_dalek::*; + +#[test] +fn byte_basepoint_matches_edwards_scalar_mul() { + let mut scalar_bytes = [0x37; 32]; + + for i in 0..32 { + scalar_bytes[i] += 2; + + let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); + + let expected = (&ED25519_BASEPOINT_TABLE * &clamp_scalar(scalar_bytes)) + .to_montgomery() + .to_bytes(); + + assert_eq!(result, expected); + } +} + +#[test] +#[cfg(feature = "serde")] +fn serde_bincode_public_key_roundtrip() { + use bincode; + + let public_key = PublicKey::from(X25519_BASEPOINT_BYTES); + + let encoded = bincode::serialize(&public_key).unwrap(); + let decoded: PublicKey = bincode::deserialize(&encoded).unwrap(); + + assert_eq!(encoded.len(), 32); + assert_eq!(decoded.as_bytes(), public_key.as_bytes()); +} + +#[test] +#[cfg(feature = "serde")] +fn serde_bincode_public_key_matches_from_bytes() { + use bincode; + + let expected = PublicKey::from(X25519_BASEPOINT_BYTES); + let decoded: PublicKey = bincode::deserialize(&X25519_BASEPOINT_BYTES).unwrap(); + + assert_eq!(decoded.as_bytes(), expected.as_bytes()); +} + +#[test] +#[cfg(feature = "serde")] +fn serde_bincode_static_secret_roundtrip() { + use bincode; + + let static_secret = StaticSecret(clamp_scalar([0x24; 32])); + + let encoded = bincode::serialize(&static_secret).unwrap(); + let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap(); + + assert_eq!(encoded.len(), 32); + assert_eq!(decoded.to_bytes(), static_secret.to_bytes()); +} + +#[test] +#[cfg(feature = "serde")] +fn serde_bincode_static_secret_matches_from_bytes() { + use bincode; + + let expected = StaticSecret(clamp_scalar([0x24; 32])); + let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); + let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); + + assert_eq!(decoded.to_bytes(), expected.to_bytes()); +} + +fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) { + let result = x25519(input_scalar, input_point); + + assert_eq!(result, expected); +} + +#[test] +fn rfc7748_ladder_test1_vectorset1() { + let input_scalar: [u8; 32] = [ + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, + 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, + 0xba, 0x44, 0x9a, 0xc4, + ]; + let input_point: [u8; 32] = [ + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, + 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, + 0xd0, 0xab, 0x1c, 0x4c, + ]; + let expected: [u8; 32] = [ + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, + 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, + 0x77, 0xa2, 0x85, 0x52, + ]; + + do_rfc7748_ladder_test1(input_scalar, input_point, expected); +} + +#[test] +fn rfc7748_ladder_test1_vectorset2() { + let input_scalar: [u8; 32] = [ + 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, + 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, + 0x79, 0x18, 0xba, 0x0d, + ]; + let input_point: [u8; 32] = [ + 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, + 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, + 0xc7, 0x15, 0xa4, 0x93, + ]; + let expected: [u8; 32] = [ + 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, + 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, + 0x7a, 0xac, 0x79, 0x57, + ]; + + do_rfc7748_ladder_test1(input_scalar, input_point, expected); +} + +#[test] +#[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations +fn rfc7748_ladder_test2() { + use curve25519_dalek::constants::X25519_BASEPOINT; + + let mut k: [u8; 32] = X25519_BASEPOINT.0; + let mut u: [u8; 32] = X25519_BASEPOINT.0; + let mut result: [u8; 32]; + + macro_rules! do_iterations { + ($n:expr) => { + for _ in 0..$n { + result = x25519(k, u); + // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE + // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS + // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH + // MY LIBRARY: + // + // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. + // + // ↓↓ DON'T DO THIS ↓↓ + u = k.clone(); + k = result; + } + }; + } + + // After one iteration: + // 422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079 + // After 1,000 iterations: + // 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51 + // After 1,000,000 iterations: + // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 + + do_iterations!(1); + assert_eq!( + k, + [ + 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, + 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, + 0x11, 0xae, 0x30, 0x79, + ] + ); + do_iterations!(999); + assert_eq!( + k, + [ + 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, + 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, + 0x99, 0x53, 0x2c, 0x51, + ] + ); + do_iterations!(999_000); + assert_eq!( + k, + [ + 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, + 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, + 0x4f, 0x66, 0x54, 0x24, + ] + ); +} + From 0a1023f4db0ae2d0944390dc9817bfa946d5e874 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 14 Apr 2021 20:00:41 +0000 Subject: [PATCH 104/160] Implement reused secret keys for Noise protocol. --- src/x25519.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index 538af36..c1c2aa4 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -95,6 +95,46 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { } } +/// A Diffie-Hellman secret key which may be used more than once, but is +/// purposefully not serialiseable in order to discourage key-reuse. This is +/// implemented to facilitate protocols such as Noise (e.g. Noise IK key usage, +/// etc.) and X3DH which require an "ephemeral" key to conduct the +/// Diffie-Hellman operation multiple times throughout the protocol, while the +/// protocol run at a higher level is only conducted once per key. +/// +/// If you're uncertain about whether you should use this, then you likely +/// should not be using this. Our strongly recommended advice is to use +/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that +/// secret keys are never reused, which can have very serious security +/// implications for many protocols. +#[derive(Zeroize)] +#[zeroize(drop)] +pub struct NonSerializeableSecret(pub(crate) Scalar); + +impl NonSerializeableSecret { + /// Perform a Diffie-Hellman key agreement between `self` and + /// `their_public` key to produce a [`SharedSecret`]. + pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { + SharedSecret(&self.0 * their_public.0) + } + + /// Generate a non-serializeable x25519 key. + pub fn new(mut csprng: T) -> Self { + let mut bytes = [0u8; 32]; + + csprng.fill_bytes(&mut bytes); + + NonSerializeableSecret(clamp_scalar(bytes)) + } +} + +impl<'a> From<&'a NonSerializeableSecret> for PublicKey { + /// Given an x25519 [`NonSerializeableSecret`] key, compute its corresponding [`PublicKey`]. + fn from(secret: &'a NonSerializeableSecret) -> PublicKey { + PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + } +} + /// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s. /// /// This type is identical to the [`EphemeralSecret`] type, except that the From 27c73fdb5763a0d0c76548f0b934455b66483326 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 20 Apr 2021 21:33:32 +0000 Subject: [PATCH 105/160] Feature gate reusable secrets and make the name more intuitive. --- Cargo.toml | 3 ++- src/x25519.rs | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16aa97c..41127dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [package.metadata.docs.rs] #rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.0.1/docs/assets/rustdoc-include-katex-header.html"] -features = ["nightly"] +features = ["nightly", "reusable_secrets", "serde"] [dependencies] curve25519-dalek = { version = "3", default-features = false } @@ -53,5 +53,6 @@ default = ["std", "u64_backend"] serde = ["our_serde", "curve25519-dalek/serde"] std = ["curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly"] +reusable_secrets = [] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] diff --git a/src/x25519.rs b/src/x25519.rs index c1c2aa4..030e49b 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -107,11 +107,13 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that /// secret keys are never reused, which can have very serious security /// implications for many protocols. +#[cfg(feature = "reusable_secrets")] #[derive(Zeroize)] #[zeroize(drop)] -pub struct NonSerializeableSecret(pub(crate) Scalar); +pub struct ReusableSecret(pub(crate) Scalar); -impl NonSerializeableSecret { +#[cfg(feature = "reusable_secrets")] +impl ReusableSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { @@ -124,13 +126,14 @@ impl NonSerializeableSecret { csprng.fill_bytes(&mut bytes); - NonSerializeableSecret(clamp_scalar(bytes)) + ReusableSecret(clamp_scalar(bytes)) } } -impl<'a> From<&'a NonSerializeableSecret> for PublicKey { - /// Given an x25519 [`NonSerializeableSecret`] key, compute its corresponding [`PublicKey`]. - fn from(secret: &'a NonSerializeableSecret) -> PublicKey { +#[cfg(feature = "reusable_secrets")] +impl<'a> From<&'a ReusableSecret> for PublicKey { + /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`]. + fn from(secret: &'a ReusableSecret) -> PublicKey { PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) } } From 893e0506a3b9795b933f1df3a63a06aa28b46a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Mon, 3 May 2021 16:31:32 -0700 Subject: [PATCH 106/160] Threads the `fiat_{u32,u64}_backend` features through the feature set This allows the fiat backends introduced in [curve25519-dalek/#342](https://github.com/dalek-cryptography/curve25519-dalek/pull/342) to be used from an x25519 import without cumbersome overrides. --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 78a2004..2173705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # - update CHANGELOG version = "1.1.0" authors = [ - "Isis Lovecruft ", + "Isis Lovecruft ", "DebugSteven ", "Henry de Valence ", ] @@ -55,3 +55,5 @@ std = ["curve25519-dalek/std"] nightly = ["curve25519-dalek/nightly"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] +fiat_u64_backend = ["curve25519-dalek/fiat_u64_backend"] +fiat_u32_backend = ["curve25519-dalek/fiat_u32_backend"] From ee2500db750ab170209e6fb192568e2697b034dd Mon Sep 17 00:00:00 2001 From: exfalso <0slemi0@gmail.com> Date: Tue, 17 Aug 2021 09:49:02 +0200 Subject: [PATCH 107/160] feature(external_doc) -> doc = include_str --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index de5ef19..845538b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,9 +16,8 @@ #![no_std] #![cfg_attr(feature = "bench", feature(test))] -#![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(feature = "nightly", doc(include = "../README.md"))] +#![cfg_attr(feature = "nightly", doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] #![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.1")] From c13e102f9585ab5cb46d7117c56ccfee90ff5c13 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 21:31:54 +0000 Subject: [PATCH 108/160] Implement optional check for contributory behaviour. --- src/x25519.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index 538af36..f39e3f5 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -17,6 +17,7 @@ use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::traits::IsIdentity; use rand_core::CryptoRng; use rand_core::RngCore; @@ -177,6 +178,43 @@ impl SharedSecret { pub fn as_bytes(&self) -> &[u8; 32] { self.0.as_bytes() } + + /// Ensure in constant-time that this shared secret did not result from a + /// key exchange with non-contributory behaviour. + /// + /// In some more exotic protocols which need to guarantee "contributory" + /// behaviour for both parties, that is, that each party contibuted a public + /// value which increased the security of the resulting shared secret. + /// To take an example protocol attack where this could lead to undesireable + /// results [from Thái "thaidn" Dương](https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html): + /// + /// > If Mallory replaces Alice's and Bob's public keys with zero, which is + /// > a valid Curve25519 public key, he would be able to force the ECDH + /// > shared value to be zero, which is the encoding of the point at infinity, + /// > and thus get to dictate some publicly known values as the shared + /// > keys. It still requires an active man-in-the-middle attack to pull the + /// > trick, after which, however, not only Mallory can decode Alice's data, + /// > but everyone too! It is also impossible for Alice and Bob to detect the + /// > intrusion, as they still share the same keys, and can communicate with + /// > each other as normal. + /// + /// The original Curve25519 specification argues that checks for + /// non-contributory behaviour are "unnecessary for Diffie-Hellman". + /// Whether this check is necessary for any particular given protocol is + /// often a matter of debate, which we will not re-hash here, but simply + /// cite some of the [relevant] [public] [discussions]. + /// + /// # Returns + /// + /// Returns `true` if the key exchange was contributory (good), and `false` + /// otherwise (can be bad for some protocols). + /// + /// [relevant]: https://tools.ietf.org/html/rfc7748#page-15 + /// [public]: https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html + /// [discussions]: https://vnhacker.blogspot.com/2016/08/the-internet-of-broken-protocols.html + pub fn was_contributory(&self) -> bool { + !self.0.is_identity() + } } /// "Decode" a scalar from a 32-byte array. From 4f0ad7780529d89569a0111382faefd8557e3723 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 21:40:13 +0000 Subject: [PATCH 109/160] Fix test errors from #70. --- {test => tests}/x25519_tests.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) rename {test => tests}/x25519_tests.rs (96%) diff --git a/test/x25519_tests.rs b/tests/x25519_tests.rs similarity index 96% rename from test/x25519_tests.rs rename to tests/x25519_tests.rs index c7cc6e2..6194612 100644 --- a/test/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,6 +1,17 @@ +use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; +use curve25519_dalek::scalar::Scalar; + use x25519_dalek::*; +fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { + scalar[0] &= 248; + scalar[31] &= 127; + scalar[31] |= 64; + + Scalar::from_bits(scalar) +} + #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; From adbd0e37a414d2c8ce2e6008bf688101347f10ce Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 21:44:15 +0000 Subject: [PATCH 110/160] Pin zeroize to 1.3 for now to support older MSRVs. I reserve the right to change this between minor version changes in x25519-dalek. This closes https://github.com/dalek-cryptography/x25519-dalek/issues/74 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5cf3945..2604de3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ rand_core = { version = "0.5", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "=1.3", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" From 02fc85ea218912a25fe43b258dd3a91ab7d5a03a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 21:54:44 +0000 Subject: [PATCH 111/160] Enable CI via github actions. --- .github/workflows/rust.yml | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..8df1723 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,101 @@ +name: Rust + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ main, develop ] + +env: + CARGO_TERM_COLOR: always + +jobs: + test-u32: + name: Test u32 backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features "std u32_backend" + + test-u64: + name: Test u64 backend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features "std u64_backend" + + nightly: + name: Test nightly compiler + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --features "nightly" + + test-defaults-serde: + name: Test default feature selection and serde + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + args: --features "serde" + + msrv: + name: Current MSRV is 1.54 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.54 + override: true + - uses: actions-rs/cargo@v1 + with: + command: test + + bench: + name: Check that benchmarks compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: bench + # This filter selects no benchmarks, so we don't run any, only build them. + args: "DONTRUNBENCHMARKS" From 84094ba9ea9f32389c535f1e0478e9508c614527 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 22:33:05 +0000 Subject: [PATCH 112/160] Fix serde tests. --- tests/x25519_tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 6194612..9fce935 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -59,8 +59,7 @@ fn serde_bincode_public_key_matches_from_bytes() { fn serde_bincode_static_secret_roundtrip() { use bincode; - let static_secret = StaticSecret(clamp_scalar([0x24; 32])); - + let static_secret = StaticSecret::from([0x24; 32]); let encoded = bincode::serialize(&static_secret).unwrap(); let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap(); @@ -73,7 +72,7 @@ fn serde_bincode_static_secret_roundtrip() { fn serde_bincode_static_secret_matches_from_bytes() { use bincode; - let expected = StaticSecret(clamp_scalar([0x24; 32])); + let expected = StaticSecret::from([0x24; 32]); let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); From a32a92798a4944e032b72a333d333424f0d0cc22 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 22:37:52 +0000 Subject: [PATCH 113/160] Get rid of the include_str!() docs to support earlier MSRVs. --- Cargo.toml | 1 + src/lib.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2604de3..0871841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2018" # - update version in README.md # - update html_root_url # - update CHANGELOG +# - if any changes were made to README.md, mirror them in src/lib.rs docs version = "1.1.1" authors = [ "Isis Lovecruft ", diff --git a/src/lib.rs b/src/lib.rs index 845538b..e5f7bfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,139 @@ #![no_std] #![cfg_attr(feature = "bench", feature(test))] #![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(feature = "nightly", doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] #![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.1")] -//! Note that docs will only build on nightly Rust until -//! `feature(external_doc)` is stabilized. +//! # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) +//! +//! A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, +//! with curve operations provided by +//! [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). +//! +//! This crate provides two levels of API: a bare byte-oriented `x25519` +//! function which matches the function specified in [RFC7748][rfc7748], as +//! well as a higher-level Rust API for static and ephemeral Diffie-Hellman. +//! +//! ## Examples +//! +//! +//! +//! +//! +//! Alice and Bob are two adorable kittens who have lost their mittens, and they +//! wish to be able to send secret messages to each other to coordinate finding +//! them, otherwise—if their caretaker cat finds out—they will surely be called +//! naughty kittens and be given no pie! +//! +//! But the two kittens are quite clever. Even though their paws are still too big +//! and the rest of them is 90% fuzziness, these clever kittens have been studying +//! up on modern public key cryptography and have learned a nifty trick called +//! *elliptic curve Diffie-Hellman key exchange*. With the right incantations, the +//! kittens will be able to secretly organise to find their mittens, and then spend +//! the rest of the afternoon nomming some yummy pie! +//! +//! First, Alice uses `EphemeralSecret::new()` and then +//! `PublicKey::from()` to produce her secret and public keys: +//! +//! ```rust +//! use rand_core::OsRng; +//! use x25519_dalek::{EphemeralSecret, PublicKey}; +//! +//! let alice_secret = EphemeralSecret::new(OsRng); +//! let alice_public = PublicKey::from(&alice_secret); +//! ``` +//! +//! Bob does the same: +//! +//! ```rust +//! # use rand_core::OsRng; +//! # use x25519_dalek::{EphemeralSecret, PublicKey}; +//! let bob_secret = EphemeralSecret::new(OsRng); +//! let bob_public = PublicKey::from(&bob_secret); +//! ``` +//! +//! Alice meows across the room, telling `alice_public` to Bob, and Bob +//! loudly meows `bob_public` back to Alice. Alice now computes her +//! shared secret with Bob by doing: +//! +//! ```rust +//! # use rand_core::OsRng; +//! # use x25519_dalek::{EphemeralSecret, PublicKey}; +//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_public = PublicKey::from(&alice_secret); +//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_public = PublicKey::from(&bob_secret); +//! let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); +//! ``` +//! +//! Similarly, Bob computes a shared secret by doing: +//! +//! ```rust +//! # use rand_core::OsRng; +//! # use x25519_dalek::{EphemeralSecret, PublicKey}; +//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_public = PublicKey::from(&alice_secret); +//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_public = PublicKey::from(&bob_secret); +//! let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); +//! ``` +//! +//! These secrets are the same: +//! +//! ```rust +//! # use rand_core::OsRng; +//! # use x25519_dalek::{EphemeralSecret, PublicKey}; +//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_public = PublicKey::from(&alice_secret); +//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_public = PublicKey::from(&bob_secret); +//! # let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); +//! # let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); +//! assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); +//! ``` +//! +//! Voilà! Alice and Bob can now use their shared secret to encrypt their +//! meows, for example, by using it to generate a key and nonce for an +//! authenticated-encryption cipher. +//! +//! This example used the ephemeral DH API, which ensures that secret keys +//! cannot be reused; Alice and Bob could instead use the static DH API +//! and load a long-term secret key. +//! +//! # Installation +//! +//! To install, add the following to your project's `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! x25519-dalek = "1.1" +//! ``` +//! +//! # Documentation +//! +//! Documentation is available [here](https://docs.rs/x25519-dalek). +//! +//! # Note +//! +//! This code matches the [RFC7748][rfc7748] test vectors. +//! The elliptic curve +//! operations are provided by `curve25519-dalek`, which makes a best-effort +//! attempt to prevent software side-channels. +//! +//! "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) +//! copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) +//! +//! [rfc7748]: https://tools.ietf.org/html/rfc7748 +//! +//! # See also +//! +//! - [crypto_box]: pure Rust public-key authenticated encryption compatible with +//! the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses +//! `x25519-dalek` for key agreement +//! +//! [crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box extern crate curve25519_dalek; From a0a6c57f9e3ac036c68f5ac08ca8e4280ed9ada9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 22:38:34 +0000 Subject: [PATCH 114/160] Bisect to determine MSRV. --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8df1723..2846856 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,14 +71,14 @@ jobs: args: --features "serde" msrv: - name: Current MSRV is 1.54 + name: Current MSRV is 1.41 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.54 + toolchain: 1.41 override: true - uses: actions-rs/cargo@v1 with: From 18323afd63d88f38009310015a04b86b99b1c267 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 13 Sep 2021 22:38:34 +0000 Subject: [PATCH 115/160] Bisect to determine MSRV. --- .github/workflows/rust.yml | 6 +++--- README.md | 4 ++++ src/lib.rs | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8df1723..2ba256c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,18 +71,18 @@ jobs: args: --features "serde" msrv: - name: Current MSRV is 1.54 + name: Current MSRV is 1.41 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.54 + toolchain: 1.41 override: true - uses: actions-rs/cargo@v1 with: - command: test + command: build bench: name: Check that benchmarks compile diff --git a/README.md b/README.md index dee9acc..ce9e7d8 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,10 @@ To install, add the following to your project's `Cargo.toml`: x25519-dalek = "1.1" ``` +# MSRV + +Current MSRV is 1.41 for production builds, and 1.48 for running tests. + # Documentation Documentation is available [here](https://docs.rs/x25519-dalek). diff --git a/src/lib.rs b/src/lib.rs index e5f7bfe..e4990b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,6 +127,10 @@ //! x25519-dalek = "1.1" //! ``` //! +//! # MSRV +//! +//! Current MSRV is 1.41 for production builds, and 1.48 for running tests. +//! //! # Documentation //! //! Documentation is available [here](https://docs.rs/x25519-dalek). From 91babd286ff9faf5625bb8c2f3dbe1a0227ff84b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 19:51:02 +0000 Subject: [PATCH 116/160] Add a #[must_use] to the was_contributory check. --- src/x25519.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/x25519.rs b/src/x25519.rs index f39e3f5..b04c2c0 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -212,6 +212,7 @@ impl SharedSecret { /// [relevant]: https://tools.ietf.org/html/rfc7748#page-15 /// [public]: https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html /// [discussions]: https://vnhacker.blogspot.com/2016/08/the-internet-of-broken-protocols.html + #[must_use] pub fn was_contributory(&self) -> bool { !self.0.is_identity() } From 3924797b599ee159eb2c6bfb3b10f8411caf5e4a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 20:23:38 +0000 Subject: [PATCH 117/160] Add note to StaticSecret that EphemeralSecret is recommended. --- src/x25519.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index 030e49b..5a342e8 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -102,6 +102,8 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Diffie-Hellman operation multiple times throughout the protocol, while the /// protocol run at a higher level is only conducted once per key. /// +/// # Warning +/// /// If you're uncertain about whether you should use this, then you likely /// should not be using this. Our strongly recommended advice is to use /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that @@ -153,6 +155,14 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// ``` /// since the only difference between the two is that [`StaticSecret`] does not enforce at /// compile-time that the key is only used once. +/// +/// # Warning +/// +/// If you're uncertain about whether you should use this, then you likely +/// should not be using this. Our strongly recommended advice is to use +/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that +/// secret keys are never reused, which can have very serious security +/// implications for many protocols. #[cfg_attr(feature = "serde", serde(crate = "our_serde"))] #[cfg_attr( feature = "serde", From 588e48f8f2bf5c3fc66e62aae17ee23f666de12f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 20:24:12 +0000 Subject: [PATCH 118/160] Make ReusableSecret derive Clone. --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index 5a342e8..166f923 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -110,7 +110,7 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// secret keys are never reused, which can have very serious security /// implications for many protocols. #[cfg(feature = "reusable_secrets")] -#[derive(Zeroize)] +#[derive(Clone, Zeroize)] #[zeroize(drop)] pub struct ReusableSecret(pub(crate) Scalar); From edb9ec984ed12c6d037b38545d1927f6df328eda Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 22:33:31 +0000 Subject: [PATCH 119/160] Document that ReusableSecret is preferrable for Noise protocols. --- src/x25519.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 166f923..7478f77 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -102,6 +102,10 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Diffie-Hellman operation multiple times throughout the protocol, while the /// protocol run at a higher level is only conducted once per key. /// +/// Similarly to [`EphemeralSecret`], this type does _not_ have serialisation +/// methods, in order to discourage long-term usage of secret key material. (For +/// long-term secret keys, see [`StaticSecret`].) +/// /// # Warning /// /// If you're uncertain about whether you should use this, then you likely @@ -147,15 +151,6 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// serialization methods to save and load key material. This means that the secret may be used /// multiple times (but does not *have to be*). /// -/// Some protocols, such as Noise, already handle the static/ephemeral distinction, so the -/// additional guarantees provided by [`EphemeralSecret`] are not helpful or would cause duplicate -/// code paths. In this case, it may be useful to -/// ```rust,ignore -/// use x25519_dalek::StaticSecret as SecretKey; -/// ``` -/// since the only difference between the two is that [`StaticSecret`] does not enforce at -/// compile-time that the key is only used once. -/// /// # Warning /// /// If you're uncertain about whether you should use this, then you likely From eef4de41c00f3416345bce3575a5b383c721fd6f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 22:34:41 +0000 Subject: [PATCH 120/160] Disambiguate what kind of key in docstring. --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index 7478f77..1146961 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -126,7 +126,7 @@ impl ReusableSecret { SharedSecret(&self.0 * their_public.0) } - /// Generate a non-serializeable x25519 key. + /// Generate a non-serializeable x25519 [`ReuseableSecret`] key. pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; From 179986ac672c947af12a423250cfade2eab08329 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 23:08:22 +0000 Subject: [PATCH 121/160] Update CHANGELOG for 1.2. --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 588b31f..ef8f8d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ Entries are listed in reverse chronological order. +# 1.x Series + +## 1.2 + +* Add module documentation for using the bytes-oriented `x25519()` API. +* Add implementation of `zeroize::Zeroize` for `PublicKey`. +* Move unittests to a separate directory. +* Add cargo feature flags `"fiat_u32_backend"` and `"fiat_u64_backend"` for + activating the Fiat crypto field element implementations. +* Fix issue with removed `feature(external_doc)` on nightly compilers. +* Pin `zeroize` to version 1.3 to support a wider range of MSRVs. +* Add CI via Github actions. +* Fix breakage in the serde unittests. +* MSRV is now 1.41 for production and 1.48 for development. +* Add an optional check to `SharedSecret` for contibutory behaviour. +* Add implementation of `ReusableSecret` keys which are non-ephemeral, but which + cannot be serialised to discourage long-term use. + ## 1.1.1 * Fix a typo in the README. @@ -23,6 +41,8 @@ Entries are listed in reverse chronological order. * Remove mention of deprecated `rand_os` crate from examples. * Clarify `EphemeralSecret`/`StaticSecret` distinction in documentation. +# Pre-1.0.0 + ## 0.6.0 * Updates `rand_core` version to `0.5`. From ea047a218fd77a46af9cf48e9376954b646a2536 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 14 Sep 2021 23:08:36 +0000 Subject: [PATCH 122/160] Bump x25519-dalek version to 1.2. --- Cargo.toml | 2 +- README.md | 2 +- src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ffc6dc..47c47d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" # - update html_root_url # - update CHANGELOG # - if any changes were made to README.md, mirror them in src/lib.rs docs -version = "1.1.1" +version = "1.2.0" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/README.md b/README.md index ce9e7d8..c7bb1be 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies] -x25519-dalek = "1.1" +x25519-dalek = "1" ``` # MSRV diff --git a/src/lib.rs b/src/lib.rs index e4990b0..ef35c62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(feature = "bench", feature(test))] #![cfg_attr(feature = "nightly", deny(missing_docs))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.1.1")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.2.0")] //! # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) //! @@ -124,7 +124,7 @@ //! //! ```toml //! [dependencies] -//! x25519-dalek = "1.1" +//! x25519-dalek = "1" //! ``` //! //! # MSRV From 2bc576310320e639dc36f5b7dee18450bad02de7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Sep 2021 00:13:09 +0000 Subject: [PATCH 123/160] Update CHANGELOG for 2.0.0-pre.0. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8f8d3..ed70598 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Entries are listed in reverse chronological order. +# 2.x Series + +## 2.0.0-pre.0 + +* Update `rand_core` dependency to `0.6`. + # 1.x Series ## 1.2 From 6be61f178b7de819e01f44eb4d688fd7bbd867de Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Sep 2021 00:16:58 +0000 Subject: [PATCH 124/160] Remove unnecessary rand_core getrandom feature. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4d662c2..55dde68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ features = ["nightly", "reusable_secrets", "serde"] [dependencies] curve25519-dalek = { version = "3", default-features = false } -rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } +rand_core = { version = "0.6", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } From 8224a214898c36ac44cb0874c962ee6581949056 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Sep 2021 00:43:59 +0000 Subject: [PATCH 125/160] Also run Github actions CI on PRs for releases. --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2ba256c..48f3b04 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,7 +4,7 @@ on: push: branches: [ '*' ] pull_request: - branches: [ main, develop ] + branches: [ main, develop, release ] env: CARGO_TERM_COLOR: always From 9d7bccbd8e4c40b7bb30376f18f152cce65083b2 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 15 Sep 2021 01:07:48 +0000 Subject: [PATCH 126/160] Bump x25519-dalek version to 2.0.0-pre.0. --- Cargo.toml | 2 +- README.md | 2 +- src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 55dde68..3a3f1a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" # - update html_root_url # - update CHANGELOG # - if any changes were made to README.md, mirror them in src/lib.rs docs -version = "1.2.0" +version = "2.0.0-pre.0" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/README.md b/README.md index c7bb1be..ca6a43a 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies] -x25519-dalek = "1" +x25519-dalek = "2.0.0-pre.0" ``` # MSRV diff --git a/src/lib.rs b/src/lib.rs index ef35c62..ae20390 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(feature = "bench", feature(test))] #![cfg_attr(feature = "nightly", deny(missing_docs))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.2.0")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/2.0.0-pre.0")] //! # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) //! @@ -124,7 +124,7 @@ //! //! ```toml //! [dependencies] -//! x25519-dalek = "1" +//! x25519-dalek = "2.0.0-pre.0" //! ``` //! //! # MSRV From 8f3e4b1960ae19017fb92e2fdf581b0f0d7efba5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 23 Sep 2021 19:07:44 +0000 Subject: [PATCH 127/160] Bump MSRV to 1.51 for zeroize dependency. --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 48f3b04..1c08037 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,14 +71,14 @@ jobs: args: --features "serde" msrv: - name: Current MSRV is 1.41 + name: Current MSRV is 1.51 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.41 + toolchain: 1.51 override: true - uses: actions-rs/cargo@v1 with: From 841b3a65dec5598e665e62181f64ce1ea1effcba Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 23 Sep 2021 19:08:11 +0000 Subject: [PATCH 128/160] Relax version constraints for zeroize dependency. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3a3f1a4..ad12ff9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ rand_core = { version = "0.6", default-features = false } # `serde` is renamed to `our_serde` in order to avoid a name collision between # importing the serde dependency and enabling the curve25519-dalek/serde feature our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "=1.3", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" From 164563d79e1baf47ce41025cbb9ef478b784d73a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 23 Sep 2021 19:09:28 +0000 Subject: [PATCH 129/160] Update MSRV documentation in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca6a43a..c2c6fb6 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ x25519-dalek = "2.0.0-pre.0" # MSRV -Current MSRV is 1.41 for production builds, and 1.48 for running tests. +Current MSRV is 1.51. # Documentation From 73d4c2fe93fcfb727ae8d9bdf42d15bac93e2b47 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 23 Sep 2021 19:53:44 +0000 Subject: [PATCH 130/160] Update x25519-dalek version to 2.0.0-pre.1. --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad12ff9..126d7be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" # - update html_root_url # - update CHANGELOG # - if any changes were made to README.md, mirror them in src/lib.rs docs -version = "2.0.0-pre.0" +version = "2.0.0-pre.1" authors = [ "Isis Lovecruft ", "DebugSteven ", diff --git a/src/lib.rs b/src/lib.rs index ae20390..0c6485a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(feature = "bench", feature(test))] #![cfg_attr(feature = "nightly", deny(missing_docs))] #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/2.0.0-pre.0")] +#![doc(html_root_url = "https://docs.rs/x25519-dalek/2.0.0-pre.1")] //! # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) //! From ab38e36a4ca5dfe96bc5ee4d71308f634047771d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 23 Sep 2021 19:53:53 +0000 Subject: [PATCH 131/160] Update CHANGELOG. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed70598..eabe803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Entries are listed in reverse chronological order. # 2.x Series +## 2.0.0-pre.1 + +* Loosen restriction on zeroize dependency version from =1.3 to 1. +* Update MSRV to 1.51. + ## 2.0.0-pre.0 * Update `rand_core` dependency to `0.6`. From cf56e789ed28e2c1fb5263d30c78a385c83f3c7c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 9 Dec 2022 15:35:49 +0100 Subject: [PATCH 132/160] bump MSRV to 1.60 and update to current curve25519-dalek --- CHANGELOG.md | 2 ++ Cargo.toml | 26 ++++++++++++-------------- README.md | 2 +- src/lib.rs | 2 +- src/x25519.rs | 18 +++--------------- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eabe803..c3d6317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Entries are listed in reverse chronological order. # 2.x Series +* Update MSRV to 1.60. + ## 2.0.0-pre.1 * Loosen restriction on zeroize dependency version from =1.3 to 1. diff --git a/Cargo.toml b/Cargo.toml index 126d7be..d7b6845 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x25519-dalek" -edition = "2018" +edition = "2021" # Before changing this: # - update version in README.md # - update html_root_url @@ -25,6 +25,7 @@ exclude = [ ".travis.yml", "CONTRIBUTING.md", ] +rust-version = "1.60" [badges] travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} @@ -34,11 +35,9 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly", "reusable_secrets", "serde"] [dependencies] -curve25519-dalek = { version = "3", default-features = false } -rand_core = { version = "0.6", default-features = false } -# `serde` is renamed to `our_serde` in order to avoid a name collision between -# importing the serde dependency and enabling the curve25519-dalek/serde feature -our_serde = { package = "serde", version = "1", default-features = false, optional = true, features = ["derive"] } +curve25519-dalek = { version = "4.0.0-pre.2", default-features = false } +rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } +serde = { version = "1", default-features = false, optional = true, features = ["derive"] } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] @@ -50,12 +49,11 @@ name = "x25519" harness = false [features] -default = ["std", "u64_backend"] -serde = ["our_serde", "curve25519-dalek/serde"] -std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly"] +default = ["alloc"] +serde = ["dep:serde", "curve25519-dalek/serde"] +alloc = ["curve25519-dalek/alloc", "serde?/alloc"] reusable_secrets = [] -u64_backend = ["curve25519-dalek/u64_backend"] -u32_backend = ["curve25519-dalek/u32_backend"] -fiat_u64_backend = ["curve25519-dalek/fiat_u64_backend"] -fiat_u32_backend = ["curve25519-dalek/fiat_u32_backend"] + + +[patch.crates-io] +curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", branch = "release/4.0" } \ No newline at end of file diff --git a/README.md b/README.md index c2c6fb6..7145f06 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ x25519-dalek = "2.0.0-pre.0" # MSRV -Current MSRV is 1.51. +Current MSRV is 1.60. # Documentation diff --git a/src/lib.rs b/src/lib.rs index 0c6485a..f87b7b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,7 @@ //! //! # MSRV //! -//! Current MSRV is 1.41 for production builds, and 1.48 for running tests. +//! Current MSRV is 1.60. //! //! # Documentation //! diff --git a/src/x25519.rs b/src/x25519.rs index ed4fe9d..1007777 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -31,11 +31,7 @@ use zeroize::Zeroize; /// should they wish to erase public keys from memory. Note that this erasure /// (in this crate) does *not* automatically happen, but either must be derived /// for Drop or explicitly called. -#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Zeroize)] pub struct PublicKey(pub(crate) MontgomeryPoint); @@ -159,11 +155,7 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that /// secret keys are never reused, which can have very serious security /// implications for many protocols. -#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Zeroize)] #[zeroize(drop)] pub struct StaticSecret( @@ -328,11 +320,7 @@ pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ /// Derived serialization methods will not work on a StaticSecret because x25519 requires /// non-canonical scalars which are rejected by curve25519-dalek. Thus we provide a way to convert /// the bytes directly to a scalar using Serde's remote derive functionality. -#[cfg_attr(feature = "serde", serde(crate = "our_serde"))] -#[cfg_attr( - feature = "serde", - derive(our_serde::Serialize, our_serde::Deserialize) -)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(remote = "Scalar"))] struct AllowUnreducedScalarBytes( #[cfg_attr(feature = "serde", serde(getter = "Scalar::to_bytes"))] [u8; 32], From 50dc19b3891afdcbd8af0af954e73d3078018a31 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 9 Dec 2022 15:35:56 +0100 Subject: [PATCH 133/160] cargo fmt --- tests/x25519_tests.rs | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 9fce935..181eedb 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,4 +1,3 @@ - use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::scalar::Scalar; @@ -88,19 +87,19 @@ fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expect #[test] fn rfc7748_ladder_test1_vectorset1() { let input_scalar: [u8; 32] = [ - 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, - 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, - 0xba, 0x44, 0x9a, 0xc4, + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, + 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, + 0x9a, 0xc4, ]; let input_point: [u8; 32] = [ - 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, - 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, - 0xd0, 0xab, 0x1c, 0x4c, + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, + 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, + 0x1c, 0x4c, ]; let expected: [u8; 32] = [ - 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, - 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, - 0x77, 0xa2, 0x85, 0x52, + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, + 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, + 0x85, 0x52, ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); @@ -109,19 +108,19 @@ fn rfc7748_ladder_test1_vectorset1() { #[test] fn rfc7748_ladder_test1_vectorset2() { let input_scalar: [u8; 32] = [ - 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, - 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, - 0x79, 0x18, 0xba, 0x0d, + 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, + 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, + 0xba, 0x0d, ]; let input_point: [u8; 32] = [ - 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, - 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, - 0xc7, 0x15, 0xa4, 0x93, + 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, + 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, + 0xa4, 0x93, ]; let expected: [u8; 32] = [ - 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, - 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, - 0x7a, 0xac, 0x79, 0x57, + 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, + 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, + 0x79, 0x57, ]; do_rfc7748_ladder_test1(input_scalar, input_point, expected); @@ -189,4 +188,3 @@ fn rfc7748_ladder_test2() { ] ); } - From fa9e16e9384e07634119adfbe833b88eb60aac68 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 9 Dec 2022 15:36:55 +0100 Subject: [PATCH 134/160] happy clippy --- benches/x25519.rs | 4 ++-- src/x25519.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index e5d77d2..fee3fa9 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -25,12 +25,12 @@ use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { - let bob_secret = EphemeralSecret::new(&mut OsRng); + let bob_secret = EphemeralSecret::new(OsRng); let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( - || EphemeralSecret::new(&mut OsRng), + || EphemeralSecret::new(OsRng), |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); diff --git a/src/x25519.rs b/src/x25519.rs index 1007777..6edd2c0 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -120,7 +120,7 @@ impl ReusableSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(&self.0 * their_public.0) + SharedSecret(self.0 * their_public.0) } /// Generate a non-serializeable x25519 [`ReuseableSecret`] key. @@ -166,7 +166,7 @@ impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(&self.0 * their_public.0) + SharedSecret(self.0 * their_public.0) } /// Generate an x25519 key. From ce3b8d36895d1d5d38b8d2df6bef15f9007d424d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 9 Dec 2022 15:40:56 +0100 Subject: [PATCH 135/160] fixup: cleanup extern crates --- benches/x25519.rs | 8 +------- src/lib.rs | 6 ------ src/x25519.rs | 2 -- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index fee3fa9..dfcee4a 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -11,13 +11,7 @@ //! Benchmark the Diffie-Hellman operation. -#[macro_use] -extern crate criterion; -extern crate curve25519_dalek; -extern crate rand_core; -extern crate x25519_dalek; - -use criterion::Criterion; +use criterion::{criterion_group, criterion_main, Criterion}; use rand_core::OsRng; diff --git a/src/lib.rs b/src/lib.rs index f87b7b8..01369d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,12 +155,6 @@ //! //! [crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box -extern crate curve25519_dalek; - -extern crate rand_core; - -extern crate zeroize; - mod x25519; pub use crate::x25519::*; diff --git a/src/x25519.rs b/src/x25519.rs index 6edd2c0..d63405e 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -281,8 +281,6 @@ fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { /// /// # Example /// ``` -/// # extern crate rand_core; -/// # /// use rand_core::OsRng; /// use rand_core::RngCore; /// From 50dbb9eb4d7fbc62fc43a516b88daea09096f015 Mon Sep 17 00:00:00 2001 From: dlblv Date: Mon, 19 Dec 2022 21:30:09 +0500 Subject: [PATCH 136/160] Add as_bytes() method --- src/x25519.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index ed4fe9d..4d4b0d2 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -190,6 +190,11 @@ impl StaticSecret { pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } + + /// View this key as a byte array. + pub fn as_bytes(&self) -> &[u8; 32] { + self.0.as_bytes() + } } impl From<[u8; 32]> for StaticSecret { From 7374fd3d2f151d21351ee6076606e8a6b7abcc88 Mon Sep 17 00:00:00 2001 From: dlblv Date: Wed, 21 Dec 2022 01:11:17 +0500 Subject: [PATCH 137/160] fix clippy warnings --- benches/x25519.rs | 4 ++-- src/x25519.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benches/x25519.rs b/benches/x25519.rs index e5d77d2..fee3fa9 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -25,12 +25,12 @@ use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { - let bob_secret = EphemeralSecret::new(&mut OsRng); + let bob_secret = EphemeralSecret::new(OsRng); let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( - || EphemeralSecret::new(&mut OsRng), + || EphemeralSecret::new(OsRng), |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); diff --git a/src/x25519.rs b/src/x25519.rs index 4d4b0d2..e13e81e 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -174,7 +174,7 @@ impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(&self.0 * their_public.0) + SharedSecret(self.0 * their_public.0) } /// Generate an x25519 key. From d95e3bd536ac9abe7ce6e050793428eb6a3ad1fd Mon Sep 17 00:00:00 2001 From: dlblv Date: Fri, 30 Dec 2022 01:43:42 +0500 Subject: [PATCH 138/160] impl AsRef for keys as well --- src/x25519.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/x25519.rs b/src/x25519.rs index e13e81e..01b22bb 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -60,6 +60,14 @@ impl PublicKey { } } +impl AsRef<[u8]> for PublicKey { + /// View this public key as a byte array. + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + /// A short-lived Diffie-Hellman secret key that can only be used to compute a single /// [`SharedSecret`]. /// @@ -187,11 +195,13 @@ impl StaticSecret { } /// Extract this key's bytes for serialization. + #[inline] pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } /// View this key as a byte array. + #[inline] pub fn as_bytes(&self) -> &[u8; 32] { self.0.as_bytes() } @@ -211,6 +221,14 @@ impl<'a> From<&'a StaticSecret> for PublicKey { } } +impl AsRef<[u8]> for StaticSecret { + /// View this key as a byte array. + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + /// The result of a Diffie-Hellman key exchange. /// /// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their @@ -271,6 +289,14 @@ impl SharedSecret { } } +impl AsRef<[u8]> for SharedSecret { + /// View this shared secret key as a byte array. + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + /// "Decode" a scalar from a 32-byte array. /// /// By "decode" here, what is really meant is applying key clamping by twiddling From 88cc32b68779860a40cb4d57c99a55e6ac0b8b16 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 29 Jan 2023 04:29:07 -0500 Subject: [PATCH 139/160] Updated to curve25519-dalek rc0 --- Cargo.toml | 14 +++++----- src/x25519.rs | 63 +++++++++++++++++-------------------------- tests/x25519_tests.rs | 20 +++++--------- 3 files changed, 37 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7b6845..5441ff1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} features = ["nightly", "reusable_secrets", "serde"] [dependencies] -curve25519-dalek = { version = "4.0.0-pre.2", default-features = false } +curve25519-dalek = { version = "4.0.0-rc.0", default-features = false } rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } serde = { version = "1", default-features = false, optional = true, features = ["derive"] } -zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } +zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" @@ -49,11 +49,9 @@ name = "x25519" harness = false [features] -default = ["alloc"] +default = ["alloc", "precomputed-tables", "zeroize"] +zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] serde = ["dep:serde", "curve25519-dalek/serde"] -alloc = ["curve25519-dalek/alloc", "serde?/alloc"] +alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] +precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] - - -[patch.crates-io] -curve25519-dalek = { git = "https://github.com/dalek-cryptography/curve25519-dalek", branch = "release/4.0" } \ No newline at end of file diff --git a/src/x25519.rs b/src/x25519.rs index d63405e..442f17d 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,14 +14,14 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; -use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::scalar::Scalar; -use curve25519_dalek::traits::IsIdentity; +use curve25519_dalek::{ + edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar, traits::IsIdentity, +}; use rand_core::CryptoRng; use rand_core::RngCore; +#[cfg(feature = "zeroize")] use zeroize::Zeroize; /// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or @@ -32,7 +32,8 @@ use zeroize::Zeroize; /// (in this crate) does *not* automatically happen, but either must be derived /// for Drop or explicitly called. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Zeroize)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct PublicKey(pub(crate) MontgomeryPoint); impl From<[u8; 32]> for PublicKey { @@ -64,8 +65,8 @@ impl PublicKey { /// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be /// generated from fresh randomness by [`EphemeralSecret::new`] and the compiler statically checks /// that the resulting secret is used at most once. -#[derive(Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] pub struct EphemeralSecret(pub(crate) Scalar); impl EphemeralSecret { @@ -81,14 +82,14 @@ impl EphemeralSecret { csprng.fill_bytes(&mut bytes); - EphemeralSecret(clamp_scalar(bytes)) + EphemeralSecret(Scalar::from_bits_clamped(bytes)) } } impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a EphemeralSecret) -> PublicKey { - PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -111,8 +112,9 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// secret keys are never reused, which can have very serious security /// implications for many protocols. #[cfg(feature = "reusable_secrets")] -#[derive(Clone, Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] +#[derive(Clone)] pub struct ReusableSecret(pub(crate) Scalar); #[cfg(feature = "reusable_secrets")] @@ -129,7 +131,7 @@ impl ReusableSecret { csprng.fill_bytes(&mut bytes); - ReusableSecret(clamp_scalar(bytes)) + ReusableSecret(Scalar::from_bits_clamped(bytes)) } } @@ -137,7 +139,7 @@ impl ReusableSecret { impl<'a> From<&'a ReusableSecret> for PublicKey { /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a ReusableSecret) -> PublicKey { - PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -156,8 +158,9 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// secret keys are never reused, which can have very serious security /// implications for many protocols. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] +#[derive(Clone)] pub struct StaticSecret( #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, ); @@ -175,7 +178,7 @@ impl StaticSecret { csprng.fill_bytes(&mut bytes); - StaticSecret(clamp_scalar(bytes)) + StaticSecret(Scalar::from_bits_clamped(bytes)) } /// Extract this key's bytes for serialization. @@ -187,14 +190,14 @@ impl StaticSecret { impl From<[u8; 32]> for StaticSecret { /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { - StaticSecret(clamp_scalar(bytes)) + StaticSecret(Scalar::from_bits_clamped(bytes)) } } impl<'a> From<&'a StaticSecret> for PublicKey { /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { - PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) } } @@ -202,8 +205,8 @@ impl<'a> From<&'a StaticSecret> for PublicKey { /// /// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their /// counterparty's [`PublicKey`]. -#[derive(Zeroize)] -#[zeroize(drop)] +#[cfg_attr(feature = "zeroize", derive(Zeroize))] +#[cfg_attr(feature = "zeroize", zeroize(drop))] pub struct SharedSecret(pub(crate) MontgomeryPoint); impl SharedSecret { @@ -258,22 +261,6 @@ impl SharedSecret { } } -/// "Decode" a scalar from a 32-byte array. -/// -/// By "decode" here, what is really meant is applying key clamping by twiddling -/// some bits. -/// -/// # Returns -/// -/// A `Scalar`. -fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { - scalar[0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; - - Scalar::from_bits(scalar) -} - /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. /// /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who @@ -305,7 +292,7 @@ fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { /// assert_eq!(alice_shared, bob_shared); /// ``` pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { - (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() + (Scalar::from_bits_clamped(k) * MontgomeryPoint(u)).to_bytes() } /// The X25519 basepoint, for use with the bare, byte-oriented x25519 @@ -325,6 +312,6 @@ struct AllowUnreducedScalarBytes( ); impl From for Scalar { fn from(bytes: AllowUnreducedScalarBytes) -> Scalar { - clamp_scalar(bytes.0) + Scalar::from_bits_clamped(bytes.0) } } diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 181eedb..21eeb43 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,16 +1,7 @@ -use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; -use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; use x25519_dalek::*; -fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar { - scalar[0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; - - Scalar::from_bits(scalar) -} - #[test] fn byte_basepoint_matches_edwards_scalar_mul() { let mut scalar_bytes = [0x37; 32]; @@ -20,9 +11,10 @@ fn byte_basepoint_matches_edwards_scalar_mul() { let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); - let expected = (&ED25519_BASEPOINT_TABLE * &clamp_scalar(scalar_bytes)) - .to_montgomery() - .to_bytes(); + let expected = { + let scalar = Scalar::from_bits_clamped(scalar_bytes); + EdwardsPoint::mul_base(&scalar).to_montgomery().to_bytes() + }; assert_eq!(result, expected); } @@ -72,7 +64,7 @@ fn serde_bincode_static_secret_matches_from_bytes() { use bincode; let expected = StaticSecret::from([0x24; 32]); - let clamped_bytes = clamp_scalar([0x24; 32]).to_bytes(); + let clamped_bytes = Scalar::from_bits_clamped([0x24; 32]).to_bytes(); let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); assert_eq!(decoded.to_bytes(), expected.to_bytes()); From 83300618ad420981423ecaccc1d091d859993b01 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Sun, 29 Jan 2023 04:29:44 -0500 Subject: [PATCH 140/160] Attempt to fix CI --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1c08037..199314c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ '*' ] + branches: [ '**' ] pull_request: - branches: [ main, develop, release ] + branches: [ '**' ] env: CARGO_TERM_COLOR: always From 327618c7d8031c5899befb2878f6372dd52ee465 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Fri, 24 Feb 2023 22:22:19 +0100 Subject: [PATCH 141/160] Fix two typos --- src/x25519.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x25519.rs b/src/x25519.rs index 01b22bb..9fac6f3 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -254,9 +254,9 @@ impl SharedSecret { /// key exchange with non-contributory behaviour. /// /// In some more exotic protocols which need to guarantee "contributory" - /// behaviour for both parties, that is, that each party contibuted a public + /// behaviour for both parties, that is, that each party contributed a public /// value which increased the security of the resulting shared secret. - /// To take an example protocol attack where this could lead to undesireable + /// To take an example protocol attack where this could lead to undesirable /// results [from Thái "thaidn" Dương](https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html): /// /// > If Mallory replaces Alice's and Bob's public keys with zero, which is From 62149c4f80e0b5b4e1d91d796b44f1a28d1d0add Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:16:01 +1100 Subject: [PATCH 142/160] Fix CI --- .github/workflows/rust.yml | 185 ++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 75 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 199314c..e91269d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,94 +8,129 @@ on: env: CARGO_TERM_COLOR: always + RUSTFLAGS: '-D warnings' + RUSTDOCFLAGS: '-D warnings' jobs: - test-u32: - name: Test u32 backend + test: runs-on: ubuntu-latest + strategy: + matrix: + include: + # 32-bit target + - target: i686-unknown-linux-gnu + deps: sudo apt update && sudo apt install gcc-multilib + # 64-bit target + - target: x86_64-unknown-linux-gnu steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std u32_backend" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: rustup target add ${{ matrix.target }} + - run: ${{ matrix.deps }} + - run: cargo test --target ${{ matrix.target }} --no-default-features --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast --lib + - run: cargo test --target ${{ matrix.target }} --no-default-features --features rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast,rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,fast,rand_core --lib --tests + - run: cargo test --target ${{ matrix.target }} + - run: cargo test --target ${{ matrix.target }} --features batch + - run: cargo test --target ${{ matrix.target }} --features digest,rand_core + - run: cargo test --target ${{ matrix.target }} --features serde + - run: cargo test --target ${{ matrix.target }} --features pem + - run: cargo test --target ${{ matrix.target }} --all-features - test-u64: - name: Test u64 backend + build-simd: + name: Test simd backend (nightly) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --no-default-features --features "std u64_backend" - - nightly: - name: Test nightly compiler - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --features "nightly" - - test-defaults-serde: - name: Test default feature selection and serde - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --features "serde" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + - env: + RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2' + run: cargo build --target x86_64-unknown-linux-gnu + - env: + RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma' + run: cargo build --target x86_64-unknown-linux-gnu msrv: - name: Current MSRV is 1.51 + name: Current MSRV is 1.60.0 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.51 - override: true - - uses: actions-rs/cargo@v1 - with: - command: build + - uses: actions/checkout@v3 + # First delete the checked-in `Cargo.lock`. We're going to regenerate it + - run: rm Cargo.lock + # Now run `cargo +nightly -Z minimal-verisons check` in order to get a + # Cargo.lock with the oldest possible deps + - uses: dtolnay/rust-toolchain@nightly + - run: cargo -Z minimal-versions check --no-default-features --features serde + # Now check that `cargo build` works with respect to the oldest possible + # deps and the stated MSRV + - uses: dtolnay/rust-toolchain@1.60.0 + - run: cargo build + + # Test no_std integration with no features + build-nostd-base: + name: Build on no_std target (thumbv7em-none-eabi) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: thumbv7em-none-eabi + - uses: taiki-e/install-action@cargo-hack + # No default features build + - run: cargo build --target thumbv7em-none-eabi --release --no-default-features + + # Test no_std integration with all no_std features + build-nostd-features: + name: Build on no_std target (thumbv7em-none-eabi) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: thumbv7em-none-eabi + - uses: taiki-e/install-action@cargo-hack + # No default features build + - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std bench: name: Check that benchmarks compile runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: bench - # This filter selects no benchmarks, so we don't run any, only build them. - args: "DONTRUNBENCHMARKS" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: cargo build --benches --features batch + + rustfmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all -- --check + + clippy: + name: Check that clippy is happy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.65 + with: + components: clippy + - run: cargo clippy + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - run: cargo doc --all-features From 5cb2a4953ae1656d68aec981337c615a56d4d283 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:19:39 +1100 Subject: [PATCH 143/160] Test all features and no_std iterate --- .github/workflows/rust.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e91269d..2f16e57 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,6 +13,7 @@ env: jobs: test: + name: Test all features runs-on: ubuntu-latest strategy: matrix: @@ -27,18 +28,6 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: rustup target add ${{ matrix.target }} - run: ${{ matrix.deps }} - - run: cargo test --target ${{ matrix.target }} --no-default-features --lib --tests - - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast --lib - - run: cargo test --target ${{ matrix.target }} --no-default-features --features rand_core --lib --tests - - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,rand_core --lib --tests - - run: cargo test --target ${{ matrix.target }} --no-default-features --features fast,rand_core --lib --tests - - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,fast,rand_core --lib --tests - - run: cargo test --target ${{ matrix.target }} - - run: cargo test --target ${{ matrix.target }} --features batch - - run: cargo test --target ${{ matrix.target }} --features digest,rand_core - - run: cargo test --target ${{ matrix.target }} --features serde - - run: cargo test --target ${{ matrix.target }} --features pem - run: cargo test --target ${{ matrix.target }} --all-features build-simd: From 8c9c94add944b2bc8ca28d109934faaacc648695 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:22:52 +1100 Subject: [PATCH 144/160] Add Cargo.lock --- .gitignore | 1 - Cargo.lock | 704 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 704 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 2328c9d..da57a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ target/ **/*.rs.bk -Cargo.lock .cargo diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bfcb715 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,704 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "csv" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "fiat-crypto" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +dependencies = [ + "bincode", + "criterion", + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] From be8a657ece2eb9e6bc94c63305dbf0c1ab55fd75 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:27:44 +1100 Subject: [PATCH 145/160] Add .lock --- .gitignore | 1 - Cargo.lock | 704 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 704 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 2328c9d..da57a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ target/ **/*.rs.bk -Cargo.lock .cargo diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bfcb715 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,704 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "csv" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "fiat-crypto" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +dependencies = [ + "bincode", + "criterion", + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] From c779e215f78a154822aa3279989a11c27cc8725f Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:42:31 +1100 Subject: [PATCH 146/160] Fixed doc nit from update-ci branch Co-Authored-by: Michael Rosenberg --- src/x25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x25519.rs b/src/x25519.rs index 442f17d..e95d528 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -125,7 +125,7 @@ impl ReusableSecret { SharedSecret(self.0 * their_public.0) } - /// Generate a non-serializeable x25519 [`ReuseableSecret`] key. + /// Generate a non-serializeable x25519 [`ReusableSecret`] key. pub fn new(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; From 707675d87806c058187b5db8c954eccbc75b5405 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:43:48 +1100 Subject: [PATCH 147/160] Disable no_std from CI --- .github/workflows/rust.yml | 56 ++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2f16e57..9dc1b8c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -59,33 +59,35 @@ jobs: - uses: dtolnay/rust-toolchain@1.60.0 - run: cargo build - # Test no_std integration with no features - build-nostd-base: - name: Build on no_std target (thumbv7em-none-eabi) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - targets: thumbv7em-none-eabi - - uses: taiki-e/install-action@cargo-hack - # No default features build - - run: cargo build --target thumbv7em-none-eabi --release --no-default-features - - # Test no_std integration with all no_std features - build-nostd-features: - name: Build on no_std target (thumbv7em-none-eabi) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - targets: thumbv7em-none-eabi - - uses: taiki-e/install-action@cargo-hack - # No default features build - - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std +# no_std support is pending feature, tracking: +# https://github.com/dalek-cryptography/x25519-dalek/issues/111 +# # Test no_std integration with no features +# build-nostd-base: +# name: Build on no_std target (thumbv7em-none-eabi) +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - uses: dtolnay/rust-toolchain@master +# with: +# toolchain: stable +# targets: thumbv7em-none-eabi +# - uses: taiki-e/install-action@cargo-hack +# # No default features build +# - run: cargo build --target thumbv7em-none-eabi --release --no-default-features +# +# # Test no_std integration with all no_std features +# build-nostd-features: +# name: Build on no_std target (thumbv7em-none-eabi) +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - uses: dtolnay/rust-toolchain@master +# with: +# toolchain: stable +# targets: thumbv7em-none-eabi +# - uses: taiki-e/install-action@cargo-hack +# # No default features build +# - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std bench: name: Check that benchmarks compile From cbd39587727c1967bc301a69d85525b954b4374c Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:50:50 +1100 Subject: [PATCH 148/160] Fix bench features --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9dc1b8c..1d70172 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -95,7 +95,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - - run: cargo build --benches --features batch + - run: cargo build --benches rustfmt: name: Check formatting From fdc992d6cd2156d976626824cf1dce35af7f246b Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:18:07 +1100 Subject: [PATCH 149/160] Fix no_std with get_random --- Cargo.toml | 4 +++- src/lib.rs | 15 ++++++++++----- src/x25519.rs | 3 ++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5441ff1..104d3e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ features = ["nightly", "reusable_secrets", "serde"] [dependencies] curve25519-dalek = { version = "4.0.0-rc.0", default-features = false } -rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } +rand_core = { version = "0.6", default-features = false } serde = { version = "1", default-features = false, optional = true, features = ["derive"] } zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } @@ -55,3 +55,5 @@ serde = ["dep:serde", "curve25519-dalek/serde"] alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] +# docs: docsrs and doctest features +docsrs = ["rand_core/getrandom"] diff --git a/src/lib.rs b/src/lib.rs index 01369d0..b4b1541 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,8 @@ //! First, Alice uses `EphemeralSecret::new()` and then //! `PublicKey::from()` to produce her secret and public keys: //! -//! ```rust +#![cfg_attr(feature = "docsrs", doc = "```")] +#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] //! use rand_core::OsRng; //! use x25519_dalek::{EphemeralSecret, PublicKey}; //! @@ -63,7 +64,8 @@ //! //! Bob does the same: //! -//! ```rust +#![cfg_attr(feature = "docsrs", doc = "```")] +#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! let bob_secret = EphemeralSecret::new(OsRng); @@ -74,7 +76,8 @@ //! loudly meows `bob_public` back to Alice. Alice now computes her //! shared secret with Bob by doing: //! -//! ```rust +#![cfg_attr(feature = "docsrs", doc = "```")] +#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); @@ -86,7 +89,8 @@ //! //! Similarly, Bob computes a shared secret by doing: //! -//! ```rust +#![cfg_attr(feature = "docsrs", doc = "```")] +#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); @@ -98,7 +102,8 @@ //! //! These secrets are the same: //! -//! ```rust +#![cfg_attr(feature = "docsrs", doc = "```")] +#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); diff --git a/src/x25519.rs b/src/x25519.rs index 442f17d..29c9c1a 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -267,7 +267,8 @@ impl SharedSecret { /// cannot use the better, safer, and faster ephemeral DH API. /// /// # Example -/// ``` +#[cfg_attr(feature = "docsrs", doc = "```")] +#[cfg_attr(not(feature = "docsrs"), doc = "```ignore")] /// use rand_core::OsRng; /// use rand_core::RngCore; /// From f3c46bc3bf8d0cc77542556de79fdd3329508ccf Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 15:27:53 +1100 Subject: [PATCH 150/160] Remove redundant feature --- Cargo.toml | 3 +-- src/lib.rs | 15 +++++---------- src/x25519.rs | 3 +-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 104d3e4..167139f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ zeroize = { version = "1", default-features = false, optional = true, features = [dev-dependencies] bincode = "1" criterion = "0.3.0" +rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } [[bench]] name = "x25519" @@ -55,5 +56,3 @@ serde = ["dep:serde", "curve25519-dalek/serde"] alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] -# docs: docsrs and doctest features -docsrs = ["rand_core/getrandom"] diff --git a/src/lib.rs b/src/lib.rs index b4b1541..01369d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,8 +53,7 @@ //! First, Alice uses `EphemeralSecret::new()` and then //! `PublicKey::from()` to produce her secret and public keys: //! -#![cfg_attr(feature = "docsrs", doc = "```")] -#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +//! ```rust //! use rand_core::OsRng; //! use x25519_dalek::{EphemeralSecret, PublicKey}; //! @@ -64,8 +63,7 @@ //! //! Bob does the same: //! -#![cfg_attr(feature = "docsrs", doc = "```")] -#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +//! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! let bob_secret = EphemeralSecret::new(OsRng); @@ -76,8 +74,7 @@ //! loudly meows `bob_public` back to Alice. Alice now computes her //! shared secret with Bob by doing: //! -#![cfg_attr(feature = "docsrs", doc = "```")] -#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +//! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); @@ -89,8 +86,7 @@ //! //! Similarly, Bob computes a shared secret by doing: //! -#![cfg_attr(feature = "docsrs", doc = "```")] -#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +//! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); @@ -102,8 +98,7 @@ //! //! These secrets are the same: //! -#![cfg_attr(feature = "docsrs", doc = "```")] -#![cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +//! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; //! # let alice_secret = EphemeralSecret::new(OsRng); diff --git a/src/x25519.rs b/src/x25519.rs index 29c9c1a..5ee00f6 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -267,8 +267,7 @@ impl SharedSecret { /// cannot use the better, safer, and faster ephemeral DH API. /// /// # Example -#[cfg_attr(feature = "docsrs", doc = "```")] -#[cfg_attr(not(feature = "docsrs"), doc = "```ignore")] +/// ```rust /// use rand_core::OsRng; /// use rand_core::RngCore; /// From 281fab6af021146335c4ed041be7b392ef1c555c Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:45:37 +1100 Subject: [PATCH 151/160] Fix docsrs --- Cargo.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5441ff1..1916b46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "x25519-dalek" +name = "test-x25519-dalek" edition = "2021" # Before changing this: # - update version in README.md @@ -31,8 +31,11 @@ rust-version = "1.60" travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} [package.metadata.docs.rs] -#rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.0.1/docs/assets/rustdoc-include-katex-header.html"] -features = ["nightly", "reusable_secrets", "serde"] +rustdoc-args = [ + "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", + "--cfg", "docsrs", +] +features = ["reusable_secrets", "serde"] [dependencies] curve25519-dalek = { version = "4.0.0-rc.0", default-features = false } From a7769a645c3f90da1e2011df5e47ea35b80944fb Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:48:15 +1100 Subject: [PATCH 152/160] Add assets --- docs/assets/rustdoc-include-katex-header.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/assets/rustdoc-include-katex-header.html diff --git a/docs/assets/rustdoc-include-katex-header.html b/docs/assets/rustdoc-include-katex-header.html new file mode 100644 index 0000000..d240432 --- /dev/null +++ b/docs/assets/rustdoc-include-katex-header.html @@ -0,0 +1,12 @@ + + + + + + + + + From 4b6c61c11177927a5c7dd92e58ad1485dcc8b5b7 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Sun, 12 Mar 2023 16:49:18 +1100 Subject: [PATCH 153/160] Correct crate name --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1916b46..cce5567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test-x25519-dalek" +name = "x25519-dalek" edition = "2021" # Before changing this: # - update version in README.md From 0139af7f9dc44b94014547c8fc3cbd5598dac9e1 Mon Sep 17 00:00:00 2001 From: pinkforest <36498018+pinkforest@users.noreply.github.com> Date: Mon, 13 Mar 2023 05:15:34 +1100 Subject: [PATCH 154/160] Remove rustup in favor of rust-toolchain --- .github/workflows/rust.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1d70172..d5c47c5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,7 +26,8 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - - run: rustup target add ${{ matrix.target }} + with: + target: ${{ matrix.target }} - run: ${{ matrix.deps }} - run: cargo test --target ${{ matrix.target }} --all-features From 02a5ce20ca900335c03f04b2a3a59eb84bebccc2 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:40:51 +1100 Subject: [PATCH 155/160] Add `getrandom` (#118) * Add getrandom to bring convenience random init functions * Fix doc name * Rename new to random_from_rng * Deprecate new() in favor of random_from_rng() * Simplify constructors documentation Co-authored-by: Ciprian Dorin Craciun --- Cargo.toml | 1 + README.md | 8 +++--- benches/x25519.rs | 4 +-- src/lib.rs | 18 ++++++------- src/x25519.rs | 59 ++++++++++++++++++++++++++++++++++++++----- tests/x25519_tests.rs | 50 ++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac8be66..46e048c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ harness = false [features] default = ["alloc", "precomputed-tables", "zeroize"] +getrandom = ["rand_core/getrandom"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] serde = ["dep:serde", "curve25519-dalek/serde"] alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] diff --git a/README.md b/README.md index 7145f06..6bc217d 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,21 @@ up on modern public key cryptography and have learned a nifty trick called kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie! -First, Alice uses `EphemeralSecret::new()` and then +First, Alice uses `EphemeralSecret::random()` and then `PublicKey::from()` to produce her secret and public keys: ```rust -use rand_core::OsRng; use x25519_dalek::{EphemeralSecret, PublicKey}; -let alice_secret = EphemeralSecret::new(OsRng); +let alice_secret = EphemeralSecret::random(); let alice_public = PublicKey::from(&alice_secret); ``` Bob does the same: ```rust -# use rand_core::OsRng; # use x25519_dalek::{EphemeralSecret, PublicKey}; -let bob_secret = EphemeralSecret::new(OsRng); +let bob_secret = EphemeralSecret::random(); let bob_public = PublicKey::from(&bob_secret); ``` diff --git a/benches/x25519.rs b/benches/x25519.rs index dfcee4a..77c832d 100644 --- a/benches/x25519.rs +++ b/benches/x25519.rs @@ -19,12 +19,12 @@ use x25519_dalek::EphemeralSecret; use x25519_dalek::PublicKey; fn bench_diffie_hellman(c: &mut Criterion) { - let bob_secret = EphemeralSecret::new(OsRng); + let bob_secret = EphemeralSecret::random_from_rng(OsRng); let bob_public = PublicKey::from(&bob_secret); c.bench_function("diffie_hellman", move |b| { b.iter_with_setup( - || EphemeralSecret::new(OsRng), + || EphemeralSecret::random_from_rng(OsRng), |alice_secret| alice_secret.diffie_hellman(&bob_public), ) }); diff --git a/src/lib.rs b/src/lib.rs index 01369d0..7bcd8f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,14 +50,14 @@ //! kittens will be able to secretly organise to find their mittens, and then spend //! the rest of the afternoon nomming some yummy pie! //! -//! First, Alice uses `EphemeralSecret::new()` and then +//! First, Alice uses `EphemeralSecret::random_from_rng` and then //! `PublicKey::from()` to produce her secret and public keys: //! //! ```rust //! use rand_core::OsRng; //! use x25519_dalek::{EphemeralSecret, PublicKey}; //! -//! let alice_secret = EphemeralSecret::new(OsRng); +//! let alice_secret = EphemeralSecret::random_from_rng(OsRng); //! let alice_public = PublicKey::from(&alice_secret); //! ``` //! @@ -66,7 +66,7 @@ //! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! let bob_secret = EphemeralSecret::new(OsRng); +//! let bob_secret = EphemeralSecret::random_from_rng(OsRng); //! let bob_public = PublicKey::from(&bob_secret); //! ``` //! @@ -77,9 +77,9 @@ //! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); //! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); //! # let bob_public = PublicKey::from(&bob_secret); //! let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); //! ``` @@ -89,9 +89,9 @@ //! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); //! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); //! # let bob_public = PublicKey::from(&bob_secret); //! let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); //! ``` @@ -101,9 +101,9 @@ //! ```rust //! # use rand_core::OsRng; //! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::new(OsRng); +//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); //! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::new(OsRng); +//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); //! # let bob_public = PublicKey::from(&bob_secret); //! # let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); //! # let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); diff --git a/src/x25519.rs b/src/x25519.rs index 25831c7..f161243 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -71,8 +71,8 @@ impl AsRef<[u8]> for PublicKey { /// This type is identical to the [`StaticSecret`] type, except that the /// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there /// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be -/// generated from fresh randomness by [`EphemeralSecret::new`] and the compiler statically checks -/// that the resulting secret is used at most once. +/// generated from fresh randomness where the compiler statically checks that the resulting +/// secret is used at most once. #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] pub struct EphemeralSecret(pub(crate) Scalar); @@ -84,14 +84,29 @@ impl EphemeralSecret { SharedSecret(self.0 * their_public.0) } - /// Generate an x25519 [`EphemeralSecret`] key. + /// Generate a new [`EphemeralSecret`] with the supplied RNG. + #[deprecated( + since = "2.0.0", + note = "Renamed to `random_from_rng`. This will be removed in 2.1.0" + )] pub fn new(mut csprng: T) -> Self { + Self::random_from_rng(&mut csprng) + } + + /// Generate a new [`EphemeralSecret`] with the supplied RNG. + pub fn random_from_rng(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); EphemeralSecret(Scalar::from_bits_clamped(bytes)) } + + /// Generate a new [`EphemeralSecret`]. + #[cfg(feature = "getrandom")] + pub fn random() -> Self { + Self::random_from_rng(&mut rand_core::OsRng) + } } impl<'a> From<&'a EphemeralSecret> for PublicKey { @@ -133,14 +148,29 @@ impl ReusableSecret { SharedSecret(self.0 * their_public.0) } - /// Generate a non-serializeable x25519 [`ReusableSecret`] key. + /// Generate a new [`ReusableSecret`] with the supplied RNG. + #[deprecated( + since = "2.0.0", + note = "Renamed to `random_from_rng`. This will be removed in 2.1.0." + )] pub fn new(mut csprng: T) -> Self { + Self::random_from_rng(&mut csprng) + } + + /// Generate a new [`ReusableSecret`] with the supplied RNG. + pub fn random_from_rng(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); ReusableSecret(Scalar::from_bits_clamped(bytes)) } + + /// Generate a new [`ReusableSecret`]. + #[cfg(feature = "getrandom")] + pub fn random() -> Self { + Self::random_from_rng(&mut rand_core::OsRng) + } } #[cfg(feature = "reusable_secrets")] @@ -180,8 +210,17 @@ impl StaticSecret { SharedSecret(self.0 * their_public.0) } - /// Generate an x25519 key. + /// Generate a new [`StaticSecret`] with the supplied RNG. + #[deprecated( + since = "2.0.0", + note = "Renamed to `random_from_rng`. This will be removed in 2.1.0" + )] pub fn new(mut csprng: T) -> Self { + Self::random_from_rng(&mut csprng) + } + + /// Generate a new [`StaticSecret`] with the supplied RNG. + pub fn random_from_rng(mut csprng: T) -> Self { let mut bytes = [0u8; 32]; csprng.fill_bytes(&mut bytes); @@ -189,6 +228,12 @@ impl StaticSecret { StaticSecret(Scalar::from_bits_clamped(bytes)) } + /// Generate a new [`StaticSecret`]. + #[cfg(feature = "getrandom")] + pub fn random() -> Self { + Self::random_from_rng(&mut rand_core::OsRng) + } + /// Extract this key's bytes for serialization. #[inline] pub fn to_bytes(&self) -> [u8; 32] { @@ -307,11 +352,11 @@ impl AsRef<[u8]> for SharedSecret { /// use x25519_dalek::PublicKey; /// /// // Generate Alice's key pair. -/// let alice_secret = StaticSecret::new(&mut OsRng); +/// let alice_secret = StaticSecret::random_from_rng(&mut OsRng); /// let alice_public = PublicKey::from(&alice_secret); /// /// // Generate Bob's key pair. -/// let bob_secret = StaticSecret::new(&mut OsRng); +/// let bob_secret = StaticSecret::random_from_rng(&mut OsRng); /// let bob_public = PublicKey::from(&bob_secret); /// /// // Alice and Bob should now exchange their public keys. diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 21eeb43..280978d 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -180,3 +180,53 @@ fn rfc7748_ladder_test2() { ] ); } + +mod rand_core { + + use super::*; + use ::rand_core::OsRng; + + #[test] + fn ephemeral_from_rng() { + #[allow(deprecated)] + EphemeralSecret::new(OsRng); + EphemeralSecret::random_from_rng(OsRng); + } + + #[test] + #[cfg(feature = "reusable_secrets")] + fn reusable_from_rng() { + #[allow(deprecated)] + ReusableSecret::new(OsRng); + ReusableSecret::random_from_rng(OsRng); + } + + #[test] + fn static_from_rng() { + #[allow(deprecated)] + StaticSecret::new(OsRng); + StaticSecret::random_from_rng(OsRng); + } +} + +#[cfg(feature = "getrandom")] +mod getrandom { + + use super::*; + + #[test] + fn ephemeral_random() { + EphemeralSecret::random(); + } + + #[test] + #[cfg(feature = "reusable_secrets")] + fn reusable_random() { + ReusableSecret::random(); + } + + #[test] + fn static_random() { + StaticSecret::random(); + } +} From 84158337afa5bca7edb676c4b3d80d0fe4b64bf1 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Sat, 1 Apr 2023 04:34:03 +1100 Subject: [PATCH 156/160] Make `static_secrets` optional (#122) * Make `static_secrets` optional * Added more feature combinations to CI --- .github/workflows/rust.yml | 6 +++++- Cargo.toml | 1 + src/x25519.rs | 15 ++++++++++----- tests/x25519_tests.rs | 2 ++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d5c47c5..c367173 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,7 +13,7 @@ env: jobs: test: - name: Test all features + name: Test with multiple feature combinations runs-on: ubuntu-latest strategy: matrix: @@ -29,6 +29,10 @@ jobs: with: target: ${{ matrix.target }} - run: ${{ matrix.deps }} + - run: cargo test --target ${{ matrix.target }} --no-default-features + - run: cargo test --target ${{ matrix.target }} --no-default-features --features reusable_secrets + - run: cargo test --target ${{ matrix.target }} --no-default-features --features static_secrets + - run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }} --all-features build-simd: diff --git a/Cargo.toml b/Cargo.toml index 46e048c..f6c48d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,3 +60,4 @@ serde = ["dep:serde", "curve25519-dalek/serde"] alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] +static_secrets = [] diff --git a/src/x25519.rs b/src/x25519.rs index f161243..c8008ac 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -24,8 +24,7 @@ use rand_core::RngCore; #[cfg(feature = "zeroize")] use zeroize::Zeroize; -/// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or -/// [`StaticSecret`] key. +/// A Diffie-Hellman public key /// /// We implement `Zeroize` so that downstream consumers may derive it for `Drop` /// should they wish to erase public keys from memory. Note that this erasure @@ -68,7 +67,7 @@ impl AsRef<[u8]> for PublicKey { /// A short-lived Diffie-Hellman secret key that can only be used to compute a single /// [`SharedSecret`]. /// -/// This type is identical to the [`StaticSecret`] type, except that the +/// This type is identical to the `StaticSecret` type, except that the /// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there /// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be /// generated from fresh randomness where the compiler statically checks that the resulting @@ -125,7 +124,7 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { /// /// Similarly to [`EphemeralSecret`], this type does _not_ have serialisation /// methods, in order to discourage long-term usage of secret key material. (For -/// long-term secret keys, see [`StaticSecret`].) +/// long-term secret keys, see `StaticSecret`.) /// /// # Warning /// @@ -195,6 +194,7 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that /// secret keys are never reused, which can have very serious security /// implications for many protocols. +#[cfg(feature = "static_secrets")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] @@ -203,6 +203,7 @@ pub struct StaticSecret( #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, ); +#[cfg(feature = "static_secrets")] impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. @@ -247,6 +248,7 @@ impl StaticSecret { } } +#[cfg(feature = "static_secrets")] impl From<[u8; 32]> for StaticSecret { /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { @@ -254,6 +256,7 @@ impl From<[u8; 32]> for StaticSecret { } } +#[cfg(feature = "static_secrets")] impl<'a> From<&'a StaticSecret> for PublicKey { /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { @@ -261,6 +264,7 @@ impl<'a> From<&'a StaticSecret> for PublicKey { } } +#[cfg(feature = "static_secrets")] impl AsRef<[u8]> for StaticSecret { /// View this key as a byte array. #[inline] @@ -343,7 +347,8 @@ impl AsRef<[u8]> for SharedSecret { /// cannot use the better, safer, and faster ephemeral DH API. /// /// # Example -/// ```rust +#[cfg_attr(feature = "static_secrets", doc = "```")] +#[cfg_attr(not(feature = "static_secrets"), doc = "```ignore")] /// use rand_core::OsRng; /// use rand_core::RngCore; /// diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index 280978d..d883915 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -202,6 +202,7 @@ mod rand_core { } #[test] + #[cfg(feature = "static_secrets")] fn static_from_rng() { #[allow(deprecated)] StaticSecret::new(OsRng); @@ -226,6 +227,7 @@ mod getrandom { } #[test] + #[cfg(feature = "static_secrets")] fn static_random() { StaticSecret::random(); } From cccf389467f48f7d1f591c8c383b0279a0bfa801 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Sat, 1 Apr 2023 04:53:51 +1100 Subject: [PATCH 157/160] chore: Release 2.0.0-rc.2 (#123) --- CHANGELOG.md | 7 ++ Cargo.lock | 206 +++++++++++++++++++++++++++------------------------ Cargo.toml | 8 +- README.md | 6 +- src/lib.rs | 147 +++--------------------------------- 5 files changed, 132 insertions(+), 242 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3d6317..672e901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,14 @@ Entries are listed in reverse chronological order. # 2.x Series +## 2.0.0-rc.2 + * Update MSRV to 1.60. +* Update edition to 2021 +* Add `.as_bytes()` and `AsRef<[u8]>` for `Shared/StaticSecret` +* Add `getrandom` feature to provide `random_from_rng` constructors +* Make `StaticSecrets` optional via feature `static_secrets` +* Update underlying `curve25519_dalek` library to `4.0.0-rc.2` ## 2.0.0-pre.1 diff --git a/Cargo.lock b/Cargo.lock index bfcb715..ce67227 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "atty" version = "0.2.14" @@ -53,27 +59,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "clap" -version = "2.34.0" +name = "ciborium" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", + "clap_lex", + "indexmap", "textwrap", - "unicode-width", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] name = "criterion" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ + "anes", "atty", "cast", + "ciborium", "clap", "criterion-plot", - "csv", "itertools", "lazy_static", "num-traits", @@ -82,7 +126,6 @@ dependencies = [ "rayon", "regex", "serde", - "serde_cbor", "serde_derive", "serde_json", "tinytemplate", @@ -91,9 +134,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", @@ -142,32 +185,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "csv" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.1" +version = "4.0.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" dependencies = [ "cfg-if", "fiat-crypto", @@ -207,6 +229,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -225,6 +253,16 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itertools" version = "0.10.5" @@ -276,12 +314,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - [[package]] name = "memoffset" version = "0.8.0" @@ -322,6 +354,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + [[package]] name = "packed_simd_2" version = "0.3.8" @@ -368,18 +406,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -417,18 +455,18 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "ryu" @@ -453,39 +491,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.155" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.155" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.12", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", @@ -510,25 +538,21 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" dependencies = [ "proc-macro2", "quote", - "syn", - "unicode-xid", + "unicode-ident", ] [[package]] name = "textwrap" -version = "0.11.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tinytemplate" @@ -546,26 +570,13 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -596,7 +607,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -618,7 +629,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -672,7 +683,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "x25519-dalek" -version = "2.0.0-pre.1" +version = "2.0.0-rc.2" dependencies = [ "bincode", "criterion", @@ -684,21 +695,20 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", + "syn 2.0.12", ] diff --git a/Cargo.toml b/Cargo.toml index f6c48d7..d2f068a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # - update html_root_url # - update CHANGELOG # - if any changes were made to README.md, mirror them in src/lib.rs docs -version = "2.0.0-pre.1" +version = "2.0.0-rc.2" authors = [ "Isis Lovecruft ", "DebugSteven ", @@ -35,17 +35,17 @@ rustdoc-args = [ "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", "--cfg", "docsrs", ] -features = ["reusable_secrets", "serde"] +features = ["getrandom", "reusable_secrets", "serde", "static_secrets"] [dependencies] -curve25519-dalek = { version = "4.0.0-rc.0", default-features = false } +curve25519-dalek = { version = "4.0.0-rc.2", default-features = false } rand_core = { version = "0.6", default-features = false } serde = { version = "1", default-features = false, optional = true, features = ["derive"] } zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } [dev-dependencies] bincode = "1" -criterion = "0.3.0" +criterion = "0.4.0" rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } [[bench]] diff --git a/README.md b/README.md index 6bc217d..039605a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ the rest of the afternoon nomming some yummy pie! First, Alice uses `EphemeralSecret::random()` and then `PublicKey::from()` to produce her secret and public keys: -```rust +```ignore use x25519_dalek::{EphemeralSecret, PublicKey}; let alice_secret = EphemeralSecret::random(); @@ -40,7 +40,7 @@ let alice_public = PublicKey::from(&alice_secret); Bob does the same: -```rust +```ignore # use x25519_dalek::{EphemeralSecret, PublicKey}; let bob_secret = EphemeralSecret::random(); let bob_public = PublicKey::from(&bob_secret); @@ -100,7 +100,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies] -x25519-dalek = "2.0.0-pre.0" +x25519-dalek = "2.0.0-rc.2" ``` # MSRV diff --git a/src/lib.rs b/src/lib.rs index 7bcd8f4..9a5fc19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,144 +16,17 @@ #![no_std] #![cfg_attr(feature = "bench", feature(test))] -#![cfg_attr(feature = "nightly", deny(missing_docs))] -#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/x25519-dalek/2.0.0-pre.1")] +#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] +#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] +#![deny(missing_docs)] +#![doc( + html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png" +)] +#![doc = include_str!("../README.md")] -//! # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) -//! -//! A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, -//! with curve operations provided by -//! [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). -//! -//! This crate provides two levels of API: a bare byte-oriented `x25519` -//! function which matches the function specified in [RFC7748][rfc7748], as -//! well as a higher-level Rust API for static and ephemeral Diffie-Hellman. -//! -//! ## Examples -//! -//! -//! -//! -//! -//! Alice and Bob are two adorable kittens who have lost their mittens, and they -//! wish to be able to send secret messages to each other to coordinate finding -//! them, otherwise—if their caretaker cat finds out—they will surely be called -//! naughty kittens and be given no pie! -//! -//! But the two kittens are quite clever. Even though their paws are still too big -//! and the rest of them is 90% fuzziness, these clever kittens have been studying -//! up on modern public key cryptography and have learned a nifty trick called -//! *elliptic curve Diffie-Hellman key exchange*. With the right incantations, the -//! kittens will be able to secretly organise to find their mittens, and then spend -//! the rest of the afternoon nomming some yummy pie! -//! -//! First, Alice uses `EphemeralSecret::random_from_rng` and then -//! `PublicKey::from()` to produce her secret and public keys: -//! -//! ```rust -//! use rand_core::OsRng; -//! use x25519_dalek::{EphemeralSecret, PublicKey}; -//! -//! let alice_secret = EphemeralSecret::random_from_rng(OsRng); -//! let alice_public = PublicKey::from(&alice_secret); -//! ``` -//! -//! Bob does the same: -//! -//! ```rust -//! # use rand_core::OsRng; -//! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! let bob_secret = EphemeralSecret::random_from_rng(OsRng); -//! let bob_public = PublicKey::from(&bob_secret); -//! ``` -//! -//! Alice meows across the room, telling `alice_public` to Bob, and Bob -//! loudly meows `bob_public` back to Alice. Alice now computes her -//! shared secret with Bob by doing: -//! -//! ```rust -//! # use rand_core::OsRng; -//! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let bob_public = PublicKey::from(&bob_secret); -//! let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); -//! ``` -//! -//! Similarly, Bob computes a shared secret by doing: -//! -//! ```rust -//! # use rand_core::OsRng; -//! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let bob_public = PublicKey::from(&bob_secret); -//! let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); -//! ``` -//! -//! These secrets are the same: -//! -//! ```rust -//! # use rand_core::OsRng; -//! # use x25519_dalek::{EphemeralSecret, PublicKey}; -//! # let alice_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let alice_public = PublicKey::from(&alice_secret); -//! # let bob_secret = EphemeralSecret::random_from_rng(OsRng); -//! # let bob_public = PublicKey::from(&bob_secret); -//! # let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); -//! # let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); -//! assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); -//! ``` -//! -//! Voilà! Alice and Bob can now use their shared secret to encrypt their -//! meows, for example, by using it to generate a key and nonce for an -//! authenticated-encryption cipher. -//! -//! This example used the ephemeral DH API, which ensures that secret keys -//! cannot be reused; Alice and Bob could instead use the static DH API -//! and load a long-term secret key. -//! -//! # Installation -//! -//! To install, add the following to your project's `Cargo.toml`: -//! -//! ```toml -//! [dependencies] -//! x25519-dalek = "2.0.0-pre.0" -//! ``` -//! -//! # MSRV -//! -//! Current MSRV is 1.60. -//! -//! # Documentation -//! -//! Documentation is available [here](https://docs.rs/x25519-dalek). -//! -//! # Note -//! -//! This code matches the [RFC7748][rfc7748] test vectors. -//! The elliptic curve -//! operations are provided by `curve25519-dalek`, which makes a best-effort -//! attempt to prevent software side-channels. -//! -//! "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) -//! copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) -//! -//! [rfc7748]: https://tools.ietf.org/html/rfc7748 -//! -//! # See also -//! -//! - [crypto_box]: pure Rust public-key authenticated encryption compatible with -//! the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses -//! `x25519-dalek` for key agreement -//! -//! [crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box +//------------------------------------------------------------------------ +// x25519-dalek public API +//------------------------------------------------------------------------ mod x25519; From 25fa593df20b098fcca04c89d88fc61fed8e413c Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Fri, 31 Mar 2023 16:58:35 -0400 Subject: [PATCH 158/160] Update to new `Scalar` API (#120) * Updated to new curve25519 scalar API * Removed clamping from constructors; clamping is always done during scalar-point multiplication * Updated test to reflect new functionality * Updated changelog --- CHANGELOG.md | 5 ++++ Cargo.lock | 3 +-- Cargo.toml | 4 +++ src/x25519.rs | 61 ++++++++++++++----------------------------- tests/x25519_tests.rs | 13 ++++----- 5 files changed, 35 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 672e901..eaa5548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Entries are listed in reverse chronological order. # 2.x Series + +## 2.0.0-rc.3 + +* Change: `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. + ## 2.0.0-rc.2 * Update MSRV to 1.60. diff --git a/Cargo.lock b/Cargo.lock index ce67227..258ad05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,8 +188,7 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "4.0.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb" dependencies = [ "cfg-if", "fiat-crypto", diff --git a/Cargo.toml b/Cargo.toml index d2f068a..7064976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,3 +61,7 @@ alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] static_secrets = [] + +[patch.crates-io.curve25519-dalek] +git = "https://github.com/dalek-cryptography/curve25519-dalek.git" +rev = "f460ae149b0000695205cc78f560d74a2d3918eb" diff --git a/src/x25519.rs b/src/x25519.rs index c8008ac..e1c79d4 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,9 +14,7 @@ //! This implements x25519 key exchange as specified by Mike Hamburg //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). -use curve25519_dalek::{ - edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar, traits::IsIdentity, -}; +use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity}; use rand_core::CryptoRng; use rand_core::RngCore; @@ -74,13 +72,13 @@ impl AsRef<[u8]> for PublicKey { /// secret is used at most once. #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] -pub struct EphemeralSecret(pub(crate) Scalar); +pub struct EphemeralSecret(pub(crate) [u8; 32]); impl EphemeralSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`EphemeralSecret`] with the supplied RNG. @@ -94,11 +92,10 @@ impl EphemeralSecret { /// Generate a new [`EphemeralSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - EphemeralSecret(Scalar::from_bits_clamped(bytes)) + EphemeralSecret(bytes) } /// Generate a new [`EphemeralSecret`]. @@ -111,7 +108,7 @@ impl EphemeralSecret { impl<'a> From<&'a EphemeralSecret> for PublicKey { /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a EphemeralSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -137,14 +134,14 @@ impl<'a> From<&'a EphemeralSecret> for PublicKey { #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] #[derive(Clone)] -pub struct ReusableSecret(pub(crate) Scalar); +pub struct ReusableSecret(pub(crate) [u8; 32]); #[cfg(feature = "reusable_secrets")] impl ReusableSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a [`SharedSecret`]. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`ReusableSecret`] with the supplied RNG. @@ -158,11 +155,10 @@ impl ReusableSecret { /// Generate a new [`ReusableSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - ReusableSecret(Scalar::from_bits_clamped(bytes)) + ReusableSecret(bytes) } /// Generate a new [`ReusableSecret`]. @@ -176,7 +172,7 @@ impl ReusableSecret { impl<'a> From<&'a ReusableSecret> for PublicKey { /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a ReusableSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -199,16 +195,14 @@ impl<'a> From<&'a ReusableSecret> for PublicKey { #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[cfg_attr(feature = "zeroize", zeroize(drop))] #[derive(Clone)] -pub struct StaticSecret( - #[cfg_attr(feature = "serde", serde(with = "AllowUnreducedScalarBytes"))] pub(crate) Scalar, -); +pub struct StaticSecret([u8; 32]); #[cfg(feature = "static_secrets")] impl StaticSecret { /// Perform a Diffie-Hellman key agreement between `self` and /// `their_public` key to produce a `SharedSecret`. pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { - SharedSecret(self.0 * their_public.0) + SharedSecret(their_public.0.mul_clamped(self.0)) } /// Generate a new [`StaticSecret`] with the supplied RNG. @@ -222,11 +216,10 @@ impl StaticSecret { /// Generate a new [`StaticSecret`] with the supplied RNG. pub fn random_from_rng(mut csprng: T) -> Self { + // The secret key is random bytes. Clamping is done later. let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - - StaticSecret(Scalar::from_bits_clamped(bytes)) + StaticSecret(bytes) } /// Generate a new [`StaticSecret`]. @@ -238,13 +231,13 @@ impl StaticSecret { /// Extract this key's bytes for serialization. #[inline] pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + self.0 } /// View this key as a byte array. #[inline] pub fn as_bytes(&self) -> &[u8; 32] { - self.0.as_bytes() + &self.0 } } @@ -252,7 +245,7 @@ impl StaticSecret { impl From<[u8; 32]> for StaticSecret { /// Load a secret key from a byte array. fn from(bytes: [u8; 32]) -> StaticSecret { - StaticSecret(Scalar::from_bits_clamped(bytes)) + StaticSecret(bytes) } } @@ -260,7 +253,7 @@ impl From<[u8; 32]> for StaticSecret { impl<'a> From<&'a StaticSecret> for PublicKey { /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. fn from(secret: &'a StaticSecret) -> PublicKey { - PublicKey(EdwardsPoint::mul_base(&secret.0).to_montgomery()) + PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) } } @@ -373,7 +366,7 @@ impl AsRef<[u8]> for SharedSecret { /// assert_eq!(alice_shared, bob_shared); /// ``` pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { - (Scalar::from_bits_clamped(k) * MontgomeryPoint(u)).to_bytes() + MontgomeryPoint(u).mul_clamped(k).to_bytes() } /// The X25519 basepoint, for use with the bare, byte-oriented x25519 @@ -382,17 +375,3 @@ pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - -/// Derived serialization methods will not work on a StaticSecret because x25519 requires -/// non-canonical scalars which are rejected by curve25519-dalek. Thus we provide a way to convert -/// the bytes directly to a scalar using Serde's remote derive functionality. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(remote = "Scalar"))] -struct AllowUnreducedScalarBytes( - #[cfg_attr(feature = "serde", serde(getter = "Scalar::to_bytes"))] [u8; 32], -); -impl From for Scalar { - fn from(bytes: AllowUnreducedScalarBytes) -> Scalar { - Scalar::from_bits_clamped(bytes.0) - } -} diff --git a/tests/x25519_tests.rs b/tests/x25519_tests.rs index d883915..d589b3e 100644 --- a/tests/x25519_tests.rs +++ b/tests/x25519_tests.rs @@ -1,4 +1,4 @@ -use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; +use curve25519_dalek::edwards::EdwardsPoint; use x25519_dalek::*; @@ -10,11 +10,9 @@ fn byte_basepoint_matches_edwards_scalar_mul() { scalar_bytes[i] += 2; let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); - - let expected = { - let scalar = Scalar::from_bits_clamped(scalar_bytes); - EdwardsPoint::mul_base(&scalar).to_montgomery().to_bytes() - }; + let expected = EdwardsPoint::mul_base_clamped(scalar_bytes) + .to_montgomery() + .to_bytes(); assert_eq!(result, expected); } @@ -64,8 +62,7 @@ fn serde_bincode_static_secret_matches_from_bytes() { use bincode; let expected = StaticSecret::from([0x24; 32]); - let clamped_bytes = Scalar::from_bits_clamped([0x24; 32]).to_bytes(); - let decoded: StaticSecret = bincode::deserialize(&clamped_bytes).unwrap(); + let decoded: StaticSecret = bincode::deserialize(&[0x24; 32]).unwrap(); assert_eq!(decoded.to_bytes(), expected.to_bytes()); } From d828434d60fee67ee785bfc18622ec0867a20346 Mon Sep 17 00:00:00 2001 From: eaon Date: Mon, 8 May 2023 18:56:43 -0400 Subject: [PATCH 159/160] Update crypto_box URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 039605a..8b05629 100644 --- a/README.md +++ b/README.md @@ -129,4 +129,4 @@ copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses `x25519-dalek` for key agreement -[crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box +[crypto_box]: https://github.com/RustCrypto/nacl-compat/tree/master/crypto_box From 8613b5a809c78160a2c16b4635f665a15aea2055 Mon Sep 17 00:00:00 2001 From: "pinkforest(she/her)" <36498018+pinkforest@users.noreply.github.com> Date: Sat, 24 Jun 2023 03:54:38 +0000 Subject: [PATCH 160/160] chore: Release 2.0.0-rc.3 (#128) * chore: Release 2.0.0-rc.3 * cargo update -p curve25519-dalek * Added note about backends * Fixed docs broken link --------- Co-authored-by: Michael Rosenberg --- CHANGELOG.md | 12 ++++++++-- Cargo.lock | 62 +++++++++++++++++++++++++++++++++++----------------- Cargo.toml | 8 ++----- README.md | 9 +++++++- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa5548..d2c337c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ Entries are listed in reverse chronological order. # 2.x Series +* Note: All `x255919-dalek` 2.x releases are in sync with the underlying `curve25519-dalek` 4.x releases. ## 2.0.0-rc.3 -* Change: `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. +* `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. +* Update underlying `curve25519_dalek` library to `4.0.0-rc.3`. Notable changes: + * [curve25519-dalek backend] now by default auto selects `simd` backend over `serial` where supported. + ## 2.0.0-rc.2 @@ -16,7 +20,9 @@ Entries are listed in reverse chronological order. * Add `.as_bytes()` and `AsRef<[u8]>` for `Shared/StaticSecret` * Add `getrandom` feature to provide `random_from_rng` constructors * Make `StaticSecrets` optional via feature `static_secrets` -* Update underlying `curve25519_dalek` library to `4.0.0-rc.2` +* Update underlying `curve25519_dalek` library to `4.0.0-rc.2`. Notable changes: + * [curve25519-dalek backend] additive features have been removed in favor of cfg based selection. + * [curve25519-dalek backend] now by default auto selects the appropriate word size over the previous default `32`. ## 2.0.0-pre.1 @@ -88,3 +94,5 @@ Entries are listed in reverse chronological order. * Adds support for static and ephemeral keys. +[curve25519-dalek backend]: https://github.com/dalek-cryptography/curve25519-dalek/#backends + diff --git a/Cargo.lock b/Cargo.lock index 258ad05..ff401af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + [[package]] name = "criterion" version = "0.4.0" @@ -187,18 +196,32 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.2" -source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git?rev=f460ae149b0000695205cc78f560d74a2d3918eb#f460ae149b0000695205cc78f560d74a2d3918eb" +version = "4.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" dependencies = [ "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", "fiat-crypto", - "packed_simd_2", "platforms", + "rustc_version", "serde", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.12", +] + [[package]] name = "either" version = "1.8.1" @@ -298,12 +321,6 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "log" version = "0.4.17" @@ -359,16 +376,6 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm", -] - [[package]] name = "platforms" version = "3.0.2" @@ -467,6 +474,15 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.13" @@ -488,6 +504,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.159" @@ -682,7 +704,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "x25519-dalek" -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" dependencies = [ "bincode", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 7064976..201968b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # - update html_root_url # - update CHANGELOG # - if any changes were made to README.md, mirror them in src/lib.rs docs -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" authors = [ "Isis Lovecruft ", "DebugSteven ", @@ -38,7 +38,7 @@ rustdoc-args = [ features = ["getrandom", "reusable_secrets", "serde", "static_secrets"] [dependencies] -curve25519-dalek = { version = "4.0.0-rc.2", default-features = false } +curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false } rand_core = { version = "0.6", default-features = false } serde = { version = "1", default-features = false, optional = true, features = ["derive"] } zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } @@ -61,7 +61,3 @@ alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] precomputed-tables = ["curve25519-dalek/precomputed-tables"] reusable_secrets = [] static_secrets = [] - -[patch.crates-io.curve25519-dalek] -git = "https://github.com/dalek-cryptography/curve25519-dalek.git" -rev = "f460ae149b0000695205cc78f560d74a2d3918eb" diff --git a/README.md b/README.md index 8b05629..a25210a 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ To install, add the following to your project's `Cargo.toml`: ```toml [dependencies] -x25519-dalek = "2.0.0-rc.2" +x25519-dalek = "2.0.0-rc.3" ``` # MSRV @@ -111,6 +111,12 @@ Current MSRV is 1.60. Documentation is available [here](https://docs.rs/x25519-dalek). +# Performance and backend selection + +Performance is a secondary goal behind correctness, safety, and clarity, but we aim to be competitive with other implementations. To this end, we allow users to choose their _backend_, i.e., the underlying implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]). + +Further instructions and details regarding backends can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends). + # Note This code matches the [RFC7748][rfc7748] test vectors. @@ -129,4 +135,5 @@ copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses `x25519-dalek` for key agreement +[fiat]: https://github.com/mit-plv/fiat-crypto [crypto_box]: https://github.com/RustCrypto/nacl-compat/tree/master/crypto_box