From 11b1dc142fb31a730ebb98aa8ce2e604cd04a4cc Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 30 Jun 2018 09:49:54 -0600 Subject: [PATCH 01/58] Add test for behavior of Scalar::batch_invert(). --- src/scalar.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 5b67732..15c8b7c 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1138,4 +1138,20 @@ mod test { // This should panic in debug mode. Scalar::batch_invert(&mut xs); } + + #[test] + fn batch_invert_consistency() { + let mut x = Scalar::from_u64(1); + let mut v1: Vec<_> = (0..16).map(|_| {let tmp = x; x = x + x; tmp}).collect(); + let v2 = v1.clone(); + + let expected: Scalar = v1.iter().product(); + let expected = expected.invert(); + let ret = Scalar::batch_invert(&mut v1); + assert_eq!(ret, expected); + + for (a, b) in v1.iter().zip(v2.iter()) { + assert_eq!(a * b, Scalar::one()); + } + } } From 611fc403188bf42b1763b68ad79170b2037a1727 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 30 Jun 2018 16:29:02 -0600 Subject: [PATCH 02/58] Add test that an empty vector field inversion returns one. --- src/scalar.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 15c8b7c..1708e29 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1139,6 +1139,11 @@ mod test { Scalar::batch_invert(&mut xs); } + #[test] + fn batch_invert_empty() { + assert_eq!(Scalar::one(), Scalar::batch_invert(&mut [])); + } + #[test] fn batch_invert_consistency() { let mut x = Scalar::from_u64(1); From 6294c02b52563a7105e9352f1a14857f2c92d73d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 30 Jun 2018 16:30:35 -0600 Subject: [PATCH 03/58] Replace batch inversion implementation for Scalar with sequential variant of Montgomery's trick. --- src/scalar.rs | 62 ++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 1708e29..65e86ad 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -434,9 +434,6 @@ impl Scalar { /// *prove* that this is the case, you **SHOULD NOT USE THIS /// FUNCTION**. /// - /// This function is most efficient when the batch size (slice - /// length) is a power of 2. - /// /// # Example /// /// ``` @@ -474,38 +471,47 @@ impl Scalar { // Mark UnpackedScalars as zeroable. unsafe impl ZeroSafe for UnpackedScalar {} - let n = inputs.len().next_power_of_two(); + let n = inputs.len(); let one: UnpackedScalar = Scalar::one().unpack().to_montgomery(); - // Wrap the tree storage in a ClearOnDrop to wipe it when we - // pass out of scope. - let tree_vec = vec![one; 2*n]; - let mut tree = ClearOnDrop::new(tree_vec); + // Wrap the scratch storage in a ClearOnDrop to wipe it when + // we pass out of scope. + let scratch_vec = vec![one; n]; + let mut scratch = ClearOnDrop::new(scratch_vec); - for i in 0..inputs.len() { - tree[n+i] = inputs[i].unpack().to_montgomery(); + // Keep an accumulator of all of the previous products + let mut acc = Scalar::one().unpack().to_montgomery(); + + // Pass through the input vector, recording the previous + // products in the scratch space + for (input, scratch) in inputs.iter_mut().zip(scratch.iter_mut()) { + *scratch = acc; + + // Avoid unnecessary Montgomery multiplication in second pass by + // keeping inputs in Montgomery form + let tmp = input.unpack().to_montgomery(); + *input = tmp.pack(); + acc = UnpackedScalar::montgomery_mul(&acc, &tmp); } - for i in (1..n).rev() { - tree[i] = UnpackedScalar::montgomery_mul(&tree[2*i], &tree[2*i+1]); + // acc is nonzero iff all inputs are nonzero + debug_assert!(acc.pack() != Scalar::zero()); + + // Compute the inverse of all products + acc = acc.montgomery_invert().from_montgomery(); + + // We need to return the product of all inverses later + let ret = acc.pack(); + + // Pass through the vector backwards to compute the inverses + // in place + for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) { + let tmp = UnpackedScalar::montgomery_mul(&acc, &input.unpack()); + *input = UnpackedScalar::montgomery_mul(&acc, &scratch).pack(); + acc = tmp; } - // tree[1] is zero iff any of the inputs are zero. - debug_assert!(tree[1].from_montgomery().pack() != Scalar::zero()); - - let allinv = tree[1].montgomery_invert(); - - for i in 0..inputs.len() { - let mut inv = allinv; - let mut node = n + i; - while node > 1 { - inv = UnpackedScalar::montgomery_mul(&inv, &tree[node ^1]); - node = node >> 1; - } - inputs[i] = inv.from_montgomery().pack(); - } - - allinv.from_montgomery().pack() + ret } /// Get the bits of the scalar. From 02af12b81a46ff165db14ff6da1ea0cc6dbfe0ba Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 30 Jun 2018 16:50:07 -0600 Subject: [PATCH 04/58] Replace batch inversion for FieldElement with sequential variant of Montgomery's trick. --- src/field.rs | 73 +++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/src/field.rs b/src/field.rs index 938fa03..c9f4c49 100644 --- a/src/field.rs +++ b/src/field.rs @@ -142,58 +142,34 @@ impl FieldElement { /// Given a slice of public `FieldElements`, replace each with its inverse. /// /// All input `FieldElements` **MUST** be nonzero. - /// - /// This function is most efficient when the batch size (slice - /// length) is a power of 2. #[cfg(any(feature = "alloc", feature = "std"))] pub fn batch_invert(inputs: &mut [FieldElement]) { - // First, compute the product of all inputs using a product - // tree: - // - // Inputs: [x_0, x_1, x_2] - // - // Tree: - // - // x_0*x_1*x_2*1 tree[1] - // / \ - // x_0*x_1 x_2*1 tree[2,3] - // / \ / \ - // x_0 x_1 x_2 1 tree[4,5,6,7] - // - // The leaves of the tree are the inputs. We store the tree in - // an array of length 2*n, similar to a binary heap. - // - // To initialize the tree, set every node to 1, then fill in - // the leaf nodes with the input variables. Finally, set every - // non-leaf node to be the product of its children. + // Montgomery’s Trick and Fast Implementation of Masked AES + // Genelle, Prouff and Quisquater + // Section 3.2 - let n = inputs.len().next_power_of_two(); - let mut tree = vec![FieldElement::one(); 2*n]; - tree[n..n+inputs.len()].copy_from_slice(inputs); - for i in (1..n).rev() { - tree[i] = &tree[2*i] * &tree[2*i+1]; + let n = inputs.len(); + let mut scratch = vec![FieldElement::one(); n]; + + // Keep an accumulator of all of the previous products + let mut acc = FieldElement::one(); + + // Pass through the input vector, recording the previous + // products in the scratch space + for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) { + *scratch = acc; + acc = &acc * input; } - // The root of the tree is the product of all inputs, and is - // stored at index 1. Compute its inverse. - let allinv = tree[1].invert(); + // Compute the inverse of all products + acc = acc.invert(); - // To compute y_i = 1/x_i, start at the i-th leaf node of the - // tree, and walk up to the root of the tree, multiplying - // `allinv` by each sibling. This computes - // - // y_i = y * (all x_j except x_i) - // - // using lg(n) multiplications for each y_i, taking n*lg(n) in - // total. - for i in 0..inputs.len() { - let mut inv = allinv; - let mut node = n + i; - while node > 1 { - inv *= &tree[node ^ 1]; - node = node >> 1; - } - inputs[i] = inv; + // Pass through the vector backwards to compute the inverses + // in place + for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) { + let tmp = &acc * input; + *input = &acc * &scratch; + acc = tmp; } } @@ -496,4 +472,9 @@ mod test { assert_eq!(one_bytes[i], 0); } } + + #[test] + fn batch_invert_empty() { + FieldElement::batch_invert(&mut []); + } } From c4f86b231c63344daf57f90ee85da2fa1c5723e0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 1 Jul 2018 02:16:19 -0600 Subject: [PATCH 05/58] Only test debug assertion in batch_invert when debug assertions are enabled. --- src/scalar.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scalar.rs b/src/scalar.rs index 65e86ad..7e02637 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1136,6 +1136,7 @@ mod test { assert_eq!(parsed, X); } + #[cfg(debug_assertions)] #[test] #[should_panic] fn batch_invert_with_a_zero_input_panics() { From 61d6d89cd82ad7f6783ce76955e12b3949fb9a26 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 2 Jul 2018 10:41:45 -0600 Subject: [PATCH 06/58] Fix comment describing Montgomery adjustment factor's value. --- src/backend/u64/constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 1cc23fa..20ecb7f 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -39,7 +39,7 @@ pub(crate) const APLUS2_OVER_FOUR: FieldElement64 = FieldElement64([121666, 0, 0 /// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493 pub(crate) const L: Scalar64 = Scalar64([ 0x0002631a5cf5d3ed, 0x000dea2f79cd6581, 0x000000000014def9, 0x0000000000000000, 0x0000100000000000 ]); -/// `L` * `LFACTOR` = -1 (mod 2^51) +/// `L` * `LFACTOR` = -1 (mod 2^52) pub(crate) const LFACTOR: u64 = 0x51da312547e1b; /// `R` = R % L where R = 2^260 From b214e6e796dad6d5c514eca6ef87656397e72050 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 19:56:16 +0000 Subject: [PATCH 07/58] Bump subtle dependency to 0.7.0. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3978ee5..0960fd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" -subtle = { version = "0.6", features = ["generic-impls"], default-features = false } +subtle = { version = "0.7", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } [build-dependencies] @@ -55,7 +55,7 @@ byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" -subtle = { version = "0.6", features = ["generic-impls"], default-features = false } +subtle = { version = "0.7", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } [features] From 3dbfad2f7aae37ae717e0fb4e7f9cd44944e0776 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Jul 2018 21:09:02 +0000 Subject: [PATCH 08/58] Update rand dependency to 0.5. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3978ee5..5f41ad9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ harness = false # match exactly, since the build.rs uses the crate itself as a library. [dependencies] -rand = { version = "0.5.0", default-features = false } +rand = { version = "0.5", default-features = false } byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" @@ -50,7 +50,7 @@ subtle = { version = "0.6", features = ["generic-impls"], default-features = fal serde = { version = "1.0", optional = true } [build-dependencies] -rand = { version = "0.5.0", default-features = false } +rand = { version = "0.5", default-features = false } byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" From ebf0801dfce8b18f13729e8de9bac8b4080acbc7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 20:50:22 +0000 Subject: [PATCH 09/58] Update warning statement on production readiness in README. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ddf4283..5e59cfa 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ prime-order group from a non-prime-order Edwards curve. This provides the speed and safety benefits of Edwards curve arithmetic, without the pitfalls of cofactor-related abstraction mismatches. -## WARNING +## Stability -We do not yet consider this code to be production-ready. We intend to -stabilize a production-ready version `1.0` soon. +We have recently released a `1.0.0-pre.0` version of `curve25519-dalek` and +would greatly appreciate testing and feedback on our API and performance. # Documentation From 4a54f66a48b6d30ace6c400b74b30001c389d054 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 20:54:05 +0000 Subject: [PATCH 10/58] Move documentation of yolocrypto feature in README. --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5e59cfa..fe086d6 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,6 @@ extern crate curve25519_dalek; # Backends and Features -The `yolocrypto` feature enables experimental features. The name `yolocrypto` -is meant to indicate that it is not considered production-ready, and we do not -consider `yolocrypto` features to be covered by semver guarantees. - The `std` feature is enabled by default, but it can be disabled. The `nightly` feature enables nightly-only features. **It is recommended for security**. @@ -93,6 +89,13 @@ cargo bench --no-default-features --features "std u64_backend" cargo bench --no-default-features --features "std avx2_backend" ``` +The `yolocrypto` feature enables experimental features. The name `yolocrypto` +is meant to indicate that it is not considered production-ready, and we do not +consider `yolocrypto` features to be covered by semver guarantees. +This is designed to make it easier to test intended new features +without having to stabilise them first. Use `yolocrypto` at your own, +obvious, risk. + # Contributing Please see [CONTRIBUTING.md][contributing]. From 1b52b4b7b7af109993af2f8ae0a50f486201c367 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 20:59:37 +0000 Subject: [PATCH 11/58] Thank Sean Bowe and Daira Hopwood in the README. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe086d6..751d043 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,8 @@ turn a port of the reference `ref10` implementation. Most of this code, including the 32-bit field arithmetic, has since been rewritten. The fast `u32` and `u64` scalar arithmetic was implemented by Andrew Moon, and -the addition chain for scalar inversion was provided by Brian Smith. +the addition chain for scalar inversion was provided by Brian Smith. The +optimised batch inversion was contributed by Sean Bowe and Daira Hopwood. The `no_std` support was contributed by Tony Arcieri. From f4669c8b4d78f217a794f671c35457065d920508 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 21:29:36 +0000 Subject: [PATCH 12/58] Move the Scalar constructor documentation to the module level. --- src/scalar.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 7e02637..2ed4bde 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -11,6 +11,28 @@ // - Brian Smith //! Arithmetic on scalars (integers mod the group order). +//! +//! Both the Ristretto group and the Ed25519 basepoint have prime order +//! \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). +//! +//! This code is intended to be useful with both the Ristretto group +//! (where everything is done modulo \\( \ell \\)), and the X/Ed25519 +//! setting, which mandates specific bit-twiddles that are not +//! well-defined modulo \\( \ell \\). +//! +//! To create a `Scalar` from a supposedly canonical encoding, use +//! `Scalar::from_canonical_bytes`. +//! +//! To create a `Scalar` by reducing a \\(256\\)-bit integer mod \\( \ell \\), +//! use `Scalar::from_bytes_mod_order`. +//! +//! To create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( \ell \\), +//! use `Scalar::from_bytes_mod_order_wide`. +//! +//! To create a `Scalar` with a specific bit-pattern (e.g., for +//! compatibility with X25519 "clamping"), use `Scalar::from_bits`. +//! +//! All arithmetic on `Scalars` is done modulo \\( \ell \\). use core::fmt::Debug; use core::ops::Neg; @@ -51,28 +73,6 @@ type UnpackedScalar = backend::u32::scalar::Scalar32; /// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which /// represents an element of \\(\mathbb Z / \ell\\). -/// -/// Both the Ristretto group and the Ed25519 basepoint have prime order -/// \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). -/// -/// The code is intended to be useful with both the Ristretto group -/// (where everything is done modulo \\( \ell \\)), and the X/Ed25519 -/// setting, which mandates specific bit-twiddles that are not -/// well-defined modulo \\( \ell \\). -/// -/// To create a `Scalar` from a supposedly canonical encoding, use -/// `Scalar::from_canonical_bytes`. -/// -/// To create a `Scalar` by reducing a \\(256\\)-bit integer mod \\( \ell \\), -/// use `Scalar::from_bytes_mod_order`. -/// -/// To create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( \ell \\), -/// use `Scalar::from_bytes_mod_order_wide`. -/// -/// To create a `Scalar` with a specific bit-pattern (e.g., for -/// compatibility with X25519 "clamping"), use `Scalar::from_bits`. -/// -/// All arithmetic on `Scalars` is done modulo \\( \ell \\). #[derive(Copy, Clone)] pub struct Scalar { /// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the group order. From b3da93b069f3e7620db10eccf61dd1b8f459094b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 21:36:57 +0000 Subject: [PATCH 13/58] Clarify README documentation on nightly-only features. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 751d043..4f09ba5 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ extern crate curve25519_dalek; The `std` feature is enabled by default, but it can be disabled. -The `nightly` feature enables nightly-only features. **It is recommended for security**. +The `nightly` feature enables features available only when using a Rust nightly +compiler. **It is recommended for security**. Curve arithmetic is implemented using one of the following backends: From 5b263dabd0e8e2c752b45217dda8bc18600350cf Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 21:43:13 +0000 Subject: [PATCH 14/58] Line wrap some docstrings in scalar.rs. --- src/scalar.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 2ed4bde..4616e67 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -75,14 +75,18 @@ type UnpackedScalar = backend::u32::scalar::Scalar32; /// represents an element of \\(\mathbb Z / \ell\\). #[derive(Copy, Clone)] pub struct Scalar { - /// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the group order. + /// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the + /// group order. /// /// # Invariant /// - /// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or equivalently the high bit of `bytes[31]` must be zero. + /// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or + /// equivalently the high bit of `bytes[31]` must be zero. /// /// This ensures that there is room for a carry bit when computing a NAF representation. - // XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead. + // + // XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could + // make the Scalar constructors const fns and use those instead. pub(crate) bytes: [u8; 32], } From 626e070896793cae2c5b997612ef4a22a558a323 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Jul 2018 23:36:18 +0000 Subject: [PATCH 15/58] Document Scalar contructors with doctests. --- src/scalar.rs | 130 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 12 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 4616e67..1004f57 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -20,19 +20,125 @@ //! setting, which mandates specific bit-twiddles that are not //! well-defined modulo \\( \ell \\). //! -//! To create a `Scalar` from a supposedly canonical encoding, use -//! `Scalar::from_canonical_bytes`. -//! -//! To create a `Scalar` by reducing a \\(256\\)-bit integer mod \\( \ell \\), -//! use `Scalar::from_bytes_mod_order`. -//! -//! To create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( \ell \\), -//! use `Scalar::from_bytes_mod_order_wide`. -//! -//! To create a `Scalar` with a specific bit-pattern (e.g., for -//! compatibility with X25519 "clamping"), use `Scalar::from_bits`. -//! //! All arithmetic on `Scalars` is done modulo \\( \ell \\). +//! +//! # Constructing a scalar +//! +//! To create a [`Scalar`](struct.Scalar.html) from a supposedly canonical encoding, use +//! [`Scalar::from_canonical_bytes`](struct.Scalar.html#method.from_canonical_bytes). +//! +//! If the bytes are a canonical encoding of a scalar mod \ell, we'll get +//! `Some(Scalar)` in return: +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let one_as_bytes: [u8; 32] = Scalar::one().to_bytes(); +//! let a: Option = Scalar::from_canonical_bytes(one_as_bytes); +//! +//! assert!(a.is_some()); +//! ``` +//! +//! However, if we give it bytes representing a scalar larger than \\( \ell \\) +//! (in this case, \\( \ell + 2 \\)), we'll get `None` back: +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let l_plus_two_bytes: [u8; 32] = [ +//! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, +//! 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +//! ]; +//! let a: Option = Scalar::from_canonical_bytes(l_plus_two_bytes); +//! +//! assert!(a.is_none()); +//! ``` +//! +//! Another way to create a `Scalar` is by reducing a \\(256\\)-bit integer mod +//! \\( \ell \\), for which one may use the +//! [`Scalar::from_bytes_mod_order`](struct.Scalar.html#method.from_bytes_mod_order) +//! method. In the case of the second example above, this would reduce the +//! resultant scalar \\( \mod \ell \\), producing \\( 2 \\): +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let l_plus_two_bytes: [u8; 32] = [ +//! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, +//! 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +//! ]; +//! let a: Scalar = Scalar::from_bytes_mod_order(l_plus_two_bytes); +//! +//! let two: Scalar = Scalar::one() + Scalar::one(); +//! +//! assert!(a == two); +//! ``` +//! +//! Similarly, to create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( +//! \ell \\), use +//! [`Scalar::from_bytes_mod_order_wide`](struct.Scalar.html#method.from_bytes_mod_order_wide). +//! This is most frequently used to produce a `Scalar` from the output of a +//! 512-bit hash function: +//! +//! ``` +//! # extern crate curve25519_dalek; +//! # extern crate digest; +//! # extern crate sha2; +//! # +//! # fn main() { +//! use curve25519_dalek::scalar::Scalar; +//! +//! use digest::Input; +//! +//! use sha2::Digest; +//! use sha2::Sha512; +//! +//! let mut hasher: Sha512 = Sha512::default(); +//! let mut hash: [u8; 64] = [0u8; 64]; +//! +//! hasher.input(b"Abolish ICE"); +//! hash.copy_from_slice(hasher.result().as_slice()); +//! +//! let a: Scalar = Scalar::from_bytes_mod_order_wide(&hash); +//! # } +//! ``` +//! +//! However, for hashes in particular, there are also the convenience methods +//! [`Scalar::from_hash`](struct.Scalar.html#method.from_hash) and +//! [`Scalar::hash_from_bytes`](struct.Scalar.html#method.hash_from_bytes). +//! +//! To create a `Scalar` with a specific bit-pattern (e.g., for compatibility +//! with X25519 +//! ["clamping"](https://github.com/isislovecruft/ed25519-dalek/blob/f790bd2ce/src/ed25519.rs#L349)), +//! use [`Scalar::from_bits`](struct.Scalar.html#method.from_bits). This +//! constructs a scalar with exactly the bit pattern given, without any +//! assurances as to reduction modulo the group order: +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let l_plus_two_bytes: [u8; 32] = [ +//! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, +//! 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +//! ]; +//! let a: Scalar = Scalar::from_bits(l_plus_two_bytes); +//! +//! let two: Scalar = Scalar::one() + Scalar::one(); +//! +//! assert!(a != two); // the scalar is not reduced (mod l)… +//! assert!(! a.is_canonical()); // …and therefore is not canonical. +//! assert!(a.reduce() == two); // if we were to reduce it manually, it would be. +//! ``` +//! +//! In particular, the bit pattern for the resulting scalar is invariant, +//! **except for the high bit, which will be unset** in order to preserve the +//! condition that scalars are 255-bit integers. use core::fmt::Debug; use core::ops::Neg; From faf860924626ec084db0f5ef64737890bdbac4a0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Jul 2018 00:14:55 +0000 Subject: [PATCH 16/58] Copy the inversions of 0 warning to the invert() method. --- src/scalar.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 1004f57..7e23699 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -525,6 +525,16 @@ impl Scalar { } /// Compute the multiplicative inverse of this scalar. + /// + /// # Warning + /// + /// All input `Scalars` **MUST** be nonzero. If you cannot + /// *prove* that this is the case, you **SHOULD NOT USE THIS + /// FUNCTION**. + /// + /// # Returns + /// + /// The multiplicative inverse of the this `Scalar`. pub fn invert(&self) -> Scalar { self.unpack().invert().pack() } From 03154d47ec1ff8f12064ed8441d71ca59106fafb Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Jul 2018 00:15:15 +0000 Subject: [PATCH 17/58] Add an example doctest for Scalar.invert(). --- src/scalar.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 7e23699..b700368 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -535,6 +535,32 @@ impl Scalar { /// # Returns /// /// The multiplicative inverse of the this `Scalar`. + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::scalar::Scalar; + /// + /// // x = 2238329342913194256032495932344128051776374960164957527413114840482143558222 + /// let X: Scalar = Scalar::from_bytes_mod_order([ + /// 0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, + /// 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52, + /// 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, + /// 0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04, + /// ]); + /// // 1/x = 6859937278830797291664592131120606308688036382723378951768035303146619657244 + /// let XINV: Scalar = Scalar::from_bytes_mod_order([ + /// 0x1c, 0xdc, 0x17, 0xfc, 0xe0, 0xe9, 0xa5, 0xbb, + /// 0xd9, 0x24, 0x7e, 0x56, 0xbb, 0x01, 0x63, 0x47, + /// 0xbb, 0xba, 0x31, 0xed, 0xd5, 0xa9, 0xbb, 0x96, + /// 0xd5, 0x0b, 0xcd, 0x7a, 0x3f, 0x96, 0x2a, 0x0f, + /// ]); + /// + /// let inv_X: Scalar = X.invert(); + /// assert!(XINV == inv_X); + /// let should_be_one: Scalar = &inv_X * &X; + /// assert!(should_be_one == Scalar::one()); + /// ``` pub fn invert(&self) -> Scalar { self.unpack().invert().pack() } From f43f4f977080b83886f0e6a8636938701b7b49d3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 5 Jul 2018 00:24:41 +0000 Subject: [PATCH 18/58] Update year in copyright notices to 2018. --- LICENSE | 2 +- src/backend/avx2/constants.rs | 2 +- src/backend/avx2/edwards.rs | 2 +- src/backend/avx2/field.rs | 2 +- src/backend/avx2/mod.rs | 2 +- src/backend/mod.rs | 2 +- src/backend/u32/constants.rs | 2 +- src/backend/u32/field.rs | 2 +- src/backend/u32/mod.rs | 2 +- src/backend/u64/constants.rs | 2 +- src/backend/u64/field.rs | 2 +- src/backend/u64/mod.rs | 2 +- src/constants.rs | 2 +- src/curve_models/mod.rs | 2 +- src/edwards.rs | 2 +- src/field.rs | 2 +- src/lib.rs | 2 +- src/macros.rs | 2 +- src/montgomery.rs | 2 +- src/ristretto.rs | 2 +- src/scalar.rs | 2 +- src/scalar_mul/window.rs | 2 +- src/traits.rs | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/LICENSE b/LICENSE index 33ed368..d94fdb5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. +Copyright (c) 2016-2018 Isis Agora Lovecruft, Henry de Valence. 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/backend/avx2/constants.rs b/src/backend/avx2/constants.rs index 304a1be..fa31c9d 100644 --- a/src/backend/avx2/constants.rs +++ b/src/backend/avx2/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 43f02f7..d9f0b01 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 175ff14..173d28e 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index b13ea1d..14a2fcb 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/mod.rs b/src/backend/mod.rs index aa44d52..d325715 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u32/constants.rs b/src/backend/u32/constants.rs index f68f662..e0e0525 100644 --- a/src/backend/u32/constants.rs +++ b/src/backend/u32/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index 8c65a46..9de460a 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u32/mod.rs b/src/backend/u32/mod.rs index bc1148e..4d6bc8b 100644 --- a/src/backend/u32/mod.rs +++ b/src/backend/u32/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 20ecb7f..0ac7fe3 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index d685ff3..25a013e 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u64/mod.rs b/src/backend/u64/mod.rs index a72dc0f..d329a89 100644 --- a/src/backend/u64/mod.rs +++ b/src/backend/u64/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/constants.rs b/src/constants.rs index fd8298c..c990b09 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index a88c37d..45c2919 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/edwards.rs b/src/edwards.rs index 5f03929..1fb4d07 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/field.rs b/src/field.rs index c9f4c49..2241c13 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/lib.rs b/src/lib.rs index 70f80f5..c28ff31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/macros.rs b/src/macros.rs index 448d32c..3ec9d77 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/montgomery.rs b/src/montgomery.rs index 0b8450d..0c21b00 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/ristretto.rs b/src/ristretto.rs index 78cd178..5581a19 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/scalar.rs b/src/scalar.rs index b700368..4f0863e 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // Portions Copyright 2017 Brian Smith // See LICENSE for licensing information. // diff --git a/src/scalar_mul/window.rs b/src/scalar_mul/window.rs index 91ebb65..c116136 100644 --- a/src/scalar_mul/window.rs +++ b/src/scalar_mul/window.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/traits.rs b/src/traits.rs index e706348..aac84d0 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: From b70b32a0c55fb8267680ad1d4cc9fc8e3f1a4568 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 5 Jul 2018 13:27:09 -0700 Subject: [PATCH 19/58] Change Scalar example to use the hasher functions --- src/scalar.rs | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 4f0863e..705034f 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -27,7 +27,9 @@ //! To create a [`Scalar`](struct.Scalar.html) from a supposedly canonical encoding, use //! [`Scalar::from_canonical_bytes`](struct.Scalar.html#method.from_canonical_bytes). //! -//! If the bytes are a canonical encoding of a scalar mod \ell, we'll get +//! This function does input validation, ensuring that the input bytes +//! are the canonical encoding of a `Scalar`. +//! If they are, we'll get //! `Some(Scalar)` in return: //! //! ``` @@ -78,41 +80,38 @@ //! assert!(a == two); //! ``` //! -//! Similarly, to create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( -//! \ell \\), use +//! There is also a constructor that reduces a \\(512\\)-bit integer, //! [`Scalar::from_bytes_mod_order_wide`](struct.Scalar.html#method.from_bytes_mod_order_wide). -//! This is most frequently used to produce a `Scalar` from the output of a -//! 512-bit hash function: +//! +//! To construct a `Scalar` as the hash of some input data, use +//! [`Scalar::hash_from_bytes`](struct.Scalar.html#method.hash_from_bytes), +//! which takes a buffer, or +//! [`Scalar::from_hash`](struct.Scalar.html#method.from_hash), +//! which allows an IUF API. //! //! ``` //! # extern crate curve25519_dalek; -//! # extern crate digest; //! # extern crate sha2; //! # //! # fn main() { +//! use sha2::{Digest, Sha512}; //! use curve25519_dalek::scalar::Scalar; //! -//! use digest::Input; +//! // Hashing a single byte slice +//! let a = Scalar::hash_from_bytes::(b"Abolish ICE"); //! -//! use sha2::Digest; -//! use sha2::Sha512; +//! // Streaming data into a hash object +//! let mut hasher = Sha512::default(); +//! hasher.input(b"Abolish "); +//! hasher.input(b"ICE"); +//! let a2 = Scalar::from_hash(hasher); //! -//! let mut hasher: Sha512 = Sha512::default(); -//! let mut hash: [u8; 64] = [0u8; 64]; -//! -//! hasher.input(b"Abolish ICE"); -//! hash.copy_from_slice(hasher.result().as_slice()); -//! -//! let a: Scalar = Scalar::from_bytes_mod_order_wide(&hash); +//! assert_eq!(a, a2); //! # } //! ``` //! -//! However, for hashes in particular, there are also the convenience methods -//! [`Scalar::from_hash`](struct.Scalar.html#method.from_hash) and -//! [`Scalar::hash_from_bytes`](struct.Scalar.html#method.hash_from_bytes). -//! -//! To create a `Scalar` with a specific bit-pattern (e.g., for compatibility -//! with X25519 +//! Finally, to create a `Scalar` with a specific bit-pattern +//! (e.g., for compatibility with X/Ed25519 //! ["clamping"](https://github.com/isislovecruft/ed25519-dalek/blob/f790bd2ce/src/ed25519.rs#L349)), //! use [`Scalar::from_bits`](struct.Scalar.html#method.from_bits). This //! constructs a scalar with exactly the bit pattern given, without any @@ -136,9 +135,8 @@ //! assert!(a.reduce() == two); // if we were to reduce it manually, it would be. //! ``` //! -//! In particular, the bit pattern for the resulting scalar is invariant, -//! **except for the high bit, which will be unset** in order to preserve the -//! condition that scalars are 255-bit integers. +//! The resulting `Scalar` has exactly the specified bit pattern, +//! **except for the highest bit, which will be set to 0**. use core::fmt::Debug; use core::ops::Neg; From 0ab60b93eec13bb42d0989d7cd1459f3a810d2eb Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 5 Jul 2018 13:34:02 -0700 Subject: [PATCH 20/58] Update wording on Scalar::invert to use self --- src/scalar.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 705034f..77704af 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -522,11 +522,11 @@ impl Scalar { Scalar{ bytes: s_bytes } } - /// Compute the multiplicative inverse of this scalar. + /// Given a nonzero `Scalar`, compute its multiplicative inverse. /// /// # Warning /// - /// All input `Scalars` **MUST** be nonzero. If you cannot + /// `self` **MUST** be nonzero. If you cannot /// *prove* that this is the case, you **SHOULD NOT USE THIS /// FUNCTION**. /// From 5b009a033e3419f833b89d34a8bda64ebde879e3 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 5 Jul 2018 13:34:14 -0700 Subject: [PATCH 21/58] Remove extra line in doctest --- src/scalar.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 77704af..945691c 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -584,7 +584,6 @@ impl Scalar { /// # extern crate curve25519_dalek; /// # use curve25519_dalek::scalar::Scalar; /// # fn main() { - /// /// let mut scalars = [ /// Scalar::from_u64(3), /// Scalar::from_u64(5), From fa904c2c420a1909044ef40b90cdde0913c1051f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 5 Jul 2018 13:39:29 -0700 Subject: [PATCH 22/58] Add note on backend selection requirement --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f09ba5..4c7353c 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,6 @@ extern crate curve25519_dalek; # Backends and Features -The `std` feature is enabled by default, but it can be disabled. - The `nightly` feature enables features available only when using a Rust nightly compiler. **It is recommended for security**. @@ -80,6 +78,11 @@ cargo build --no-default-features --features "std avx2_backend" Crates using `curve25519-dalek` can either select a backend on behalf of their users, or expose feature flags that control the `curve25519-dalek` backend. +The `std` feature is enabled by default, but it can be disabled for no-`std` +builds using `--no-default-features`. Note that this requires explicitly +selecting an arithmetic backend using one of the `_backend` features. +If no backend is selected, compilation will fail. + Benchmarks are run using [`criterion.rs`][criterion]: ```sh From 37935674eb5614d2e2ff943f164f193108905312 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 6 Jul 2018 00:10:21 +0000 Subject: [PATCH 23/58] Add doctest for Scalar::random(). --- src/scalar.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 945691c..d4c921e 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -436,6 +436,21 @@ impl Scalar { /// # Returns /// /// A random scalar within ℤ/lℤ. + /// + /// # Example + /// + /// ``` + /// extern crate rand; + /// # extern crate curve25519_dalek; + /// # + /// # fn main() { + /// use curve25519_dalek::scalar::Scalar; + /// + /// use rand::OsRng; + /// + /// let mut csprng: OsRng = OsRng::new().unwrap(); + /// let a: Scalar = Scalar::random(&mut csprng); + /// # } #[cfg(feature = "std")] pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; From 3854eb0fd84e9d3e4926ee238016aefbfa6a8222 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 6 Jul 2018 00:10:41 +0000 Subject: [PATCH 24/58] Remove extra line and unneeded XXX comment from Scalar::hash_from_bytes. --- src/scalar.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index d4c921e..f490948 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -480,7 +480,6 @@ impl Scalar { /// let s = Scalar::hash_from_bytes::(msg.as_bytes()); /// # } /// ``` - /// pub fn hash_from_bytes(input: &[u8]) -> Scalar where D: Digest + Default { @@ -497,7 +496,6 @@ impl Scalar { pub fn from_hash(hash: D) -> Scalar where D: Digest + Default { - // XXX this seems clumsy let mut output = [0u8; 64]; output.copy_from_slice(hash.result().as_slice()); Scalar::from_bytes_mod_order_wide(&output) From 61daa9dce6c2c5011b63db3e6849d5ad73b2e6d5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 6 Jul 2018 00:12:35 +0000 Subject: [PATCH 25/58] Add a doctest for Scalar::from_u64(). --- src/scalar.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index f490948..328af05 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -527,6 +527,26 @@ impl Scalar { } /// Construct a scalar from the given `u64`. + /// + /// # Inputs + /// + /// An `u64` to convert to a `Scalar`. + /// + /// # Returns + /// + /// A `Scalar` corresponding to the input `u64`. + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::scalar::Scalar; + /// + /// let fourtytwo = Scalar::from_u64(42); + /// let six = Scalar::from_u64(6); + /// let seven = Scalar::from_u64(7); + /// + /// assert!(fourtytwo == six * seven); + /// ``` pub fn from_u64(x: u64) -> Scalar { let mut s_bytes = [0u8; 32]; for i in 0..8 { From ff16e93102590ba87f56d6e022745b1a26bfe3e6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 17 Jul 2018 00:21:37 +0000 Subject: [PATCH 26/58] Add doctest for Scalar::from_hash(). --- src/lib.rs | 3 +++ src/scalar.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c28ff31..97fbb81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,9 @@ extern crate core; #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(test)] +extern crate sha2; + extern crate rand; extern crate clear_on_drop; extern crate byteorder; diff --git a/src/scalar.rs b/src/scalar.rs index 328af05..3219bfc 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -471,6 +471,7 @@ impl Scalar { /// # extern crate curve25519_dalek; /// # use curve25519_dalek::scalar::Scalar; /// extern crate sha2; + /// /// use sha2::Sha512; /// /// # // Need fn main() here in comment so the doctest compiles @@ -493,6 +494,36 @@ impl Scalar { /// Use this instead of `hash_from_bytes` if it is more convenient /// to stream data into the `Digest` than to pass a single byte /// slice. + /// + /// # Example + /// + /// ``` + /// # extern crate curve25519_dalek; + /// # use curve25519_dalek::scalar::Scalar; + /// extern crate sha2; + /// + /// use sha2::Digest; + /// use sha2::Sha512; + /// + /// # fn main() { + /// let mut h = Sha512::default(); + /// + /// h.input(b"To really appreciate architecture, you may even need to commit a murder."); + /// h.input(b"While the programs used for The Manhattan Transcripts are of the most extreme"); + /// h.input(b"nature, they also parallel the most common formula plot: the archetype of"); + /// h.input(b"murder. Other phantasms were occasionally used to underline the fact that"); + /// h.input(b"perhaps all architecture, rather than being about functional standards, is"); + /// h.input(b"about love and death."); + /// + /// let s = Scalar::from_hash(h); + /// + /// println!("{:?}", s.to_bytes()); + /// assert!(s == Scalar::from_bits([ 21, 88, 208, 252, 63, 122, 210, 152, + /// 154, 38, 15, 23, 16, 167, 80, 150, + /// 192, 221, 77, 226, 62, 25, 224, 148, + /// 239, 48, 176, 10, 185, 69, 168, 11, ])); + /// # } + /// ``` pub fn from_hash(hash: D) -> Scalar where D: Digest + Default { From 74a28559c4acb6fc6bf8d0089d3d628377c42a1c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 17 Jul 2018 00:22:34 +0000 Subject: [PATCH 27/58] Add example code for Scalar.to_bytes() and Scalar.as_bytes(). --- src/scalar.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 3219bfc..4e9286d 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -533,11 +533,31 @@ impl Scalar { } /// Convert this `Scalar` to its underlying sequence of bytes. + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::scalar::Scalar; + /// + /// let s: Scalar = Scalar::zero(); + /// + /// assert!(s.to_bytes() == [0u8; 32]); + /// ``` pub fn to_bytes(&self) -> [u8; 32] { self.bytes } - /// View this `Scalar` as a sequence of bytes. + /// View this `Scalar` as its underlying sequence of bytes. + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::scalar::Scalar; + /// + /// let s: Scalar = Scalar::zero(); + /// + /// assert!(s.as_bytes() == &[0u8; 32]); + /// ``` pub fn as_bytes(&self) -> &[u8; 32] { &self.bytes } From 46c98224f58333441fc945e6fbf115ce8e7c5d73 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 17 Jul 2018 00:28:04 +0000 Subject: [PATCH 28/58] Remove erroneous and extraneous alloc import from edwards module. The "alloc" feature doesn't compile otherwise. * FIXES #160. --- src/edwards.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 1fb4d07..7e15fce 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -90,9 +90,6 @@ // affine and projective cakes and eat both of them too. #![allow(non_snake_case)] -#[cfg(feature = "alloc")] -use alloc::Vec; - use core::fmt::Debug; use core::iter::Iterator; use core::ops::{Add, Sub, Neg}; From ca8c46220b3410ced03e7f190b8c50584531aa7d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 12 Jul 2018 12:44:56 -0700 Subject: [PATCH 29/58] Add safety notes to the README --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4c7353c..050c976 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,14 @@ Curve arithmetic is implemented using one of the following backends: * a `u32` backend using `u64` products; * a `u64` backend using `u128` products; -* an `avx2` backend using parallel formulas, available when compiling for a - target with `target_feature=+avx2`. +* an `avx2` backend using [parallel formulas][parallel_doc], available + when compiling for a target with `target_feature=+avx2`. By default the `u64` backend is selected. To select a specific backend, use: ```sh cargo build --no-default-features --features "std u32_backend" cargo build --no-default-features --features "std u64_backend" +# Requires RUSTFLAGS="-C target_feature=+avx2" cargo build --no-default-features --features "std avx2_backend" ``` Crates using `curve25519-dalek` can either select a backend on behalf of their @@ -83,6 +84,50 @@ builds using `--no-default-features`. Note that this requires explicitly selecting an arithmetic backend using one of the `_backend` features. If no backend is selected, compilation will fail. +The `yolocrypto` feature enables experimental features. The name `yolocrypto` +is meant to indicate that it is not considered production-ready, and we do not +consider `yolocrypto` features to be covered by semver guarantees. +This is designed to make it easier to test intended new features +without having to stabilise them first. Use `yolocrypto` at your own, +obvious, risk. + +# Safety + +The `curve25519-dalek` types are designed to make illegal states +unrepresentable. For example, any instance of an `EdwardsPoint` is +guaranteed to hold a point on the Edwards curve, and any instance of a +`RistrettoPoint` is guaranteed to hold a valid point in the Ristretto +group. + +All operations are implemented using constant-time logic (no +secret-dependent branches, no secret-dependent memory accesses), +unless specifically marked as being variable-time code. +When using the `nightly` feature, we also insert an optimization +barrier before every conditional move or assignment. + +Some functionality (e.g., multiscalar multiplication or batch +inversion) requires heap allocation for temporary buffers. **All +heap-allocated buffers of potentially secret data are explicitly +zeroed before release**. + +However, we do not attempt to zero stack data, for two reasons. +First, it's not possible to do so correctly: we don't have control +over stack allocations, so there's no way to know how much data to +wipe. Second, because `curve25519-dalek` provides a mid-level API, +the correct place to start zeroing stack data is likely not at the +entrypoints of `curve25519-dalek` functions, but at the entrypoints of +functions in other crates. + +The implementation is memory-safe, and contains no significant +`unsafe` code. The AVX2 backend uses `unsafe` internally to call AVX2 +intrinsics. These are marked `unsafe` because invoking them on a +non-AVX2 target would cause `SIGILL`, but the entire backend is only +compiled for `target_feature=+avx2`. Some types implement an `unsafe +trait` to mark them as zeroable (for heap allocations), but this does +not affect memory safety. + +# Performance + Benchmarks are run using [`criterion.rs`][criterion]: ```sh @@ -93,12 +138,8 @@ cargo bench --no-default-features --features "std u64_backend" cargo bench --no-default-features --features "std avx2_backend" ``` -The `yolocrypto` feature enables experimental features. The name `yolocrypto` -is meant to indicate that it is not considered production-ready, and we do not -consider `yolocrypto` features to be covered by semver guarantees. -This is designed to make it easier to test intended new features -without having to stabilise them first. Use `yolocrypto` at your own, -obvious, risk. +Performance is a secondary goal behind correctness, safety, and +clarity, but we aim to be competitive with other implementations. # Contributing @@ -144,3 +185,4 @@ contributions. [docs-external]: https://doc.dalek.rs/curve25519_dalek/ [docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/ [criterion]: https://github.com/japaric/criterion.rs +[parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/avx2/index.html From 8d46eacd2cea9f248ee603d67237c9d1f96257c2 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 14:30:14 -0700 Subject: [PATCH 30/58] Clarify wording on the nightly feature and CT Closes #147 --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 050c976..f241f81 100644 --- a/README.md +++ b/README.md @@ -102,13 +102,19 @@ group. All operations are implemented using constant-time logic (no secret-dependent branches, no secret-dependent memory accesses), unless specifically marked as being variable-time code. -When using the `nightly` feature, we also insert an optimization -barrier before every conditional move or assignment. +We believe that our constant-time logic is lowered to constant-time +assembly, at least on `x86_64` targets. + +As an additional guard against possible future compiler optimizations, the +`nightly` feature places an optimization barrier before every +conditional move or assignment. More details can be found in [the +documentation for the `subtle` crate][subtle_doc]. This is +recommended, but not required. Some functionality (e.g., multiscalar multiplication or batch -inversion) requires heap allocation for temporary buffers. **All +inversion) requires heap allocation for temporary buffers. All heap-allocated buffers of potentially secret data are explicitly -zeroed before release**. +zeroed before release. However, we do not attempt to zero stack data, for two reasons. First, it's not possible to do so correctly: we don't have control @@ -186,3 +192,4 @@ contributions. [docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/ [criterion]: https://github.com/japaric/criterion.rs [parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/avx2/index.html +[subtle_doc]: https://doc.dalek.rs/subtle/ From bc731f9d79100743b1995286b111564ec8df2f9f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:11:48 -0700 Subject: [PATCH 31/58] Remove fixme notes from FieldElement code --- src/field.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/field.rs b/src/field.rs index 2241c13..9d8d041 100644 --- a/src/field.rs +++ b/src/field.rs @@ -96,17 +96,10 @@ impl FieldElement { /// Compute (self^(2^250-1), self^11), used as a helper function /// within invert() and pow22523(). - /// - /// XXX This returns an extra intermediate to save computation in - /// finding inverses, at the cost of an extra copy when it's not - /// used (e.g., when raising to (p-1)/2 or (p-5)/8). Good idea? fn pow22501(&self) -> (FieldElement, FieldElement) { // Instead of managing which temporary variables are used - // for what, we define as many as we need and trust the - // compiler to reuse stack space as appropriate. - // - // XXX testing some examples suggests that this does happen, - // but it would be good to check asm for this function. + // for what, we define as many as we need and leave stack + // allocation to the compiler // // Each temporary variable t_i is of the form (self)^e_i. // Squaring t_i corresponds to multiplying e_i by 2, @@ -177,12 +170,9 @@ impl FieldElement { /// /// The inverse is computed as self^(p-2), since /// x^(p-2)x = x^(p-1) = 1 (mod p). - // - // XXX do we want the debug assertion to check for zero? it breaks behaviour - // such as that such as in curve25519_dalek::montgomery::test::identity_to_monty. + /// + /// This function returns zero on input zero. pub fn invert(&self) -> FieldElement { - // debug_assert!(*self != FieldElement::zero()); - // The bits of p-2 = 2^255 -19 -2 are 11010111111...11. // // nonzero bits of exponent @@ -194,8 +184,7 @@ impl FieldElement { } /// Raise this field element to the power (p-5)/8 = 2^252 -3. - /// Used in decoding. - pub fn pow_p58(&self) -> FieldElement { + fn pow_p58(&self) -> FieldElement { // The bits of (p-5)/8 are 101111.....11. // // nonzero bits of exponent From 5bb6cd42a21f40979e84238cf185c458531bd30b Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:13:40 -0700 Subject: [PATCH 32/58] we won't remove this function --- src/edwards.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 7e15fce..63c8689 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -583,8 +583,6 @@ impl VartimeMultiscalarMul for EdwardsPoint { impl EdwardsPoint { /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. - /// - /// XXX eliminate this function when we have the precomputation API #[cfg(feature = "stage2_build")] pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. From f7f3f79da89ec8b08d58718b4df860a4c3314db8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:22:21 -0700 Subject: [PATCH 33/58] Add missing Ristretto vartime-double-base fn --- src/edwards.rs | 14 +++++--------- src/ristretto.rs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 63c8689..9930d84 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -587,16 +587,12 @@ impl EdwardsPoint { pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] - { - use backend::avx2::scalar_mul::vartime_double_base::mul; - mul(a, A, b) - } - // Otherwise, proceed as normal: + use backend::avx2::scalar_mul::vartime_double_base; + // Otherwise, use the serial backend: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] - { - use scalar_mul::vartime_double_base::mul; - mul(a, A, b) - } + use scalar_mul::vartime_double_base; + + vartime_double_base::mul(a, A, b) } } diff --git a/src/ristretto.rs b/src/ristretto.rs index 5581a19..cb4e8e0 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -830,6 +830,21 @@ impl VartimeMultiscalarMul for RistrettoPoint { } } +impl RistrettoPoint { + /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the + /// Ristretto basepoint. + #[cfg(feature = "stage2_build")] + pub fn vartime_double_scalar_mul_basepoint( + a: &Scalar, + A: &RistrettoPoint, + b: &Scalar, + ) -> RistrettoPoint { + RistrettoPoint( + EdwardsPoint::vartime_double_scalar_mul_basepoint(a, &A.0, b) + ) + } +} + /// A precomputed table of multiples of a basepoint, used to accelerate /// scalar multiplication. /// From 0c58de036787a34c1db791ee3c84fd8e292ada3f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:22:58 -0700 Subject: [PATCH 34/58] it wouldn't be --- src/edwards.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 9930d84..df2f8c8 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -689,8 +689,6 @@ impl EdwardsBasepointTable { } /// Get the basepoint for this table as an `EdwardsPoint`. - /// - /// XXX maybe this would be better as a `From` impl pub fn basepoint(&self) -> EdwardsPoint { // self.0[0].select(1) = 1*(16^2)^0*B // but as an `AffineNielsPoint`, so add identity to convert to extended. From dfc9e7c0b7391e47580fa1f9f24093c8cbb4f5c8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:28:22 -0700 Subject: [PATCH 35/58] fixup extendedpoint validity check --- src/edwards.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index df2f8c8..08eff32 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -271,9 +271,11 @@ impl Identity for EdwardsPoint { // ------------------------------------------------------------------------ impl ValidityCheck for EdwardsPoint { - // XXX this should also check that T is correct fn is_valid(&self) -> bool { - self.to_projective().is_valid() + let point_on_curve = self.to_projective().is_valid(); + let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T); + + point_on_curve && on_segre_image } } From 7bbf7495b0440446fec212a3cbd9144d60cb56f1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 16 Jul 2018 22:54:45 -0700 Subject: [PATCH 36/58] Change VartimeMultiscalarMul docs to use vartime_ --- src/traits.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index aac84d0..42530b2 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -106,11 +106,12 @@ pub trait VartimeMultiscalarMul { /// The type of point being multiplied, e.g., `RistrettoPoint`. type Point; - /// Given an iterator of (possibly secret) scalars and an iterator of + /// Given an iterator of public scalars and an iterator of /// public points, compute /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n, /// $$ + /// using variable-time operations. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -123,7 +124,7 @@ pub trait VartimeMultiscalarMul { /// /// ``` /// use curve25519_dalek::constants; - /// use curve25519_dalek::traits::MultiscalarMul; + /// use curve25519_dalek::traits::VartimeMultiscalarMul; /// use curve25519_dalek::ristretto::RistrettoPoint; /// use curve25519_dalek::scalar::Scalar; /// @@ -139,12 +140,12 @@ pub trait VartimeMultiscalarMul { /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; - /// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]); + /// let A1 = RistrettoPoint::vartime_multiscalar_mul(&abc, &[P,Q,R]); /// // Note: (&abc).into_iter(): Iterator /// /// // A2 = (-a)*P + (-b)*Q + (-c)*R /// let minus_abc = abc.iter().map(|x| -x); - /// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]); + /// let A2 = RistrettoPoint::vartime_multiscalar_mul(minus_abc, &[P,Q,R]); /// // Note: minus_abc.into_iter(): Iterator /// /// assert_eq!(A1.compress(), (-A2).compress()); From b4db0afe18d59523c962a915b03e365e379bf9e7 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 17 Jul 2018 08:19:48 -0700 Subject: [PATCH 37/58] Allow Options in the VartimeMultiscalarMul trait This changes the primary function for the `VartimeMultiscalarMul` trait to an `optional_multiscalar_mul` trait that accepts `Option` (and returns `None` if any input points are `None`). The existing `vartime_multiscalar_mul` is changed to be a wrapper around this function to avoid code duplication. This may result in an extra copy of each input point, but that cost is probably not significant compared to the cost of the multiscalar multiplication. The motivation is to allow performing multiscalar multiplications with inline decompression. Currently, API consumers have to allocate temporary buffers for all of their points, decompress into those buffers, then pass (iterators over) those buffers into the multiscalar multiplication code, which then creates new buffers for lookup tables. --- src/edwards.rs | 9 +++--- src/ristretto.rs | 13 ++++---- src/scalar_mul/straus.rs | 18 ++++++----- src/traits.rs | 68 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 7e15fce..7353da1 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -556,12 +556,11 @@ impl MultiscalarMul for EdwardsPoint { impl VartimeMultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; - fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { // XXX later when we do more fancy multiscalar mults, we can // delegate based on the iter's size hint -- hdevalence @@ -570,13 +569,13 @@ impl VartimeMultiscalarMul for EdwardsPoint { #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::straus::Straus; - Straus::vartime_multiscalar_mul(scalars, points) + Straus::optional_multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::straus::Straus; - Straus::vartime_multiscalar_mul(scalars, points) + Straus::optional_multiscalar_mul(scalars, points) } } } diff --git a/src/ristretto.rs b/src/ristretto.rs index 5581a19..06d977f 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -816,17 +816,16 @@ impl MultiscalarMul for RistrettoPoint { impl VartimeMultiscalarMul for RistrettoPoint { type Point = RistrettoPoint; - fn vartime_multiscalar_mul(scalars: I, points: J) -> RistrettoPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { - let extended_points = points.into_iter().map(|P| P.borrow().0); - RistrettoPoint( - EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points) - ) + let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.borrow().0)); + + EdwardsPoint::optional_multiscalar_mul(scalars, extended_points) + .map(|P| RistrettoPoint(P)) } } diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index 21bf29e..cfa563f 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -152,12 +152,11 @@ impl VartimeMultiscalarMul for Straus { /// The non-adjacent form has signed, odd digits. Using only odd /// digits halves the table size (since we only need odd /// multiples), or gives fewer additions for the same table size. - fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint}; use scalar_mul::window::NafLookupTable5; @@ -167,10 +166,15 @@ impl VartimeMultiscalarMul for Straus { .into_iter() .map(|c| c.borrow().non_adjacent_form(5)) .collect(); - let lookup_tables: Vec<_> = points + + let lookup_tables = match points .into_iter() - .map(|P| NafLookupTable5::::from(P.borrow())) - .collect(); + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>() + { + Some(x) => x, + None => return None, + }; let mut r = ProjectivePoint::identity(); @@ -188,6 +192,6 @@ impl VartimeMultiscalarMul for Straus { r = t.to_projective(); } - r.to_extended() + Some(r.to_extended()) } } diff --git a/src/traits.rs b/src/traits.rs index 42530b2..2ed295a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -106,6 +106,64 @@ pub trait VartimeMultiscalarMul { /// The type of point being multiplied, e.g., `RistrettoPoint`. type Point; + /// Given an iterator of public scalars and an iterator of + /// `Option`s of points, compute either `Some(Q)`, where + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n, + /// $$ + /// if all points were `Some(P_i)`, or else return `None`. + /// + /// This function is particularly useful when verifying statements + /// involving compressed points. Accepting `Option` allows + /// inlining point decompression into the multiscalar call, + /// avoiding the need for temporary buffers. + /// ``` + /// use curve25519_dalek::constants; + /// use curve25519_dalek::traits::VartimeMultiscalarMul; + /// use curve25519_dalek::ristretto::RistrettoPoint; + /// use curve25519_dalek::scalar::Scalar; + /// + /// // Some scalars + /// let a = Scalar::from_u64(87329482); + /// let b = Scalar::from_u64(37264829); + /// let c = Scalar::from_u64(98098098); + /// let abc = [a,b,c]; + /// + /// // Some points + /// let P = constants::RISTRETTO_BASEPOINT_POINT; + /// let Q = P + P; + /// let R = P + Q; + /// let PQR = [P, Q, R]; + /// + /// let compressed = [P.compress(), Q.compress(), R.compress()]; + /// + /// // Now we can compute A1 = a*P + b*Q + c*R using P, Q, R: + /// let A1 = RistrettoPoint::vartime_multiscalar_mul(&abc, &PQR); + /// + /// // Or using the compressed points: + /// let A2 = RistrettoPoint::optional_multiscalar_mul( + /// &abc, + /// compressed.iter().map(|pt| pt.decompress()), + /// ); + /// + /// assert_eq!(A2, Some(A1)); + /// + /// // It's also possible to mix compressed and uncompressed points: + /// let A3 = RistrettoPoint::optional_multiscalar_mul( + /// abc.iter() + /// .chain(abc.iter()), + /// compressed.iter().map(|pt| pt.decompress()) + /// .chain(PQR.iter().map(|&pt| Some(pt))), + /// ); + /// + /// assert_eq!(A3, Some(A1+A1)); + /// ``` + fn optional_multiscalar_mul(scalars: I, points: J) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator>; + /// Given an iterator of public scalars and an iterator of /// public points, compute /// $$ @@ -150,12 +208,20 @@ pub trait VartimeMultiscalarMul { /// /// assert_eq!(A1.compress(), (-A2).compress()); /// ``` + #[allow(non_snake_case)] fn vartime_multiscalar_mul(scalars: I, points: J) -> Self::Point where I: IntoIterator, I::Item: Borrow, J: IntoIterator, - J::Item: Borrow; + J::Item: Borrow, + Self::Point: Clone, + { + Self::optional_multiscalar_mul( + scalars, + points.into_iter().map(|P| Some(P.borrow().clone())) + ).unwrap() + } } // ------------------------------------------------------------------------ From 1e74cb3e56c1b2dba7d72b5bf8ed69c094417e0b Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 17 Jul 2018 10:53:02 -0700 Subject: [PATCH 38/58] Replace Scalar::from_u64 with From impls Unfortunately, Rust selects `i32` as the type for an integer literal when the literal has no other type constraints. This means that someone cannot write `Scalar::from(1)`, as Rust will choose `i32` as the type for `1`, and we don't `impl From for Scalar`. We could implement `From` conversions for signed integers, but since `Scalar` operations should be constant-time by default, this would require us to extract the sign bit of the integer and use it to conditionally select between the positive and negative of Scalar constructed from the value bits. This is more expensive than the unsigned operation, and I don't think it's what anyone really wants. Making API consumers specify that their literals are unsigned is slightly annoying, but better than the above alternative. It would also be nice to change `Scalar::from_hash` to be `impl> From for Scalar`, but this isn't currently allowed by Rust (since that `impl` "could" conflict with the `impl From` if someone decided that `u8` should `impl Digest`). --- Cargo.toml | 4 +- benches/dalek_benchmarks.rs | 12 ++--- src/backend/avx2/edwards.rs | 4 +- src/edwards.rs | 8 +-- src/ristretto.rs | 10 ++-- src/scalar.rs | 103 ++++++++++++++++++++++++------------ src/traits.rs | 12 ++--- 7 files changed, 94 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 052b9cc..32d5783 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ harness = false [dependencies] rand = { version = "0.5", default-features = false } -byteorder = { version = "1", default-features = false } +byteorder = { version = "1", default-features = false, features = ["i128"] } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" @@ -51,7 +51,7 @@ serde = { version = "1.0", optional = true } [build-dependencies] rand = { version = "0.5", default-features = false } -byteorder = { version = "1", default-features = false } +byteorder = { version = "1", default-features = false, features = ["i128"] } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 75017cf..71792a3 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -39,7 +39,7 @@ mod edwards_benches { fn consttime_fixed_base_scalar_mul(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_TABLE; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time fixed-base scalar mul", move |b| { b.iter(|| B * &s) }); @@ -47,7 +47,7 @@ mod edwards_benches { fn consttime_variable_base_scalar_mul(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_POINT; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time variable-base scalar mul", move |b| { b.iter(|| B * &s) }); @@ -56,8 +56,8 @@ mod edwards_benches { fn vartime_double_base_scalar_mul(c: &mut Criterion) { c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| { let B = &constants::ED25519_BASEPOINT_POINT; - let a = Scalar::from_u64(298374928).invert(); - let b = Scalar::from_u64(897987897).invert(); + let a = Scalar::from(298374928u64).invert(); + let b = Scalar::from(897987897u64).invert(); let A = B * (b * a); bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b)); }); @@ -157,7 +157,7 @@ mod montgomery_benches { fn montgomery_ladder(c: &mut Criterion) { c.bench_function("Montgomery pseudomultiplication", |b| { let B = constants::X25519_BASEPOINT; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); b.iter(|| B * s); }); } @@ -174,7 +174,7 @@ mod scalar_benches { fn scalar_inversion(c: &mut Criterion) { c.bench_function("Scalar inversion", |b| { - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); b.iter(|| s.invert()); }); } diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index d9f0b01..1510bda 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -431,7 +431,7 @@ mod test { println!("Testing B +- kB"); let P = constants::ED25519_BASEPOINT_POINT; - let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); + let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64); addition_test_helper(P, Q); } @@ -510,7 +510,7 @@ mod test { doubling_test_helper(P); println!("Testing [2]([k]B)"); - let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); + let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64); doubling_test_helper(P); } } diff --git a/src/edwards.rs b/src/edwards.rs index 7e15fce..2246d6f 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1004,7 +1004,7 @@ mod test { /// Test that computing 2*basepoint is the same as basepoint.double() #[test] fn basepoint_mult_two_vs_basepoint2() { - let two = Scalar::from_u64(2); + let two = Scalar::from(2u64); let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two; assert_eq!(bp2.compress(), BASE2_CMPRSSD); } @@ -1030,10 +1030,10 @@ mod test { // Test that sum works for non-empty iterators let BASE = constants::ED25519_BASEPOINT_POINT; - let s1 = Scalar::from_u64(999); + let s1 = Scalar::from(999u64); let P1 = &BASE * &s1; - let s2 = Scalar::from_u64(333); + let s2 = Scalar::from(333u64); let P2 = &BASE * &s2; let vec = vec![P1.clone(), P2.clone()]; @@ -1048,7 +1048,7 @@ mod test { assert_eq!(sum, EdwardsPoint::identity()); // Test that sum works on owning iterators - let s = Scalar::from_u64(2); + let s = Scalar::from(2u64); let mapped = vec.iter().map(|x| x * &s); let sum: EdwardsPoint = mapped.sum(); diff --git a/src/ristretto.rs b/src/ristretto.rs index 5581a19..6124f92 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -839,7 +839,7 @@ impl VartimeMultiscalarMul for RistrettoPoint { /// use curve25519_dalek::constants; /// use curve25519_dalek::scalar::Scalar; /// -/// let a = Scalar::from_u64(87329482); +/// let a = Scalar::from(87329482u64); /// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE; /// ``` #[derive(Clone)] @@ -959,7 +959,7 @@ mod test { #[test] fn scalarmult_ristrettopoint_works_both_ways() { let P = constants::RISTRETTO_BASEPOINT_POINT; - let s = Scalar::from_u64(999); + let s = Scalar::from(999u64); let P1 = &P * &s; let P2 = &s * &P; @@ -973,10 +973,10 @@ mod test { // Test that sum works for non-empty iterators let BASE = constants::RISTRETTO_BASEPOINT_POINT; - let s1 = Scalar::from_u64(999); + let s1 = Scalar::from(999u64); let P1 = &BASE * &s1; - let s2 = Scalar::from_u64(333); + let s2 = Scalar::from(333u64); let P2 = &BASE * &s2; let vec = vec![P1.clone(), P2.clone()]; @@ -991,7 +991,7 @@ mod test { assert_eq!(sum, RistrettoPoint::identity()); // Test that sum works on owning iterators - let s = Scalar::from_u64(2); + let s = Scalar::from(2u64); let mapped = vec.iter().map(|x| x * &s); let sum: RistrettoPoint = mapped.sum(); diff --git a/src/scalar.rs b/src/scalar.rs index 945691c..36045b0 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -426,6 +426,50 @@ where } } +impl From for Scalar { + fn from(x: u8) -> Scalar { + let mut s_bytes = [0u8; 32]; + s_bytes[0] = x; + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u16) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u16(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u32) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u32(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u64) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u64(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u128) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u128(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + impl Scalar { /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// @@ -513,15 +557,6 @@ impl Scalar { } } - /// Construct a scalar from the given `u64`. - pub fn from_u64(x: u64) -> Scalar { - let mut s_bytes = [0u8; 32]; - for i in 0..8 { - s_bytes[i] = (x >> (i*8)) as u8; - } - Scalar{ bytes: s_bytes } - } - /// Given a nonzero `Scalar`, compute its multiplicative inverse. /// /// # Warning @@ -585,19 +620,19 @@ impl Scalar { /// # use curve25519_dalek::scalar::Scalar; /// # fn main() { /// let mut scalars = [ - /// Scalar::from_u64(3), - /// Scalar::from_u64(5), - /// Scalar::from_u64(7), - /// Scalar::from_u64(11), + /// Scalar::from(3u64), + /// Scalar::from(5u64), + /// Scalar::from(7u64), + /// Scalar::from(11u64), /// ]; /// /// let allinv = Scalar::batch_invert(&mut scalars); /// - /// assert_eq!(allinv, Scalar::from_u64(3*5*7*11).invert()); - /// assert_eq!(scalars[0], Scalar::from_u64(3).invert()); - /// assert_eq!(scalars[1], Scalar::from_u64(5).invert()); - /// assert_eq!(scalars[2], Scalar::from_u64(7).invert()); - /// assert_eq!(scalars[3], Scalar::from_u64(11).invert()); + /// assert_eq!(allinv, Scalar::from(3*5*7*11u64).invert()); + /// assert_eq!(scalars[0], Scalar::from(3u64).invert()); + /// assert_eq!(scalars[1], Scalar::from(5u64).invert()); + /// assert_eq!(scalars[2], Scalar::from(7u64).invert()); + /// assert_eq!(scalars[3], Scalar::from(11u64).invert()); /// # } /// ``` #[cfg(any(feature = "alloc", feature = "std"))] @@ -1050,9 +1085,9 @@ mod test { } #[test] - fn from_unsigned() { - let val = 0xdeadbeefdeadbeef; - let s = Scalar::from_u64(val); + fn from_u64() { + let val: u64 = 0xdeadbeefdeadbeef; + let s = Scalar::from(val); assert_eq!(s[7], 0xde); assert_eq!(s[6], 0xad); assert_eq!(s[5], 0xbe); @@ -1073,7 +1108,7 @@ mod test { #[test] fn impl_add() { - let two = Scalar::from_u64(2); + let two = Scalar::from(2u64); let one = Scalar::one(); let should_be_two = &one + &one; assert_eq!(should_be_two, two); @@ -1101,8 +1136,8 @@ mod test { assert_eq!(should_be_one, one); // Test that product works for iterators where Item = Scalar - let xs = [Scalar::from_u64(2); 10]; - let ys = [Scalar::from_u64(3); 10]; + let xs = [Scalar::from(2u64); 10]; + let ys = [Scalar::from(3u64); 10]; // now zs is an iterator with Item = Scalar let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x * y); @@ -1110,9 +1145,9 @@ mod test { let y_prod: Scalar = ys.iter().product(); let z_prod: Scalar = zs.product(); - assert_eq!(x_prod, Scalar::from_u64(1024)); - assert_eq!(y_prod, Scalar::from_u64(59049)); - assert_eq!(z_prod, Scalar::from_u64(60466176)); + assert_eq!(x_prod, Scalar::from(1024u64)); + assert_eq!(y_prod, Scalar::from(59049u64)); + assert_eq!(z_prod, Scalar::from(60466176u64)); assert_eq!(x_prod * y_prod, z_prod); } @@ -1121,7 +1156,7 @@ mod test { fn impl_sum() { // Test that sum works for non-empty iterators - let two = Scalar::from_u64(2); + let two = Scalar::from(2u64); let one_vector = vec![Scalar::one(), Scalar::one()]; let should_be_two: Scalar = one_vector.iter().sum(); assert_eq!(should_be_two, two); @@ -1133,8 +1168,8 @@ mod test { assert_eq!(should_be_zero, zero); // Test that sum works for owned types - let xs = [Scalar::from_u64(1); 10]; - let ys = [Scalar::from_u64(2); 10]; + let xs = [Scalar::from(1u64); 10]; + let ys = [Scalar::from(2u64); 10]; // now zs is an iterator with Item = Scalar let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x + y); @@ -1142,9 +1177,9 @@ mod test { let y_sum: Scalar = ys.iter().sum(); let z_sum: Scalar = zs.sum(); - assert_eq!(x_sum, Scalar::from_u64(10)); - assert_eq!(y_sum, Scalar::from_u64(20)); - assert_eq!(z_sum, Scalar::from_u64(30)); + assert_eq!(x_sum, Scalar::from(10u64)); + assert_eq!(y_sum, Scalar::from(20u64)); + assert_eq!(z_sum, Scalar::from(30u64)); assert_eq!(x_sum + y_sum, z_sum); } @@ -1296,7 +1331,7 @@ mod test { #[test] fn batch_invert_consistency() { - let mut x = Scalar::from_u64(1); + let mut x = Scalar::from(1u64); let mut v1: Vec<_> = (0..16).map(|_| {let tmp = x; x = x + x; tmp}).collect(); let v2 = v1.clone(); diff --git a/src/traits.rs b/src/traits.rs index aac84d0..5e8ae05 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -72,9 +72,9 @@ pub trait MultiscalarMul { /// use curve25519_dalek::scalar::Scalar; /// /// // Some scalars - /// let a = Scalar::from_u64(87329482); - /// let b = Scalar::from_u64(37264829); - /// let c = Scalar::from_u64(98098098); + /// let a = Scalar::from(87329482u64); + /// let b = Scalar::from(37264829u64); + /// let c = Scalar::from(98098098u64); /// /// // Some points /// let P = constants::RISTRETTO_BASEPOINT_POINT; @@ -128,9 +128,9 @@ pub trait VartimeMultiscalarMul { /// use curve25519_dalek::scalar::Scalar; /// /// // Some scalars - /// let a = Scalar::from_u64(87329482); - /// let b = Scalar::from_u64(37264829); - /// let c = Scalar::from_u64(98098098); + /// let a = Scalar::from(87329482u64); + /// let b = Scalar::from(37264829u64); + /// let c = Scalar::from(98098098u64); /// /// // Some points /// let P = constants::RISTRETTO_BASEPOINT_POINT; From 133afff5a77bd8b5405f815b138798a586a25b55 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 19 Jul 2018 23:50:00 +0000 Subject: [PATCH 39/58] Feature gate some uses on alloc/std which aren't used in nostd. * FIXES part of #166. --- src/edwards.rs | 3 +++ src/ristretto.rs | 4 +++- src/scalar_mul/straus.rs | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/edwards.rs b/src/edwards.rs index 7e15fce..9c9aa73 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -119,7 +119,10 @@ use scalar_mul::window::LookupTable; use traits::{Identity, IsIdentity}; use traits::ValidityCheck; + +#[cfg(any(feature = "alloc", feature = "std"))] use traits::MultiscalarMul; +#[cfg(any(feature = "alloc", feature = "std"))] use traits::VartimeMultiscalarMul; // ------------------------------------------------------------------------ diff --git a/src/ristretto.rs b/src/ristretto.rs index 5581a19..d12de5d 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -192,7 +192,9 @@ use scalar::Scalar; use curve_models::CompletedPoint; -use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; +use traits::Identity; +#[cfg(any(feature = "alloc", feature = "std"))] +use traits::{MultiscalarMul, VartimeMultiscalarMul}; // ------------------------------------------------------------------------ // Compressed points diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index 21bf29e..398e011 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -12,11 +12,16 @@ #![allow(non_snake_case)] +#[cfg(any(feature = "alloc", feature = "std"))] use core::borrow::Borrow; +#[cfg(any(feature = "alloc", feature = "std"))] use edwards::EdwardsPoint; +#[cfg(any(feature = "alloc", feature = "std"))] use scalar::Scalar; +#[cfg(any(feature = "alloc", feature = "std"))] use traits::MultiscalarMul; +#[cfg(any(feature = "alloc", feature = "std"))] use traits::VartimeMultiscalarMul; /// Perform multiscalar multiplication by the interleaved window @@ -40,6 +45,7 @@ use traits::VartimeMultiscalarMul; /// /// [solution]: https://www.jstor.org/stable/2310929 /// [problem]: https://www.jstor.org/stable/2312273 +#[cfg(any(feature = "alloc", feature = "std"))] pub struct Straus {} #[cfg(any(feature = "alloc", feature = "std"))] From 04f75767f3a1292fbe8b5d94af5cd5f5da3dda0f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 19 Jul 2018 23:51:13 +0000 Subject: [PATCH 40/58] Scalar::random should work with nostd. * FIXES #166. --- src/scalar.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 945691c..5e4407f 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -436,7 +436,6 @@ impl Scalar { /// # Returns /// /// A random scalar within ℤ/lℤ. - #[cfg(feature = "std")] pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; rng.fill(&mut scalar_bytes); From 7b22fe6e87c0be332ad0844010ddd212415316d9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 00:04:25 +0000 Subject: [PATCH 41/58] Change the wording on the Scalar::as_bytes() docstring. --- src/scalar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 4e9286d..6c3bcfe 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -547,7 +547,7 @@ impl Scalar { self.bytes } - /// View this `Scalar` as its underlying sequence of bytes. + /// View the little-endian byte encoding of the integer representing this Scalar. /// /// # Example /// From 73a5f4711adefecdb0f7d74dc31116b7cb042f42 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 00:06:31 +0000 Subject: [PATCH 42/58] Remove unnecessary extern crate sha2 from test code. --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 97fbb81..c28ff31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,9 +37,6 @@ extern crate core; #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(test)] -extern crate sha2; - extern crate rand; extern crate clear_on_drop; extern crate byteorder; From 38aa0ee2b7fb2c02194a7cd2c301ba9c6ce9b54e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 00:47:33 +0000 Subject: [PATCH 43/58] Implement Default for remaining point types. * FIXES https://github.com/dalek-cryptography/curve25519-dalek/issues/154 --- src/edwards.rs | 12 ++++++++++++ src/montgomery.rs | 6 ++++++ src/ristretto.rs | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index 08eff32..aa22b67 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -257,6 +257,12 @@ impl Identity for CompressedEdwardsY { } } +impl Default for CompressedEdwardsY { + fn default() -> CompressedEdwardsY { + CompressedEdwardsY::identity() + } +} + impl Identity for EdwardsPoint { fn identity() -> EdwardsPoint { EdwardsPoint{ X: FieldElement::zero(), @@ -266,6 +272,12 @@ impl Identity for EdwardsPoint { } } +impl Default for EdwardsPoint { + fn default() -> EdwardsPoint { + EdwardsPoint::identity() + } +} + // ------------------------------------------------------------------------ // Validity checks (for debugging, not CT) // ------------------------------------------------------------------------ diff --git a/src/montgomery.rs b/src/montgomery.rs index 0c21b00..68b871a 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -155,6 +155,12 @@ impl Identity for ProjectivePoint { } } +impl Default for ProjectivePoint { + fn default() -> ProjectivePoint { + ProjectivePoint::identity() + } +} + impl ConditionallyAssignable for ProjectivePoint { fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) { self.U.conditional_assign(&that.U, choice); diff --git a/src/ristretto.rs b/src/ristretto.rs index cb4e8e0..978f78c 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -279,6 +279,12 @@ impl Identity for CompressedRistretto { } } +impl Default for CompressedRistretto { + fn default() -> CompressedRistretto { + CompressedRistretto::identity() + } +} + // ------------------------------------------------------------------------ // Serde support // ------------------------------------------------------------------------ @@ -661,6 +667,12 @@ impl Identity for RistrettoPoint { } } +impl Default for RistrettoPoint { + fn default() -> RistrettoPoint { + RistrettoPoint::identity() + } +} + // ------------------------------------------------------------------------ // Equality // ------------------------------------------------------------------------ From 6f82c30a883d95d502787b563f6de9c06ea818c3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 04:45:06 +0000 Subject: [PATCH 44/58] Fix doctests for From for Scalar. --- src/scalar.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index fd0ae17..6e7dad5 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -453,7 +453,6 @@ impl From for Scalar { } impl From for Scalar { - /// Construct a scalar from the given `u64`. /// /// # Inputs @@ -469,9 +468,9 @@ impl From for Scalar { /// ``` /// use curve25519_dalek::scalar::Scalar; /// - /// let fourtytwo = Scalar::from_u64(42); - /// let six = Scalar::from_u64(6); - /// let seven = Scalar::from_u64(7); + /// let fourtytwo = Scalar::from(42u64); + /// let six = Scalar::from(6u64); + /// let seven = Scalar::from(7u64); /// /// assert!(fourtytwo == six * seven); /// ``` From 6eb876f3cb2cba2b373d93f12fee3ca4b1738964 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 20 Jul 2018 11:50:42 -0700 Subject: [PATCH 45/58] Fix doctests (missed during merge) --- src/traits.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index f9d4594..8db963a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -124,9 +124,9 @@ pub trait VartimeMultiscalarMul { /// use curve25519_dalek::scalar::Scalar; /// /// // Some scalars - /// let a = Scalar::from_u64(87329482); - /// let b = Scalar::from_u64(37264829); - /// let c = Scalar::from_u64(98098098); + /// let a = Scalar::from(87329482u64); + /// let b = Scalar::from(37264829u64); + /// let c = Scalar::from(98098098u64); /// let abc = [a,b,c]; /// /// // Some points From 5a58f421559c03354128427acfa3b3f53fb6db44 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 20 Jul 2018 12:24:28 -0700 Subject: [PATCH 46/58] Point to https://ristretto.group since our notes live there now. --- docs/ristretto-notes.md | 475 ---------------------------------------- src/backend/avx2/mod.rs | 10 +- src/ristretto.rs | 34 +-- 3 files changed, 21 insertions(+), 498 deletions(-) delete mode 100644 docs/ristretto-notes.md diff --git a/docs/ristretto-notes.md b/docs/ristretto-notes.md deleted file mode 100644 index 8999137..0000000 --- a/docs/ristretto-notes.md +++ /dev/null @@ -1,475 +0,0 @@ -Below are some notes on Ristretto, which are not an authoritative -writeup and which may have errors. See also the [Decaf -paper][decaf_paper], the [libdecaf -implementation of Ristretto][ristretto_libdecaf], and its [Sage -script][ristretto_sage]. - -Decaf constructs a prime-order group from a cofactor-\\(4\\) Edwards -curve by defining an encoding of a related Jacobi quartic, then -transporting the encoding from the Jacobi quartic to the Edwards curve -by means of an isogeny. Ristretto uses a different Jacobi quartic and -a different isogeny, but is otherwise similar. - -These notes only describe Ristretto, and focus on the cofactor-\\(8\\) -case. - -# The Jacobi Quartic - -The Jacobi quartic curve is parameterized by \\(e, A\\), and is of the -form $$ \mathcal J\_{e,A} : t\^2 = es\^4 + 2As\^2 + 1, $$ with -identity point \\((0,1)\\). For more details on the Jacobi quartic, -see the [Decaf paper][decaf_paper] or -[_Jacobi Quartic Curves Revisited_][hwcd_jacobi] by Hisil, Wong, -Carter, and Dawson). - -When \\(e = a\^2\\) is a square, \\(\mathcal J\_{e,A}\\) has full -\\(2\\)-torsion (i.e., \\(\mathcal J[2] \cong \mathbb Z /2 \times -\mathbb Z/2\\)), and -we can write the \\(\mathcal J[2]\\)-coset of a point \\(P = -(s,t)\\) as -$$ -P + \mathcal J[2] = \left\\{ -(s,t), -(-s,-t), -(1/as, -t/as\^2), -(-1/as, t/as\^2) -\right\\}. -$$ -Notice that replacing \\(a\\) by \\(-a\\) just swaps the last two -points, so this set does not depend on the choice of \\(a\\). In -what follows we require \\(a = \pm 1\\). - -# Encoding \\(\mathcal J / \mathcal J[2]\\) - -To encode points on \\(\mathcal J\\) modulo \\(\mathcal J[2]\\), -we need to choose a canonical representative of the above coset. -To do this, it's sufficient to make two independent sign choices: -the Decaf paper suggests choosing \\((s,t)\\) with \\(s\\) -non-negative and finite, and \\(t/s\\) non-negative or infinite. - -The encoding is then the (canonical byte encoding of the) -\\(s\\)-value of the canonical representative. - -# The Edwards Curve - -The primary internal model in `curve25519-dalek` for Curve25519 points -is the [_Extended Twisted Edwards Coordinates_][hwcd_edwards] of -Hisil, Wong, Carter, and Dawson. These correspond to the affine model -$$ -\mathcal E\_{a,d} : ax\^2 + y\^2 = 1 + dx\^2y\^2. -$$ -In projective coordinates, we represent a point as \\((X:Y:Z:T)\\) -with -$$ -XY = ZT, \quad aX\^2 + Y\^2 = Z\^2 + dT\^2. -$$ -(For more details on this model, see the -[`curve_models`][curve_models] documentation). The case \\(a = 1\\) is -the _untwisted_ case; we only consider \\(a = \pm 1\\), and in -particular we focus on the twisted Edwards form of Curve25519, which -has \\(a = -1, d = -121665/121666\\). When not otherwise specified, -we write \\(\mathcal E\\) for \\(\mathcal E\_{a,d}\\). - -When both \\(d\\) and \\(ad\\) are nonsquare (which forces \\(a\\) -to be square), the curve is *complete*. In this case the -four-torsion subgroup is cyclic, and we -can write it explicitly as -$$ -\mathcal E\_{a,d}[4] = \\{ (0,1),\\; (1/\sqrt a, 0),\\; (0, -1),\\; (-1/\sqrt{a}, 0)\\}. -$$ -These are the only points with \\(xy = 0\\); the points with -\\( y \neq 0 \\) are \\(2\\)-torsion. - -# The Ristretto Group - -We consider two cases: - -* cofactor \\(4\\), where \\( \\# \mathcal E(\mathbb F_p) = 4\cdot \ell \\); -* cofactor \\(8\\) with cyclic \\(8\\)-torsion, - where \\( \\# \mathcal E(\mathbb F_p) = 8 \cdot \ell \\) - and \\( \mathcal E[8] \cong \mathbb Z / 8 \\). - -In the cofactor \\(4\\) case, we have \\( \[2\](\mathcal E[4]) = -\mathcal E[2] \\), so that \\( \mathcal E[2] \subseteq \[2\](\mathcal -E) \\), and the group we will construct is -$$ -\frac{\[2\](\mathcal E)}{\mathcal E[2]} -$$ -which has prime order \\( (4\ell/2)/2 = \ell \\). - -In the cofactor \\(8\\) case, since the \\(8\\)-torsion is cyclic, we -have \\( \[2\](\mathcal E[8]) = \mathcal E[4] \\), so that \\(\mathcal -E[4] \subseteq \[2\](\mathcal E)\\), and the group we will construct -is -$$ -\frac{\[2\](\mathcal E)}{\mathcal E[4]} -$$ -which has prime order \\( (8\ell/2)/4 = \ell \\). - -In particular, Curve25519 has \\( \mathcal E(\mathbb -F\_p) \cong \mathbb Z / 8 \times \mathbb Z / \ell\\), where \\( \ell -= 2\^{252} + \cdots \\) is a large prime, and meets the requirements -for the cofactor \\(8\\) case. - -# Torquing points to lift from \\(\mathcal E[4]\\) to \\(\mathcal E[2]\\) - -To bridge the gap between the cofactor \\(4\\) and cofactor \\(8\\) -cases, we need a way to canonically select a representative modulo -\\(\mathcal E[2] \\), given a representative modulo \\(\mathcal E[4] \\). - -Using the description of \\(\mathcal E[4]\\) above, we can write the -\\(\mathcal E[4]\\)-coset of a point \\(P = (x,y)\\) as -$$ -P + \mathcal E\_{a,d}[4] = \\{ (x,y),\\; (y/\sqrt a, -x\sqrt a),\\; (-x, -y),\\; (-y/\sqrt a, x\sqrt a)\\}. -$$ -Notice that if \\(xy \neq 0 \\), then exactly two of these points have -\\( xy \\) non-negative, and they differ by the \\(2\\)-torsion point -\\( (0,-1) \\). - -This means that we can select a representative modulo -\\(\mathcal E[2]\\) by requiring \\(xy\\) nonnegative and \\(y \neq -0\\), and we can ensure that this condition holds by conditionally -adding a \\(4\\)-torsion point \\(Q_4\\) if \\(xy\\) is negative or -\\(y = 0\\). - -The points of exact order \\(4\\) are \\( (\pm 1/\sqrt{a}, 0 )\\); -convenient choices for \\( Q_4 \\) are \\((1,0)\\) when \\( a = 1 \\) -and \\( (i, 0) \\) when \\( a = -1 \\), although the choice of which -\\(4\\)-torsion point to use doesn't matter. - -This procedure gives a canonical lift from \\(\mathcal E / \mathcal -E[4]\\) to \\(\mathcal E / \mathcal E[2]\\). Since it involves a -conditional rotation, we refer to it as *torquing* the point. - -# The Isogeny - -For \\(a = \pm 1\\), we have a \\(2\\)-isogeny -$$ -\theta\_{a,d} : \mathcal J\_{a\^2, -a(a+d)/(a-d)} \longrightarrow \mathcal E\_{a,d} -$$ -(or simply \\(\theta\\)) defined by -$$ -\theta\_{a,d} : (s,t) \mapsto \left( \frac{1}{\sqrt{ad-1}} \cdot \frac{2s}{t},\quad \frac{1+as\^2}{1-as\^2} \right). -$$ -Its dual is -$$ -\hat{\theta}\_{a,d} : \mathcal E\_{a,d} \longrightarrow \mathcal J\_{a\^2, -a(a+d)/(a-d)}, -$$ -defined by -$$ -\hat{\theta}\_{a,d} : (x,y) \mapsto \left( \sqrt{ad-1} \cdot \frac{xy}{1-ax\^2}, \frac{y^2 + ax^2}{1-ax^2} \right) -$$ - -The kernel of the isogeny is \\( \{(0, \pm 1)\} \\). -The image of the isogeny is \\(\[2\](\mathcal E)\\). To see this, -first note that because \\( \theta \circ \hat{\theta} = [2] \\), we -know that \\( \[2\](\mathcal E) \subseteq \theta(\mathcal J)\\); then, to see that -\\(\theta(\mathcal J)\\) is exactly \\(\[2\](\mathcal E)\\), -recall that isogenous elliptic curves over a finite field have the -same number of points (exercise 5.4 of Silverman), so that -$$ -\\# \theta(\mathcal J) = \frac {\\# \mathcal J} {\\# \ker \theta} -= \frac {\\# \mathcal E}{2} = \\# \[2\](\mathcal E). -$$ - -To determine the image \\(\theta(\mathcal J[2])\\) of the -\\(2\\)-torsion, we consider the image of the coset -\\(\theta((s,t) + \mathcal J[2])\\). -Let \\((x,y) = \theta(s,t)\\); then -\\(\theta(-s,-t) = (x,y)\\) and -\\(\theta(1/as, -t/as\^2) = (-x, -y)\\), -so that \\(\theta(\mathcal J[2]) = \mathcal E[2]\\). - -# Encoding with the Isogeny - -The Decaf paper recalls that, for a group \\( G \\) with normal -subgroup \\(G' \leq G\\), a group homomorphism \\( \phi : G -\rightarrow H \\) induces a homomorphism -$$ -\bar{\phi} : \frac G {G'} \longrightarrow \frac {\phi(G)}{\phi(G')} \leq \frac {H} {\phi(G')}, -$$ -and that the induced homomorphism \\(\bar{\phi}\\) is injective if -\\( \ker \phi \leq G' \\). In our context, the kernel of -\\(\theta\\) is \\( \\{(0, \pm 1)\\} \leq \mathcal J[2] \\), -so \\(\theta\\) gives an isomorphism -$$ -\frac {\mathcal J} {\mathcal J[2]} -\cong -\frac {\theta(\mathcal J)} {\theta(\mathcal J[2])} -\cong -\frac {\[2\](\mathcal E)} {\mathcal E[2]}. -$$ - -We can use the isomorphism to transfer the encoding of \\(\mathcal -J / \mathcal J[2] \\) defined above to \\(\[2\](\mathcal E)/\mathcal -E[2]\\), by encoding the Edwards point \\((x,y)\\) using the Jacobi -quartic encoding of \\(\theta\^{-1}(x,y)\\). - -Since \\(\\# (\[2\](\mathcal E) / \mathcal E[2]) = (\\#\mathcal -E)/4\\), if \\(\mathcal E\\) has cofactor \\(4\\), we're done. -Otherwise, if \\(\mathcal E\\) has cofactor \\(8\\), as in the -Curve25519 case, we use the torquing procedure to lift \\(\mathcal E -/ \mathcal E[4]\\) to \\(\mathcal E / \mathcal E[2]\\), and then -apply the encoding for \\( \[2\](\mathcal E) / \mathcal E[2] \\). - -# The Ristretto Encoding - -We can write the above encoding/decoding procedure in affine -coordinates, before describing optimized formulas to and from -projective coordinates. - -## Encoding in Affine Coordinates - -On input \\( (x,y) \in \[2\](\mathcal E)\\), a representative for a -coset in \\( \[2\](\mathcal E) / \mathcal E[4] \\): - -1. Check if \\( xy \\) is negative or \\( x = 0 \\); if so, torque - the point by setting \\( (x,y) \gets (x,y) + Q_4 \\), where - \\(Q_4\\) is a \\(4\\)-torsion point. - -2. Check if \\(x\\) is negative or \\( y = -1 \\); if so, set - \\( (x,y) \gets (x,y) + (0,-1) = (-x, -y) \\). - -3. Compute $$ s = +\sqrt {(-a) \frac {1 - y} {1 + y} }, $$ choosing - the positive square root. - -The output is then the (canonical) byte-encoding of \\(s\\). - -If \\(\mathcal E\\) has cofactor \\(4\\), we skip the first step, -since our input already represents a coset in -\\( \[2\](\mathcal E) / \mathcal E[2] \\). - -## Interpreting the Encoding Procedure - -How does this procedure correspond to the description involving -\\( \theta \\)? - -The first step lifts from \\( \mathcal E / \mathcal E[4] \\) to -\\(\mathcal E / \mathcal E[2]\\). To understand steps 2 and 3, -notice that the \\(y\\)-coordinate of \\(\theta(s,t)\\) is -$$ -y = \frac {1 + as\^2}{1 - as\^2}, -$$ -so that the \\(s\\)-coordinate of \\(\theta\^{-1}(x,y)\\) has -$$ -s\^2 = (-a)\frac {1-y}{1+y}. -$$ -Since -$$ -x = \frac 1 {\sqrt {ad - 1}} \frac {2s} {t}, -$$ -we also have -$$ -\frac s t = x \frac {\sqrt {ad-1}} 2, -$$ -so that the sign of \\(s/t\\) is determined by the sign of \\(x\\). - -Recall that to choose a canonical representative of \\( (s,t) + -\mathcal J[2] \\), it's sufficient to make two sign choices: the -sign of \\(s\\) and the sign of \\(s/t\\). Step 2 determines the -sign of \\(s/t\\), while step 3 computes \\(s\\) and determines its -sign (by choosing the positive square root). Finally, the check -that \\(y \neq -1\\) prevents division-by-zero when encoding the -identity; it falls out of the optimized formulas below. - -## Decoding to Affine Coordinates - -On input `s_bytes`, decoding proceeds as follows: - -1. Decode `s_bytes` to \\(s\\); reject if `s_bytes` is not the - canonical encoding of \\(s\\). - -2. Check whether \\(s\\) is negative; if so, reject. - -3. Compute -$$ -y \gets \frac {1 + as\^2}{1 - as\^2}. -$$ - -4. Compute -$$ -x \gets +\sqrt{ \frac{4s\^2} {ad(1+as\^2)\^2 - (1-as\^2)\^2}}, -$$ -choosing the positive square root, or reject if the square root does -not exist. - -5. Check whether \\(xy\\) is negative or \\(y = 0\\); if so, reject. - -# Encoding in Extended Coordinates - -The formulas above are given in affine coordinates, but the usual -internal representation is extended twisted Edwards coordinates \\( -(X:Y:Z:T) \\) with \\( x = X/Z \\), \\(y = Y/Z\\), \\(xy = T/Z \\). - -This section only covers the cofactor-\\(8\\) case, since it is more complicated: -selecting the distinguished representative of the coset -requires the affine coordinates \\( (x,y) \\), and computing \\( s -\\) requires an inverse square root. -As inversions are expensive, we'd like to be able to do this -whole computation with only one inverse square root, by batching -together the inversion and the inverse square root. - -It is not obvious how to do this, since we need the inverse square -root of one of two values, depending on what the distinguished -representative is, but the choice of representative depends on the -affine coordinates. However, an ingenious trick (due to Mike Hamburg) -allows recovering either of the inverse square roots we want. - -## Batching the Inversion and Inverse Square Root - -Write \\( (X\_0 : Y\_0 : Z\_0 : T\_0) \\) -for the coordinates of the initial representative, and write -\\( (X:Y:Z:T) \\) for the coordinates of the distinguished -representative of the coset. - -Since \\(y = Y/Z\\), in extended coordinates the formula for \\(s\\) becomes -$$ -s -= \sqrt{ (-a) \frac{ 1 - Y/Z}{1+Y/Z}} = \sqrt{\frac{Z - Y}{Z+Y}} \sqrt{-a} -= \frac {Z - Y} {\sqrt{Z\^2 - Y\^2}} \sqrt{-a}, -$$ -so we need to compute \\( 1 / \sqrt{Z^2 - Y^2} \\). - -The distinguished representative \\( (X:Y:Z:T) \\) is selected by the -torquing procedure in step 1, which conditionally adds a -\\(4\\)-torsion point \\(Q_4\\). As noted in the torquing section -above, \\( Q_4 = (\pm 1/\sqrt{a}, 0) \\), so we obtain -$$ -(X : Y : Z : T ) = -\begin{cases} -(X\_0 : Y\_0 : Z\_0 : T\_0) \\\\ -(\pm Y\_0 / \sqrt{a} : \mp X\_0 \sqrt{a} : Z\_0 : -T\_0) -\end{cases} -. -$$ -This means we want to compute either of -$$ -\frac {1} {\sqrt{Z^2 - Y^2}} -= -\begin{cases} -1 / \sqrt{Z\_0^2 - Y\_0^2} \\\\ -1 / \sqrt{Z\_0^2 - aX\_0^2} -\end{cases} -. -$$ -To relate these quantities, recall from the curve equation that -$$ --dX\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2, -$$ -so -$$ -(a-d)X\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2 + aX\^2Y\^2. -$$ -Factoring the right-hand side gives -$$ -(a-d)X\^2Y\^2 = (Z\^2 - Y\^2)(Z\^2 - aX\^2), -$$ -which relates the two quantities we want to compute: -$$ -\frac 1 {Z^2 - aX^2} = \frac 1 {a - d} \frac {Z^2 - Y^2} {X^2 Y^2} -$$ -so -$$ -\frac 1 {\sqrt{Z^2 - aX^2}} = \frac 1 {\sqrt{a - d}} \sqrt{ \frac {Z^2 - Y^2} {X^2 Y^2} } -$$ - -## Explicit Encoding Formulas - -Using this trick, we can write the encoding procedure explicitly: - -1. \\(u\_1 \gets (Z\_0 + Y\_0)(Z\_0 - Y\_0) - \textcolor{gray}{= Z\_0\^2 - Y\_0\^2} - \\) -2. \\(u\_2 \gets X\_0 Y\_0 \\) -3. \\(I \gets \mathrm{invsqrt}(u\_1 u\_2\^2) - \textcolor{gray}{= 1/\sqrt{X\_0\^2 Y\_0\^2 (Z\_0\^2 - Y\_0\^2)}} - \\) -4. \\(D\_1 \gets u\_1 I - \textcolor{gray}{= \sqrt{(Z\_0\^2 - Y\_0\^2)/(X\_0\^2 Y\_0\^2)} } - \\) -5. \\(D\_2 \gets u\_2 I - \textcolor{gray}{= \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} } - \\) -6. \\(Z\_{inv} \gets D\_1 D\_2 T\_0 - \textcolor{gray}{= (u\_1 u\_2)/(u\_1 u\_2\^2) T\_0 = T\_0 / X\_0 Y\_0 = 1/Z\_0} - \\) -7. If \\( T\_0 Z\_{inv} \textcolor{gray}{= x\_0 y\_0 }\\) is negative: - 1. \\( (X, Y) \gets (Y\_0 (\pm 1/\sqrt{a}), X\_0 (\mp \sqrt{a})) \\) - 2. \\( D \gets D\_1 / \sqrt{a-d} - \textcolor{gray}{= 1/\sqrt{Z\_0\^2 - a X\_0\^2} = 1/\sqrt{Z^2 -Y^2} } - \\) -8. Otherwise: - 1. \\( (X, Y) \gets (X\_0, Y\_0) \\) - 2. \\( D \gets D\_2 - \textcolor{gray}{= \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} = \pm 1/\sqrt{Z^2 - Y^2}} - \\) -9. If \\( X Z\_{inv} \textcolor{gray}{= x} \\) is negative, set \\( Y \gets - Y\\) -10. Compute \\( s \gets |\sqrt{-a} (Z - Y) D| \textcolor{gray}{= |\sqrt{-a} (Z - Y) / \sqrt{Z\^2 - Y\^2}| } \\) -11. Return the canonical byte encoding of \\( s \\). - -The choice of \\( Q\_4 = (i, 0) \\) when \\( a = -1 \\) is convenient -since it simplifies 7.1 to \\( (X,Y) \gets (iY_0, iX_0) \\). - -## Explicit Decoding Formulas - -As with encoding, we want to batch operations to use only a single -inverse square root. However, the procedure is much simpler since -there's no torquing. - -On input `s_bytes`: - -1. Check that `s_bytes` is the canonical byte-encoding of a field -element \\(s\\), otherwise reject. -2. Decode `s_bytes` to \\(s\\). -3. Check that \\( s \\) is nonnegative, otherwise reject. -4. \\( u_1 \gets 1 + as^2 \\) -5. \\( u_2 \gets 1 - as^2 \\) -6. \\( v \gets (ad)u_1^2 - u_2^2 \textcolor{gray}{= ad(1+as^2)^2 - (1-as^2)^2} \\) -7. \\( I \gets \mathrm{invsqrt}( v u_2^2 ) \textcolor{gray}{= 1/\sqrt{v u_2^2} } \\) -8. \\( D_x \gets Iu_2 \textcolor{gray}{= 1/\sqrt{v} } \\) -9. \\( D_y \gets ID_x v \textcolor{gray}{= I^2 u_2 v = (v u_2) / (v u_2^2) = 1/u_2 } \\) -10. \\( x \gets |2sD_x| \textcolor{gray}{= +\sqrt{ 4s^2 / (ad(1+as^2)^2 - (1-as^2)^2 )}}\\) -11. \\( y \gets u_1 D_y \textcolor{gray}{= (1+as^2)/(1-as^2) } \\) -12. \\( t \gets xy \\) -12. Check that \\(t \\) is nonnegative and that \\( y \neq 0 \\), otherwise reject. -13. Return \\( P = (x: y: 1: t) \\) - -# Batched Double-and-Encode Using \\( \hat \theta \\) - -The encoding is not batchable, since it requires an inverse square -root. However, since \\( \theta \circ \hat \theta = [2] P \\), it's -possible to compute the encoding of \\( [2]P \\) by using \\( \hat -\theta \\) instead of \\( \theta^{-1} \\). Since \\( \hat \theta \\) only -requires inversions, given \\( P\_1, \ldots, P\_n \\), it's possible -to compute the encodings of \\( [2]P\_1, \ldots, [2]P\_n \\) in a -batch. - -XXX write up details - -# Equality Testing - -Testing equality of two Ristretto points means testing whether they -are equal in the quotient group, i.e., whether they lie in the same -coset of \\(\mathcal E[4] \\) (for the cofactor-\\(8\\) case) or -\\(\mathcal E[2] \\) (for the cofactor-\\(4\\) case). - -Equality testing of points on the Edwards curve requires comparing to -affine coordinates, which requires an expensive inversion. However, -testing whether two points lie in the same coset can be done in -projective coordinates, making it actually *easier* than equality -testing in the original non-quotient group. - -XXX write up details - -# Elligator - -XXX write up details - -[ristretto_sage]: https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/aux/ristretto/ristretto.sage -[ristretto_libdecaf]: https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/ -[decaf_paper]: https://eprint.iacr.org/2015/673.pdf -[hwcd_jacobi]: https://eprint.iacr.org/2009/312.pdf -[hwcd_edwards]: https://eprint.iacr.org/2008/522.pdf -[edwards_edwards]: https://www.ams.org/journals/bull/2007-44-03/S0273-0979-07-01153-6/S0273-0979-07-01153-6.pdf -[twisted_edwards]: https://eprint.iacr.org/2008/013.pdf -[curve_models]: ../../curve_models/index.html \ No newline at end of file diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index 14a2fcb..10886af 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -8,7 +8,15 @@ // - Isis Agora Lovecruft // - Henry de Valence -// See the comment above the ristretto::notes module. +// Conditionally include the AVX2 notes if: +// - we're on nightly (so we can include docs at all) +// - we're in stage 2 of the build. +// The latter point prevents a really silly and annoying problem, +// where the location of ".." is different depending on whether we're +// building the crate for real, or whether we're in build.rs +// generating the lookup tables (in which case we're relative to the +// location of build.rs, not lib.rs, so the markdown file appears +// missing). #![cfg_attr( all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md") )] diff --git a/src/ristretto.rs b/src/ristretto.rs index 56c610c..4a1df92 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -14,7 +14,8 @@ // affine and projective cakes and eat both of them too. #![allow(non_snake_case)] -//! An implementation of Ristretto, which provides a prime-order group. +//! An implementation of [Ristretto][ristretto_main], which provides a +//! prime-order group. //! //! # The Ristretto Group //! @@ -50,8 +51,10 @@ //! this [additional restriction][ristretto_coffee] gives the //! _Ristretto_ encoding. //! -//! More details -//! are described in the *Implementation* section below. Ristretto +//! More details on why Ristretto is necessary can be found in the +//! [Why Ristretto?][why_ristretto] section of the Ristretto website. +//! +//! Ristretto //! points are provided in `curve25519-dalek` by the `RistrettoPoint` //! struct. //! @@ -137,8 +140,7 @@ //! using Edwards formulas. //! //! Notes on the details of the encoding can be found in the -//! [`ristretto::notes`][ristretto_notes] submodule of the internal `curve25519-dalek` -//! documentation. +//! [Details][ristretto_notes] section of the Ristretto website. //! //! [cryptonote]: //! https://moderncrypto.org/mail-archive/curves/2017/000898.html @@ -147,23 +149,11 @@ //! [ristretto_coffee]: //! https://en.wikipedia.org/wiki/Ristretto //! [ristretto_notes]: -//! https://doc-internal.dalek.rs/curve25519_dalek/ristretto/notes/index.html - - -// Conditionally include the Ristretto notes if: -// - we're on nightly (so we can include docs at all) -// - we're in stage 2 of the build. -// The latter point prevents a really silly and annoying problem, -// where the location of ".." is different depending on whether we're -// building the crate for real, or whether we're in build.rs -// generating the lookup tables (in which case we're relative to the -// location of build.rs, not lib.rs, so the markdown file appears -// missing). -// -// This hack is also used in the avx2 notes. -#[cfg_attr(all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/ristretto-notes.md"))] -mod notes { -} +//! https://ristretto.group/details/index.html +//! [why_ristretto]: +//! https://ristretto.group/why_ristretto.html +//! [ristretto_main]: +//! https://ristretto.group/ use core::fmt::Debug; use core::ops::{Add, Sub, Neg}; From 3324e7d0ae7b8c5fc6c65e9d7523322df433382f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 19:51:51 +0000 Subject: [PATCH 47/58] Remove impl Default for ProjectivePoint. --- src/curve_models/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index 45c2919..d2ded64 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -212,12 +212,6 @@ impl Identity for ProjectivePoint { } } -impl Default for ProjectivePoint { - fn default() -> ProjectivePoint { - ProjectivePoint::identity() - } -} - impl Identity for ProjectiveNielsPoint { fn identity() -> ProjectiveNielsPoint { ProjectiveNielsPoint{ From b087551696018fe96c5b809c53fa41bb1fc566a5 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 20 Jul 2018 21:36:38 +0000 Subject: [PATCH 48/58] Impl Default for Scalar. --- src/scalar.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 2d55d73..beaaec0 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -426,6 +426,12 @@ where } } +impl Default for Scalar { + fn default() -> Scalar { + Scalar::zero() + } +} + impl From for Scalar { fn from(x: u8) -> Scalar { let mut s_bytes = [0u8; 32]; From f675f4cd2b0699d14e5857aa898eaa1b6e83c6d9 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 20 Jul 2018 20:33:17 -0700 Subject: [PATCH 49/58] fixup! Allow Options in the VartimeMultiscalarMul trait --- src/backend/avx2/scalar_mul/straus.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/backend/avx2/scalar_mul/straus.rs b/src/backend/avx2/scalar_mul/straus.rs index 053713f..1e40b7c 100644 --- a/src/backend/avx2/scalar_mul/straus.rs +++ b/src/backend/avx2/scalar_mul/straus.rs @@ -72,21 +72,24 @@ impl MultiscalarMul for Straus { impl VartimeMultiscalarMul for Straus { type Point = EdwardsPoint; - fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { let nafs: Vec<_> = scalars .into_iter() .map(|c| c.borrow().non_adjacent_form(5)) .collect(); - let lookup_tables: Vec<_> = points + let lookup_tables: Vec<_> = match points .into_iter() - .map(|point| NafLookupTable5::::from(point.borrow())) - .collect(); + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>() + { + Some(x) => x, + None => return None, + }; let mut Q = ExtendedPoint::identity(); @@ -101,6 +104,7 @@ impl VartimeMultiscalarMul for Straus { } } } - Q.into() + + Some(Q.into()) } } From 10e8abf926bc83375c1efebecce723e35df7dbfd Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 20 Jul 2018 14:43:31 -0700 Subject: [PATCH 50/58] Unify `alloc` and `std` cargo features This change provides a common convention for using allocator-dependent features with: #![cfg(feature = "alloc")] When available, `Vec` is imported consistently as `prelude::Vec`, which means modules that need access to `Vec` can simply do: use prelude::*; and if an allocator is available, `Vec` will be in the crate prelude. This allows all `alloc` vs `std` gating to be handled in `lib.rs`, `build.rs`, and `prelude.rs` so the rest of the codebase doesn't have to do any gating whatsoever. --- .travis.yml | 8 +++++--- Cargo.toml | 2 +- build.rs | 5 +++++ src/backend/avx2/scalar_mul/straus.rs | 7 +++++-- src/backend/mod.rs | 6 ++++++ src/edwards.rs | 7 +++++-- src/field.rs | 2 +- src/lib.rs | 17 +++++++++++------ src/montgomery.rs | 2 ++ src/prelude.rs | 8 ++++++++ src/ristretto.rs | 14 +++++++++++--- src/scalar.rs | 2 +- src/scalar_mul/straus.rs | 7 +++++-- 13 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 src/prelude.rs diff --git a/.travis.yml b/.travis.yml index 50e9a3c..fc5f2dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,16 +15,18 @@ env: - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' # Tests building without std. We have to select a backend, so we select the one # most likely to be useful in an embedded environment. - - TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' + - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend' + # Tests no_std+alloc usage using the most embedded-friendly backend + - TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' matrix: exclude: # Test the avx2 backend only on nightly - rust: stable env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend' - # Test no_std only on nightly. + # Test no_std+alloc only on nightly - rust: stable - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' + env: TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS diff --git a/Cargo.toml b/Cargo.toml index 32d5783..f8b1503 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ serde = { version = "1.0", optional = true } [features] nightly = ["subtle/nightly", "clear_on_drop/nightly"] default = ["std", "u64_backend"] -std = ["subtle/std", "rand/std"] +std = ["alloc", "subtle/std", "rand/std"] alloc = [] yolocrypto = [] diff --git a/build.rs b/build.rs index e0a2da7..6295fdd 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,12 @@ +#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))] #![allow(unused_variables)] #![allow(non_snake_case)] #![allow(dead_code)] +#[cfg(all(feature = "alloc", not(feature = "std")))] +extern crate alloc; extern crate byteorder; extern crate clear_on_drop; extern crate core; @@ -53,6 +56,8 @@ mod field; mod curve_models; #[path = "src/backend/mod.rs"] mod backend; +#[path = "src/prelude.rs"] +mod prelude; #[path = "src/scalar_mul/mod.rs"] mod scalar_mul; diff --git a/src/backend/avx2/scalar_mul/straus.rs b/src/backend/avx2/scalar_mul/straus.rs index 1e40b7c..eed9084 100644 --- a/src/backend/avx2/scalar_mul/straus.rs +++ b/src/backend/avx2/scalar_mul/straus.rs @@ -20,6 +20,9 @@ use scalar::Scalar; use scalar_mul::window::{LookupTable, NafLookupTable5}; use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; +#[allow(unused_imports)] +use prelude::*; + /// Multiscalar multiplication using interleaved window / Straus' /// method. See the `Straus` struct in the serial backend for more /// details. @@ -30,7 +33,7 @@ use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; /// point representation on the fly. pub struct Straus {} -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl MultiscalarMul for Straus { type Point = EdwardsPoint; @@ -68,7 +71,7 @@ impl MultiscalarMul for Straus { } } -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl VartimeMultiscalarMul for Straus { type Point = EdwardsPoint; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index d325715..75fa6c0 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -21,6 +21,12 @@ //! `32bit` since identifiers can't start with letters, and the backends //! do use `u32`/`u64`, so this seems like a least-bad option. +#[cfg(not(any(feature = "u32_backend", feature = "u64_backend", feature = "avx2_backend")))] +compile_error!( + "no curve25519-dalek backend cargo feature enabled! \ + please enable one of: u32_backend, u64_backend, avx2_backend" +); + #[cfg(feature = "u32_backend")] pub mod u32; diff --git a/src/edwards.rs b/src/edwards.rs index 97e97c9..f58b182 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -115,6 +115,9 @@ use curve_models::CompletedPoint; use curve_models::AffineNielsPoint; use curve_models::ProjectiveNielsPoint; +#[allow(unused_imports)] +use prelude::*; + use scalar_mul::window::LookupTable; use traits::{Identity, IsIdentity}; @@ -540,7 +543,7 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { // These use the iterator's size hint and the target settings to // forward to a specific backend implementation. -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl MultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; @@ -569,7 +572,7 @@ impl MultiscalarMul for EdwardsPoint { } } -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl VartimeMultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; diff --git a/src/field.rs b/src/field.rs index 9d8d041..e523df9 100644 --- a/src/field.rs +++ b/src/field.rs @@ -135,7 +135,7 @@ impl FieldElement { /// Given a slice of public `FieldElements`, replace each with its inverse. /// /// All input `FieldElements` **MUST** be nonzero. - #[cfg(any(feature = "alloc", feature = "std"))] + #[cfg(feature = "alloc")] pub fn batch_invert(inputs: &mut [FieldElement]) { // Montgomery’s Trick and Fast Implementation of Masked AES // Genelle, Prouff and Quisquater diff --git a/src/lib.rs b/src/lib.rs index c28ff31..aaf8ace 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,9 @@ // - Isis Agora Lovecruft // - Henry de Valence -#![cfg_attr(not(feature = "std"), no_std)] - -#![cfg_attr(feature = "alloc", feature(alloc))] +#![no_std] +#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))] @@ -32,11 +31,14 @@ // External dependencies: //------------------------------------------------------------------------ -#[cfg(feature = "std")] -extern crate core; -#[cfg(feature = "alloc")] +#[cfg(all(feature = "alloc", not(feature = "std")))] +#[macro_use] extern crate alloc; +#[cfg(feature = "std")] +#[macro_use] +extern crate std; + extern crate rand; extern crate clear_on_drop; extern crate byteorder; @@ -94,5 +96,8 @@ pub(crate) mod backend; // Internal curve models which are not part of the public API. pub(crate) mod curve_models; +// Crate-local prelude (for alloc-dependent features like `Vec`) +pub(crate) mod prelude; + // Implementations of scalar mul algorithms live here pub(crate) mod scalar_mul; diff --git a/src/montgomery.rs b/src/montgomery.rs index 68b871a..fe7423b 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -290,6 +290,7 @@ mod test { use constants; use super::*; + #[cfg(feature = "rand")] use rand::rngs::OsRng; /// Test Montgomery -> Edwards on the X/Ed25519 basepoint @@ -343,6 +344,7 @@ mod test { assert_eq!(u18, u18_unred); } + #[cfg(feature = "rand")] #[test] fn montgomery_ladder_matches_edwards_scalarmult() { let mut csprng: OsRng = OsRng::new().unwrap(); diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..be2f600 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,8 @@ +//! Crate-local prelude (for alloc-dependent features like `Vec`) + +// TODO: switch to alloc::prelude +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use alloc::vec::Vec; + +#[cfg(feature = "std")] +pub use std::vec::Vec; diff --git a/src/ristretto.rs b/src/ristretto.rs index 6be5310..c0041f5 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -178,6 +178,9 @@ use subtle::Choice; use edwards::EdwardsPoint; use edwards::EdwardsBasepointTable; +#[allow(unused_imports)] +use prelude::*; + use scalar::Scalar; use curve_models::CompletedPoint; @@ -418,7 +421,7 @@ impl RistrettoPoint { /// } /// # } /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] + #[cfg(feature = "alloc")] pub fn double_and_compress_batch<'a, I>(points: I) -> Vec where I: IntoIterator { @@ -798,7 +801,7 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint // These use iterator combinators to unwrap the underlying points and // forward to the EdwardsPoint implementations. -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl MultiscalarMul for RistrettoPoint { type Point = RistrettoPoint; @@ -816,7 +819,7 @@ impl MultiscalarMul for RistrettoPoint { } } -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl VartimeMultiscalarMul for RistrettoPoint { type Point = RistrettoPoint; @@ -955,6 +958,7 @@ impl Debug for RistrettoPoint { #[cfg(all(test, feature = "stage2_build"))] mod test { + #[cfg(feature = "rand")] use rand::rngs::OsRng; use scalar::Scalar; @@ -1090,6 +1094,7 @@ mod test { } } + #[cfg(feature = "rand")] #[test] fn four_torsion_random() { let mut rng = OsRng::new().unwrap(); @@ -1152,6 +1157,7 @@ mod test { } } + #[cfg(feature = "rand")] #[test] fn random_roundtrip() { let mut rng = OsRng::new().unwrap(); @@ -1164,6 +1170,7 @@ mod test { } } + #[cfg(feature = "rand")] #[test] fn double_and_compress_1024_random_points() { let mut rng = OsRng::new().unwrap(); @@ -1178,6 +1185,7 @@ mod test { } } + #[cfg(feature = "rand")] #[test] fn random_is_valid() { let mut rng = OsRng::new().unwrap(); diff --git a/src/scalar.rs b/src/scalar.rs index beaaec0..7bf330d 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -725,7 +725,7 @@ impl Scalar { /// assert_eq!(scalars[3], Scalar::from(11u64).invert()); /// # } /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] + #[cfg(feature = "alloc")] pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar { // This code is essentially identical to the FieldElement // implementation, and is documented there. Unfortunately, diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index 8d6c109..0053570 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -24,6 +24,9 @@ use traits::MultiscalarMul; #[cfg(any(feature = "alloc", feature = "std"))] use traits::VartimeMultiscalarMul; +#[allow(unused_imports)] +use prelude::*; + /// Perform multiscalar multiplication by the interleaved window /// method, also known as Straus' method (since it was apparently /// [first published][solution] by Straus in 1964, as a solution to [a @@ -48,7 +51,7 @@ use traits::VartimeMultiscalarMul; #[cfg(any(feature = "alloc", feature = "std"))] pub struct Straus {} -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl MultiscalarMul for Straus { type Point = EdwardsPoint; @@ -145,7 +148,7 @@ impl MultiscalarMul for Straus { } } -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl VartimeMultiscalarMul for Straus { type Point = EdwardsPoint; From d62fd8ebe40d2a42c6d93fabcba2e55aba1cef91 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 23 Jul 2018 10:59:11 -0700 Subject: [PATCH 51/58] Add prelude import to scalar.rs --- src/scalar.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 7bf330d..703369b 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -148,6 +148,9 @@ use core::cmp::{Eq, PartialEq}; use core::iter::{Product, Sum}; use core::borrow::Borrow; +#[allow(unused_imports)] +use prelude::*; + use rand::{Rng, CryptoRng}; use digest::Digest; From 82a5e18c29c56e830e898bcea82c8875267beecd Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 25 Jul 2018 10:54:31 -0700 Subject: [PATCH 52/58] Update docs to point to multiscalar traits --- src/edwards.rs | 8 +++++--- src/ristretto.rs | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index f58b182..98512fa 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -57,11 +57,13 @@ //! `EdwardsBasepointTable`, which performs constant-time fixed-base //! scalar multiplication; //! -//! * the `edwards::multiscalar_mul` function, which performs +//! * an implementation of the +//! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for //! constant-time variable-base multiscalar multiplication; //! -//! * the `edwards::vartime::multiscalar_mul` function, which -//! performs variable-time variable-base multiscalar multiplication. +//! * an implementation of the +//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html) +//! trait for variable-time variable-base multiscalar multiplication; //! //! ## Implementation //! diff --git a/src/ristretto.rs b/src/ristretto.rs index c0041f5..771b8ff 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -98,11 +98,13 @@ //! `RistrettoBasepointTable`, which performs constant-time fixed-base //! scalar multiplication; //! -//! * the `ristretto::multiscalar_mul` function, which performs +//! * an implementation of the +//! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for //! constant-time variable-base multiscalar multiplication; //! -//! * the `ristretto::vartime::multiscalar_mul` function, which -//! performs variable-time variable-base multiscalar multiplication. +//! * an implementation of the +//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html) +//! trait for variable-time variable-base multiscalar multiplication; //! //! ## Random Points and Hashing to Ristretto //! From b7dab8d08341bcceef73c36adf1875c10c677007 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 15 May 2018 14:57:37 -0700 Subject: [PATCH 53/58] Add iterator length checks to multiscalar muls. This partially re-adds functionality removed in commit d2ce1ce5dc7133f8fe7f96ebd03a2242b042621f We would like to require ExactSizeIterator, but unfortunately we can't do that, since ExactSizeIterators aren't chainable, for (in my opinion) silly reasons (chaining two 4-billion-element ExactSizeIterators could overflow on 32-bit systems). Instead we inspect the size hints manually and assert that the lower and upper bounds are all equal. --- src/edwards.rs | 60 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 98512fa..68e6b9f 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -556,21 +556,31 @@ impl MultiscalarMul for EdwardsPoint { J: IntoIterator, J::Item: Borrow, { - // XXX later when we do more fancy multiscalar mults, we can - // delegate based on the iter's size hint -- hdevalence + // Sanity-check lengths of input iterators + let mut scalars = scalars.into_iter(); + let mut points = points.into_iter(); + + // Lower and upper bounds on iterators + let (s_lo, s_hi) = scalars.by_ref().size_hint(); + let (p_lo, p_hi) = points.by_ref().size_hint(); + + // They should all be equal + assert_eq!(s_lo, p_lo); + assert_eq!(s_hi, Some(s_lo)); + assert_eq!(p_hi, Some(p_lo)); + + // Now we know there's a single size. When we do + // size-dependent algorithm dispatch, use this as the hint. + let _size = s_lo; // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] - { - use backend::avx2::scalar_mul::straus::Straus; - Straus::multiscalar_mul(scalars, points) - } + use backend::avx2::scalar_mul::straus::Straus; // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] - { - use scalar_mul::straus::Straus; - Straus::multiscalar_mul(scalars, points) - } + use scalar_mul::straus::Straus; + + Straus::multiscalar_mul(scalars, points) } } @@ -584,21 +594,31 @@ impl VartimeMultiscalarMul for EdwardsPoint { I::Item: Borrow, J: IntoIterator>, { - // XXX later when we do more fancy multiscalar mults, we can - // delegate based on the iter's size hint -- hdevalence + // Sanity-check lengths of input iterators + let mut scalars = scalars.into_iter(); + let mut points = points.into_iter(); + + // Lower and upper bounds on iterators + let (s_lo, s_hi) = scalars.by_ref().size_hint(); + let (p_lo, p_hi) = points.by_ref().size_hint(); + + // They should all be equal + assert_eq!(s_lo, p_lo); + assert_eq!(s_hi, Some(s_lo)); + assert_eq!(p_hi, Some(p_lo)); + + // Now we know there's a single size. When we do + // size-dependent algorithm dispatch, use this as the hint. + let _size = s_lo; // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] - { - use backend::avx2::scalar_mul::straus::Straus; - Straus::optional_multiscalar_mul(scalars, points) - } + use backend::avx2::scalar_mul::straus::Straus; // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] - { - use scalar_mul::straus::Straus; - Straus::optional_multiscalar_mul(scalars, points) - } + use scalar_mul::straus::Straus; + + Straus::optional_multiscalar_mul(scalars, points) } } From e4ad0ec60aaec430ed1719249fbed44f8bed20b5 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 25 Jul 2018 12:23:08 -0700 Subject: [PATCH 54/58] Remove outdated note about powers-of-two --- src/ristretto.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index 771b8ff..eff2919 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -400,9 +400,6 @@ impl RistrettoPoint { /// \mathrm{enc}( [2]P\_1), \ldots, \mathrm{enc}( [2]P\_n ) \\) /// in a batch. /// - /// This function has optimal performance when the batch size is a - /// power of two, but this is not a requirement. - /// /// ``` /// # extern crate curve25519_dalek; /// # use curve25519_dalek::ristretto::RistrettoPoint; From a1e2c83d3183f1f83c86b675cd18be867509566b Mon Sep 17 00:00:00 2001 From: Sam Scott Date: Wed, 25 Jul 2018 18:52:52 -0400 Subject: [PATCH 55/58] Fix distribution of curve points for hashing to Ristretto points. --- src/ristretto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index c0041f5..0976eef 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -646,7 +646,7 @@ impl RistrettoPoint { let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); let mut r_2_bytes = [0u8; 32]; - r_2_bytes.copy_from_slice(&output.as_slice()[0..32]); + r_2_bytes.copy_from_slice(&output.as_slice()[32..64]); let r_2 = FieldElement::from_bytes(&r_2_bytes); let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); From 288625418dd9f88691f0cd7f150d09a499de2667 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 26 Jul 2018 12:40:24 -0700 Subject: [PATCH 56/58] Migrate to packed_simd from core::simd --- Cargo.toml | 4 +++- build.rs | 4 +++- src/backend/avx2/constants.rs | 2 +- src/backend/avx2/field.rs | 2 +- src/lib.rs | 4 +++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f8b1503..c9a0114 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ generic-array = "0.9" clear_on_drop = "=0.2.3" subtle = { version = "0.7", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } +packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true } [build-dependencies] rand = { version = "0.5", default-features = false } @@ -57,6 +58,7 @@ generic-array = "0.9" clear_on_drop = "=0.2.3" subtle = { version = "0.7", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } +packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true } [features] nightly = ["subtle/nightly", "clear_on_drop/nightly"] @@ -71,7 +73,7 @@ u32_backend = [] u64_backend = [] # The AVX2 backend uses u32x8s with u64x4 products. # It uses the u64 code for serial operations. -avx2_backend = ["nightly", "u64_backend"] +avx2_backend = ["nightly", "u64_backend", "packed_simd"] # Signals that we're in the main build stage. This is off by default, # to signal stage 1 of the build, where build.rs loads the library diff --git a/build.rs b/build.rs index 6295fdd..cc26334 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,5 @@ #![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] -#![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))] #![allow(unused_variables)] #![allow(non_snake_case)] #![allow(dead_code)] @@ -15,6 +14,9 @@ extern crate generic_array; extern crate rand; extern crate subtle; +#[cfg(all(feature = "nightly", feature = "avx2_backend"))] +extern crate packed_simd; + use std::env; use std::fs::File; use std::io::Write; diff --git a/src/backend/avx2/constants.rs b/src/backend/avx2/constants.rs index fa31c9d..76a644a 100644 --- a/src/backend/avx2/constants.rs +++ b/src/backend/avx2/constants.rs @@ -10,7 +10,7 @@ //! This module contains constants used by the AVX2 backend. -use core::simd::u32x8; +use packed_simd::u32x8; use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; use backend::avx2::field::FieldElement32x4; diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 173d28e..6f4e1fd 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -40,7 +40,7 @@ const C_LANES64: u8 = 0b00_11_00_00; const D_LANES64: u8 = 0b11_00_00_00; use core::ops::{Add, Mul, Neg}; -use core::simd::{i32x8, u32x8, u64x4, IntoBits}; +use packed_simd::{i32x8, u32x8, u64x4, IntoBits}; use backend::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO}; use backend::u64::field::FieldElement64; diff --git a/src/lib.rs b/src/lib.rs index aaf8ace..862c81a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ #![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(feature = "nightly", feature(external_doc))] -#![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))] // Refuse to compile if documentation is missing, but only on nightly. // @@ -39,6 +38,9 @@ extern crate alloc; #[macro_use] extern crate std; +#[cfg(all(feature = "nightly", feature = "avx2_backend"))] +extern crate packed_simd; + extern crate rand; extern crate clear_on_drop; extern crate byteorder; From 259e2cd1883bed56aa40f81304d523ab69a47cfb Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 26 Jul 2018 19:04:56 -0700 Subject: [PATCH 57/58] Expose a Ristretto::from_uniform_bytes function. Why expose this instead of `from_hash`? Because it allows constructing arbitrary-length chains of orthogonal generators from a XOF. --- src/ristretto.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index 0976eef..00149b9 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -576,19 +576,10 @@ impl RistrettoPoint { /// point should be unknown. The map is applied twice and the /// results are added, to ensure a uniform distribution. pub fn random(rng: &mut T) -> Self { - let mut field_bytes = [0u8; 32]; + let mut uniform_bytes = [0u8; 64]; + rng.fill(&mut uniform_bytes); - rng.fill(&mut field_bytes); - let r_1 = FieldElement::from_bytes(&field_bytes); - let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); - - rng.fill(&mut field_bytes); - let r_2 = FieldElement::from_bytes(&field_bytes); - let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); - - // Applying Elligator twice and adding the results ensures a - // uniform distribution. - &R_1 + &R_2 + RistrettoPoint::from_uniform_bytes(&uniform_bytes) } /// Hash a slice of bytes into a `RistrettoPoint`. @@ -639,14 +630,31 @@ impl RistrettoPoint { { // dealing with generic arrays is clumsy, until const generics land let output = hash.result(); + let mut output_bytes = [0u8; 64]; + output_bytes.copy_from_slice(&output.as_slice()); + RistrettoPoint::from_uniform_bytes(&output_bytes) + } + + /// Construct a `RistrettoPoint` from 64 bytes of data. + /// + /// If the input bytes are uniformly distributed, the resulting + /// point will be uniformly distributed over the group, and its + /// discrete log with respect to other points should be unknown. + /// + /// # Implementation + /// + /// This function splits the input array into two 32-byte halves, + /// takes the low 255 bits of each half mod p, applies the + /// Ristretto-flavored Elligator map to each, and adds the results. + pub fn from_uniform_bytes(bytes: &[u8; 64]) -> RistrettoPoint { let mut r_1_bytes = [0u8; 32]; - r_1_bytes.copy_from_slice(&output.as_slice()[0..32]); + r_1_bytes.copy_from_slice(&bytes[0..32]); let r_1 = FieldElement::from_bytes(&r_1_bytes); let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); let mut r_2_bytes = [0u8; 32]; - r_2_bytes.copy_from_slice(&output.as_slice()[32..64]); + r_2_bytes.copy_from_slice(&bytes[32..64]); let r_2 = FieldElement::from_bytes(&r_2_bytes); let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); From 8a488d3032e70cf9151f315eca179ce6eb9d8180 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 26 Jul 2018 20:33:57 -0700 Subject: [PATCH 58/58] Bump version number to 0.19.0 Add an interim version prior to 1.0.0-pre.0 in order to fix the AVX2 build. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c9a0114..0976cdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.18.0" +version = "0.19.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md"