mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-06 20:41:14 +00:00
Merge branch 'release/0.17.0'
This commit is contained in:
commit
32288e8625
21 changed files with 623 additions and 598 deletions
31
.travis.yml
31
.travis.yml
|
|
@ -2,34 +2,29 @@ language: rust
|
||||||
|
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- beta
|
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES=''
|
# Tests the u32 backend
|
||||||
|
- TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u32_backend'
|
||||||
|
# Tests the u64 backend
|
||||||
|
- TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend'
|
||||||
|
# Tests the avx2 backend
|
||||||
|
- TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto'
|
||||||
|
# Tests serde support and default feature selection
|
||||||
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde'
|
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde'
|
||||||
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
|
# Tests building without std. We have to select a backend, so we select the one
|
||||||
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly'
|
# most likely to be useful in an embedded environment.
|
||||||
- TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES=''
|
- TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend'
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
exclude:
|
||||||
# Test nightly features, such as radix_51, only on nightly.
|
# Test the avx2 backend only on nightly
|
||||||
- rust: stable
|
- rust: stable
|
||||||
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
|
env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto'
|
||||||
- rust: beta
|
|
||||||
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
|
|
||||||
- rust: stable
|
|
||||||
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly'
|
|
||||||
- rust: beta
|
|
||||||
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly'
|
|
||||||
# Test no_std only on nightly.
|
# Test no_std only on nightly.
|
||||||
- rust: stable
|
- rust: stable
|
||||||
env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES=''
|
env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend'
|
||||||
- rust: beta
|
|
||||||
env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES=''
|
|
||||||
- rust: nightly
|
|
||||||
env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='alloc'
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
|
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
|
||||||
|
|
|
||||||
39
Cargo.toml
39
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "0.16.4"
|
version = "0.17.0"
|
||||||
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||||
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -41,33 +41,40 @@ harness = false
|
||||||
# match exactly, since the build.rs uses the crate itself as a library.
|
# match exactly, since the build.rs uses the crate itself as a library.
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = {version = "1", default-features = false }
|
rand = { version = "0.5.0-pre.2", default-features = false }
|
||||||
|
byteorder = { version = "1", default-features = false }
|
||||||
digest = "0.7"
|
digest = "0.7"
|
||||||
generic-array = "0.9"
|
generic-array = "0.9"
|
||||||
clear_on_drop = "=0.2.3"
|
clear_on_drop = "=0.2.3"
|
||||||
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
rand = { version = "0.4", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
byteorder = "1"
|
rand = { version = "0.5.0-pre.2", default-features = false }
|
||||||
|
byteorder = { version = "1", default-features = false }
|
||||||
digest = "0.7"
|
digest = "0.7"
|
||||||
generic-array = "0.9"
|
generic-array = "0.9"
|
||||||
clear_on_drop = "=0.2.3"
|
clear_on_drop = "=0.2.3"
|
||||||
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
# Allowing rand to be optional during builds causes a build failure when compiling for no_std targets
|
|
||||||
rand = { version = "0.4", optional = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = ["radix_51", "subtle/nightly", "clear_on_drop/nightly"]
|
nightly = ["subtle/nightly", "clear_on_drop/nightly"]
|
||||||
default = ["std"]
|
default = ["std", "u64_backend"]
|
||||||
std = ["rand", "subtle/std"]
|
std = ["subtle/std", "rand/std"]
|
||||||
alloc = []
|
alloc = []
|
||||||
yolocrypto = ["avx2_backend"]
|
yolocrypto = []
|
||||||
# Radix-51 arithmetic using u128
|
|
||||||
radix_51 = []
|
# The u32 backend uses u32s with u64 products.
|
||||||
# Include precomputed basepoint tables. This is off by default so that build.rs can generate the tables, and then re-enabled by build.rs in the main-stage compilation.
|
u32_backend = []
|
||||||
precomputed_tables = []
|
# The u64 backend uses u64s with u128 products.
|
||||||
# experimental avx2 support
|
u64_backend = []
|
||||||
avx2_backend = ["nightly"]
|
# The AVX2 backend uses u32x8s with u64x4 products.
|
||||||
|
# It uses the u64 code for serial operations.
|
||||||
|
avx2_backend = ["nightly", "u64_backend"]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# into the build script. Then, the build.rs emits the stage2_build
|
||||||
|
# feature before the main-stage compilation.
|
||||||
|
stage2_build = []
|
||||||
|
|
|
||||||
31
README.md
31
README.md
|
|
@ -50,7 +50,7 @@ make doc-internal
|
||||||
To import `curve25519-dalek`, add the following to the dependencies section of
|
To import `curve25519-dalek`, add the following to the dependencies section of
|
||||||
your project's `Cargo.toml`:
|
your project's `Cargo.toml`:
|
||||||
```toml
|
```toml
|
||||||
curve25519-dalek = "^0.16"
|
curve25519-dalek = "^0.17"
|
||||||
```
|
```
|
||||||
Then import the crate as:
|
Then import the crate as:
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
|
|
@ -59,25 +59,38 @@ extern crate curve25519_dalek;
|
||||||
|
|
||||||
# Backends and Features
|
# 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**.
|
||||||
|
|
||||||
Curve arithmetic is implemented using one of the following backends:
|
Curve arithmetic is implemented using one of the following backends:
|
||||||
|
|
||||||
* a `u32` backend using `u64` products;
|
* a `u32` backend using `u64` products;
|
||||||
* a `u64` backend using `u128` products, available using the `nightly` feature;
|
* a `u64` backend using `u128` products;
|
||||||
* an experimental AVX2 backend, available using the `yolocrypto` feature when
|
* an experimental AVX2 backend, available using the `yolocrypto` feature when
|
||||||
compiling for a target with `target_feature=+avx2`.
|
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"
|
||||||
|
cargo build --no-default-features --features "std avx2_backend yolocrypto"
|
||||||
|
```
|
||||||
|
|
||||||
Benchmarks are run using [`criterion.rs`][criterion]:
|
Benchmarks are run using [`criterion.rs`][criterion]:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo bench # u32 backend
|
# You must set RUSTFLAGS to enable AVX2 support.
|
||||||
cargo bench --features="nightly" # u64 backend
|
export RUSTFLAGS="-C target_cpu=native"
|
||||||
cargo bench --features="nightly yolocrypto" # u64 or avx2 if available
|
cargo bench --no-default-features --features "std u32_backend"
|
||||||
|
cargo bench --no-default-features --features "std u64_backend"
|
||||||
|
cargo bench --no-default-features --features "std avx2_backend yolocrypto"
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Please see [CONTRIBUTING.md][contributing].
|
Please see [CONTRIBUTING.md][contributing].
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
use rand::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate criterion;
|
extern crate criterion;
|
||||||
|
|
@ -18,7 +18,10 @@ static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384,
|
||||||
|
|
||||||
mod edwards_benches {
|
mod edwards_benches {
|
||||||
use super::*;
|
use super::*;
|
||||||
use curve25519_dalek::edwards::{self, EdwardsPoint};
|
use curve25519_dalek::edwards;
|
||||||
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
|
use curve25519_dalek::traits::MultiscalarMul;
|
||||||
|
use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
fn compress(c: &mut Criterion) {
|
fn compress(c: &mut Criterion) {
|
||||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
@ -56,7 +59,7 @@ mod edwards_benches {
|
||||||
let a = Scalar::from_u64(298374928).invert();
|
let a = Scalar::from_u64(298374928).invert();
|
||||||
let b = Scalar::from_u64(897987897).invert();
|
let b = Scalar::from_u64(897987897).invert();
|
||||||
let A = B * (b * a);
|
let A = B * (b * a);
|
||||||
bench.iter(|| edwards::vartime::double_scalar_mul_basepoint(&a, &A, &b));
|
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +73,7 @@ mod edwards_benches {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
||||||
.collect();
|
.collect();
|
||||||
b.iter(|| edwards::multiscalar_mul(&scalars, &points));
|
b.iter(|| EdwardsPoint::multiscalar_mul(&scalars, &points));
|
||||||
},
|
},
|
||||||
&MULTISCALAR_SIZES,
|
&MULTISCALAR_SIZES,
|
||||||
);
|
);
|
||||||
|
|
@ -86,7 +89,7 @@ mod edwards_benches {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
||||||
.collect();
|
.collect();
|
||||||
b.iter(|| edwards::vartime::multiscalar_mul(&scalars, &points));
|
b.iter(|| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points));
|
||||||
},
|
},
|
||||||
&MULTISCALAR_SIZES,
|
&MULTISCALAR_SIZES,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
13
build.rs
13
build.rs
|
|
@ -1,4 +1,3 @@
|
||||||
#![cfg_attr(feature = "nightly", feature(i128_type))]
|
|
||||||
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
||||||
#![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))]
|
#![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))]
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
|
|
@ -62,8 +61,8 @@ use curve_models::AffineNielsPoint;
|
||||||
use scalar_mul::window::NafLookupTable8;
|
use scalar_mul::window::NafLookupTable8;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Enable the "precomputed_tables" feature in the main build stage
|
// Enable the "stage2_build" feature in the main build stage
|
||||||
println!("cargo:rustc-cfg=feature=\"precomputed_tables\"\n");
|
println!("cargo:rustc-cfg=feature=\"stage2_build\"\n");
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let dest_path = Path::new(&out_dir).join("basepoint_table.rs");
|
let dest_path = Path::new(&out_dir).join("basepoint_table.rs");
|
||||||
|
|
@ -75,12 +74,12 @@ fn main() {
|
||||||
f.write_all(
|
f.write_all(
|
||||||
format!(
|
format!(
|
||||||
"\n
|
"\n
|
||||||
#[cfg(feature=\"radix_51\")]
|
#[cfg(feature = \"u32_backend\")]
|
||||||
use backend::u64::field::FieldElement64;
|
|
||||||
|
|
||||||
#[cfg(not(feature=\"radix_51\"))]
|
|
||||||
use backend::u32::field::FieldElement32;
|
use backend::u32::field::FieldElement32;
|
||||||
|
|
||||||
|
#[cfg(feature = \"u64_backend\")]
|
||||||
|
use backend::u64::field::FieldElement64;
|
||||||
|
|
||||||
use edwards::EdwardsBasepointTable;
|
use edwards::EdwardsBasepointTable;
|
||||||
|
|
||||||
use curve_models::AffineNielsPoint;
|
use curve_models::AffineNielsPoint;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
// See the comment above the ristretto::notes module.
|
// See the comment above the ristretto::notes module.
|
||||||
#![cfg_attr(all(feature = "nightly", feature="precomputed_tables"), doc(include = "../docs/avx2-notes.md"))]
|
#![cfg_attr(
|
||||||
|
all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md")
|
||||||
|
)]
|
||||||
|
|
||||||
pub(crate) mod field;
|
pub(crate) mod field;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@
|
||||||
|
|
||||||
pub mod variable_base;
|
pub mod variable_base;
|
||||||
|
|
||||||
#[cfg(feature="precomputed_tables")]
|
#[cfg(feature = "stage2_build")]
|
||||||
pub mod vartime_double_base;
|
pub mod vartime_double_base;
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub mod straus;
|
pub mod straus;
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub mod vartime_straus;
|
|
||||||
|
|
|
||||||
|
|
@ -7,48 +7,98 @@
|
||||||
// Authors:
|
// Authors:
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
use clear_on_drop::ClearOnDrop;
|
use clear_on_drop::ClearOnDrop;
|
||||||
|
|
||||||
use traits::Identity;
|
|
||||||
use scalar::Scalar;
|
|
||||||
use edwards::EdwardsPoint;
|
|
||||||
use scalar_mul::window::LookupTable;
|
|
||||||
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
|
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
|
||||||
|
use edwards::EdwardsPoint;
|
||||||
|
use scalar::Scalar;
|
||||||
|
use scalar_mul::window::{LookupTable, NafLookupTable5};
|
||||||
|
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||||
|
|
||||||
/// Perform constant-time, variable-base scalar multiplication.
|
/// Multiscalar multiplication using interleaved window / Straus'
|
||||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
/// method. See the `Straus` struct in the serial backend for more
|
||||||
where
|
/// details.
|
||||||
I: IntoIterator,
|
///
|
||||||
I::Item: Borrow<Scalar>,
|
/// This exists as a seperate implementation from that one because the
|
||||||
J: IntoIterator,
|
/// AVX2 code uses different curve models (it does not pass between
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
/// multiple models during scalar mul), and it has to convert the
|
||||||
{
|
/// point representation on the fly.
|
||||||
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
|
pub struct Straus {}
|
||||||
// for each input point P
|
|
||||||
let lookup_tables: Vec<_> = points
|
|
||||||
.into_iter()
|
|
||||||
.map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let scalar_digits_vec: Vec<_> = scalars
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
.into_iter()
|
impl MultiscalarMul for Straus {
|
||||||
.map(|s| s.borrow().to_radix_16())
|
type Point = EdwardsPoint;
|
||||||
.collect();
|
|
||||||
// Pass ownership to a ClearOnDrop wrapper
|
|
||||||
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
|
|
||||||
|
|
||||||
let mut Q = ExtendedPoint::identity();
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
for j in (0..64).rev() {
|
where
|
||||||
Q = Q.mul_by_pow_2(4);
|
I: IntoIterator,
|
||||||
let it = scalar_digits.iter().zip(lookup_tables.iter());
|
I::Item: Borrow<Scalar>,
|
||||||
for (s_i, lookup_table_i) in it {
|
J: IntoIterator,
|
||||||
// Q = Q + s_{i,j} * P_i
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
Q = &Q + &lookup_table_i.select(s_i[j]);
|
{
|
||||||
|
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
|
||||||
|
// for each input point P
|
||||||
|
let lookup_tables: Vec<_> = points
|
||||||
|
.into_iter()
|
||||||
|
.map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let scalar_digits_vec: Vec<_> = scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.borrow().to_radix_16())
|
||||||
|
.collect();
|
||||||
|
// Pass ownership to a ClearOnDrop wrapper
|
||||||
|
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
|
||||||
|
|
||||||
|
let mut Q = ExtendedPoint::identity();
|
||||||
|
for j in (0..64).rev() {
|
||||||
|
Q = Q.mul_by_pow_2(4);
|
||||||
|
let it = scalar_digits.iter().zip(lookup_tables.iter());
|
||||||
|
for (s_i, lookup_table_i) in it {
|
||||||
|
// Q = Q + s_{i,j} * P_i
|
||||||
|
Q = &Q + &lookup_table_i.select(s_i[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Q.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
impl VartimeMultiscalarMul for Straus {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
|
{
|
||||||
|
let nafs: Vec<_> = scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect();
|
||||||
|
let lookup_tables: Vec<_> = points
|
||||||
|
.into_iter()
|
||||||
|
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut Q = ExtendedPoint::identity();
|
||||||
|
|
||||||
|
for i in (0..255).rev() {
|
||||||
|
Q = Q.double();
|
||||||
|
|
||||||
|
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
||||||
|
if naf[i] > 0 {
|
||||||
|
Q = &Q + &lookup_table.select(naf[i] as usize);
|
||||||
|
} else if naf[i] < 0 {
|
||||||
|
Q = &Q - &lookup_table.select(-naf[i] as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q.into()
|
||||||
}
|
}
|
||||||
Q.into()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
// -*- mode: rust; -*-
|
|
||||||
//
|
|
||||||
// This file is part of curve25519-dalek.
|
|
||||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
|
||||||
// See LICENSE for licensing information.
|
|
||||||
//
|
|
||||||
// Authors:
|
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
|
||||||
|
|
||||||
use traits::Identity;
|
|
||||||
use scalar::Scalar;
|
|
||||||
use edwards::EdwardsPoint;
|
|
||||||
use scalar_mul::window::NafLookupTable5;
|
|
||||||
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
|
|
||||||
|
|
||||||
/// Perform variable-time, variable-base scalar multiplication.
|
|
||||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
|
||||||
where
|
|
||||||
I: IntoIterator,
|
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
|
||||||
let nafs: Vec<_> = scalars
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| c.borrow().non_adjacent_form(5))
|
|
||||||
.collect();
|
|
||||||
let lookup_tables: Vec<_> = points
|
|
||||||
.into_iter()
|
|
||||||
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut Q = ExtendedPoint::identity();
|
|
||||||
|
|
||||||
for i in (0..255).rev() {
|
|
||||||
Q = Q.double();
|
|
||||||
|
|
||||||
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
|
||||||
if naf[i] > 0 {
|
|
||||||
Q = &Q + &lookup_table.select(naf[i] as usize);
|
|
||||||
} else if naf[i] < 0 {
|
|
||||||
Q = &Q - &lookup_table.select(-naf[i] as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Q.into()
|
|
||||||
}
|
|
||||||
|
|
@ -21,12 +21,12 @@
|
||||||
//! `32bit` since identifiers can't start with letters, and the backends
|
//! `32bit` since identifiers can't start with letters, and the backends
|
||||||
//! do use `u32`/`u64`, so this seems like a least-bad option.
|
//! do use `u32`/`u64`, so this seems like a least-bad option.
|
||||||
|
|
||||||
#[cfg(not(feature="radix_51"))]
|
#[cfg(feature = "u32_backend")]
|
||||||
pub mod u32;
|
pub mod u32;
|
||||||
|
|
||||||
#[cfg(feature="radix_51")]
|
#[cfg(feature = "u64_backend")]
|
||||||
pub mod u64;
|
pub mod u64;
|
||||||
|
|
||||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
|
#[cfg(all(feature = "avx2_backend", feature = "yolocrypto", target_feature = "avx2"))]
|
||||||
pub mod avx2;
|
pub mod avx2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ use ristretto::CompressedRistretto;
|
||||||
use montgomery::MontgomeryPoint;
|
use montgomery::MontgomeryPoint;
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
||||||
#[cfg(feature="radix_51")]
|
#[cfg(feature = "u64_backend")]
|
||||||
pub use backend::u64::constants::*;
|
pub use backend::u64::constants::*;
|
||||||
#[cfg(not(feature="radix_51"))]
|
#[cfg(feature = "u32_backend")]
|
||||||
pub use backend::u32::constants::*;
|
pub use backend::u32::constants::*;
|
||||||
|
|
||||||
/// The Ed25519 basepoint, in `CompressedEdwardsY` format.
|
/// The Ed25519 basepoint, in `CompressedEdwardsY` format.
|
||||||
|
|
@ -85,14 +85,14 @@ pub const BASEPOINT_ORDER: Scalar = Scalar{
|
||||||
|
|
||||||
// Precomputed basepoint table is generated into a file by build.rs
|
// Precomputed basepoint table is generated into a file by build.rs
|
||||||
|
|
||||||
#[cfg(feature="precomputed_tables")]
|
#[cfg(feature = "stage2_build")]
|
||||||
include!(concat!(env!("OUT_DIR"), "/basepoint_table.rs"));
|
include!(concat!(env!("OUT_DIR"), "/basepoint_table.rs"));
|
||||||
|
|
||||||
#[cfg(feature="precomputed_tables")]
|
#[cfg(feature = "stage2_build")]
|
||||||
use ristretto::RistrettoBasepointTable;
|
use ristretto::RistrettoBasepointTable;
|
||||||
|
|
||||||
/// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication.
|
/// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication.
|
||||||
#[cfg(feature="precomputed_tables")]
|
#[cfg(feature = "stage2_build")]
|
||||||
pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable
|
pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable
|
||||||
= RistrettoBasepointTable(ED25519_BASEPOINT_TABLE);
|
= RistrettoBasepointTable(ED25519_BASEPOINT_TABLE);
|
||||||
|
|
||||||
|
|
@ -149,8 +149,8 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that d = -121665/121666
|
/// Test that d = -121665/121666
|
||||||
#[cfg(not(feature="radix_51"))]
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "u32_backend")]
|
||||||
fn test_d_vs_ratio() {
|
fn test_d_vs_ratio() {
|
||||||
use backend::u32::field::FieldElement32;
|
use backend::u32::field::FieldElement32;
|
||||||
let a = -&FieldElement32([121665,0,0,0,0,0,0,0,0,0]);
|
let a = -&FieldElement32([121665,0,0,0,0,0,0,0,0,0]);
|
||||||
|
|
@ -162,8 +162,8 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that d = -121665/121666
|
/// Test that d = -121665/121666
|
||||||
#[cfg(feature="radix_51")]
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "u64_backend")]
|
||||||
fn test_d_vs_ratio() {
|
fn test_d_vs_ratio() {
|
||||||
use backend::u64::field::FieldElement64;
|
use backend::u64::field::FieldElement64;
|
||||||
let a = -&FieldElement64([121665,0,0,0,0]);
|
let a = -&FieldElement64([121665,0,0,0,0]);
|
||||||
|
|
|
||||||
262
src/edwards.rs
262
src/edwards.rs
|
|
@ -112,6 +112,7 @@ use field::FieldElement;
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
||||||
use montgomery::MontgomeryPoint;
|
use montgomery::MontgomeryPoint;
|
||||||
|
|
||||||
use curve_models::ProjectivePoint;
|
use curve_models::ProjectivePoint;
|
||||||
use curve_models::CompletedPoint;
|
use curve_models::CompletedPoint;
|
||||||
use curve_models::AffineNielsPoint;
|
use curve_models::AffineNielsPoint;
|
||||||
|
|
@ -121,6 +122,8 @@ use scalar_mul::window::LookupTable;
|
||||||
|
|
||||||
use traits::{Identity, IsIdentity};
|
use traits::{Identity, IsIdentity};
|
||||||
use traits::ValidityCheck;
|
use traits::ValidityCheck;
|
||||||
|
use traits::MultiscalarMul;
|
||||||
|
use traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Compressed points
|
// Compressed points
|
||||||
|
|
@ -490,13 +493,13 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint {
|
||||||
/// `EdwardsBasepointTable` is approximately 4x faster.
|
/// `EdwardsBasepointTable` is approximately 4x faster.
|
||||||
fn mul(self, scalar: &'b Scalar) -> EdwardsPoint {
|
fn mul(self, scalar: &'b Scalar) -> EdwardsPoint {
|
||||||
// If we built with AVX2, use the AVX2 backend.
|
// If we built with AVX2, use the AVX2 backend.
|
||||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
|
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||||
{
|
{
|
||||||
use backend::avx2::scalar_mul::variable_base::mul;
|
use backend::avx2::scalar_mul::variable_base::mul;
|
||||||
mul(self, scalar)
|
mul(self, scalar)
|
||||||
}
|
}
|
||||||
// Otherwise, use the serial backend:
|
// Otherwise, use the serial backend:
|
||||||
#[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))]
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
{
|
{
|
||||||
use scalar_mul::variable_base::mul;
|
use scalar_mul::variable_base::mul;
|
||||||
mul(self, scalar)
|
mul(self, scalar)
|
||||||
|
|
@ -516,71 +519,89 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
// ------------------------------------------------------------------------
|
||||||
/// (possibly secret) points, compute
|
// Multiscalar Multiplication impls
|
||||||
/// $$
|
// ------------------------------------------------------------------------
|
||||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
|
||||||
/// $$
|
|
||||||
///
|
|
||||||
/// This function has the same behaviour as
|
|
||||||
/// `vartime::multiscalar_mul` but is constant-time.
|
|
||||||
///
|
|
||||||
/// It is an error to call this function with two iterators of different lengths.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
|
||||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
|
||||||
/// must be `Borrow<Scalar>` (or `Borrow<EdwardsPoint>`), to allow
|
|
||||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use curve25519_dalek::{constants, edwards};
|
|
||||||
/// 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);
|
|
||||||
///
|
|
||||||
/// // Some points
|
|
||||||
/// let P = constants::ED25519_BASEPOINT_POINT;
|
|
||||||
/// let Q = P + P;
|
|
||||||
/// let R = P + Q;
|
|
||||||
///
|
|
||||||
/// // A1 = a*P + b*Q + c*R
|
|
||||||
/// let abc = [a,b,c];
|
|
||||||
/// let A1 = edwards::multiscalar_mul(&abc, &[P,Q,R]);
|
|
||||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
|
||||||
///
|
|
||||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
|
||||||
/// let minus_abc = abc.iter().map(|x| -x);
|
|
||||||
/// let A2 = edwards::multiscalar_mul(minus_abc, &[P,Q,R]);
|
|
||||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
|
||||||
///
|
|
||||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
|
||||||
/// ```
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
|
||||||
where I: IntoIterator,
|
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
|
||||||
// XXX later when we do more fancy multiscalar mults, we can
|
|
||||||
// delegate based on the iter's size hint -- hdevalence
|
|
||||||
|
|
||||||
// If we built with AVX2, use the AVX2 backend.
|
// These use the iterator's size hint and the target settings to
|
||||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
|
// forward to a specific backend implementation.
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
impl MultiscalarMul for EdwardsPoint {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
{
|
{
|
||||||
use backend::avx2::scalar_mul::straus::multiscalar_mul;
|
// XXX later when we do more fancy multiscalar mults, we can
|
||||||
multiscalar_mul(scalars, points)
|
// delegate based on the iter's size hint -- hdevalence
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
// Otherwise, proceed as normal:
|
||||||
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
|
{
|
||||||
|
use scalar_mul::straus::Straus;
|
||||||
|
Straus::multiscalar_mul(scalars, points)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, proceed as normal:
|
}
|
||||||
#[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))]
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
impl VartimeMultiscalarMul for EdwardsPoint {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
{
|
{
|
||||||
use scalar_mul::straus::multiscalar_mul;
|
// XXX later when we do more fancy multiscalar mults, we can
|
||||||
multiscalar_mul(scalars, points)
|
// delegate based on the iter's size hint -- hdevalence
|
||||||
|
|
||||||
|
// 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::vartime_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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
#[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:
|
||||||
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
|
{
|
||||||
|
use scalar_mul::vartime_double_base::mul;
|
||||||
|
mul(a, A, b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -783,103 +804,11 @@ impl Debug for EdwardsBasepointTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Variable-time functions
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
pub mod vartime {
|
|
||||||
//! Variable-time operations on curve points, useful for non-secret data.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
|
||||||
/// $$
|
|
||||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
|
||||||
/// $$
|
|
||||||
///
|
|
||||||
/// This function has the same behaviour as
|
|
||||||
/// `edwards::multiscalar_mul` but operates on non-secret data.
|
|
||||||
///
|
|
||||||
/// It is an error to call this function with two iterators of different lengths.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
|
||||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
|
||||||
/// must be `Borrow<Scalar>` (or `Borrow<EdwardsPoint>`), to allow
|
|
||||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use curve25519_dalek::{constants, edwards};
|
|
||||||
/// 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);
|
|
||||||
///
|
|
||||||
/// // Some points
|
|
||||||
/// let P = constants::ED25519_BASEPOINT_POINT;
|
|
||||||
/// let Q = P + P;
|
|
||||||
/// let R = P + Q;
|
|
||||||
///
|
|
||||||
/// // A1 = a*P + b*Q + c*R
|
|
||||||
/// let abc = [a,b,c];
|
|
||||||
/// let A1 = edwards::vartime::multiscalar_mul(&abc, &[P,Q,R]);
|
|
||||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
|
||||||
///
|
|
||||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
|
||||||
/// let minus_abc = abc.iter().map(|x| -x);
|
|
||||||
/// let A2 = edwards::vartime::multiscalar_mul(minus_abc, &[P,Q,R]);
|
|
||||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
|
||||||
///
|
|
||||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
|
||||||
/// ```
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
|
||||||
where I: IntoIterator,
|
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
|
||||||
// XXX later when we do more fancy multiscalar mults, we can delegate
|
|
||||||
// based on the iter's size hint -- hdevalence
|
|
||||||
// If we built with AVX2, use the AVX2 backend.
|
|
||||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
|
|
||||||
{
|
|
||||||
use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul;
|
|
||||||
multiscalar_mul(scalars, points)
|
|
||||||
}
|
|
||||||
// Otherwise, proceed as normal:
|
|
||||||
#[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))]
|
|
||||||
{
|
|
||||||
use scalar_mul::vartime_straus::multiscalar_mul;
|
|
||||||
multiscalar_mul(scalars, points)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
|
||||||
// If we built with AVX2, use the AVX2 backend.
|
|
||||||
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
|
|
||||||
{
|
|
||||||
use backend::avx2::scalar_mul::vartime_double_base::mul;
|
|
||||||
mul(a, A, b)
|
|
||||||
}
|
|
||||||
// Otherwise, proceed as normal:
|
|
||||||
#[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))]
|
|
||||||
{
|
|
||||||
use scalar_mul::vartime_double_base::mul;
|
|
||||||
mul(a, A, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "stage2_build"))]
|
||||||
mod test {
|
mod test {
|
||||||
use field::FieldElement;
|
use field::FieldElement;
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
@ -971,7 +900,6 @@ mod test {
|
||||||
|
|
||||||
/// Test that computing 1*basepoint gives the correct basepoint.
|
/// Test that computing 1*basepoint gives the correct basepoint.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn basepoint_mult_one_vs_basepoint() {
|
fn basepoint_mult_one_vs_basepoint() {
|
||||||
let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one();
|
let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one();
|
||||||
let compressed = bp.compress();
|
let compressed = bp.compress();
|
||||||
|
|
@ -980,7 +908,6 @@ mod test {
|
||||||
|
|
||||||
/// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint.
|
/// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn basepoint_table_basepoint_function_correct() {
|
fn basepoint_table_basepoint_function_correct() {
|
||||||
let bp = constants::ED25519_BASEPOINT_TABLE.basepoint();
|
let bp = constants::ED25519_BASEPOINT_TABLE.basepoint();
|
||||||
assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
|
assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
|
||||||
|
|
@ -1031,7 +958,6 @@ mod test {
|
||||||
|
|
||||||
/// Sanity check for conversion to precomputed points
|
/// Sanity check for conversion to precomputed points
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn to_affine_niels_clears_denominators() {
|
fn to_affine_niels_clears_denominators() {
|
||||||
// construct a point as aB so it has denominators (ie. Z != 1)
|
// construct a point as aB so it has denominators (ie. Z != 1)
|
||||||
let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
||||||
|
|
@ -1043,7 +969,6 @@ mod test {
|
||||||
|
|
||||||
/// Test basepoint_mult versus a known scalar multiple from ed25519.py
|
/// Test basepoint_mult versus a known scalar multiple from ed25519.py
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn basepoint_mult_vs_ed25519py() {
|
fn basepoint_mult_vs_ed25519py() {
|
||||||
let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
||||||
assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
|
assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
|
||||||
|
|
@ -1051,7 +976,6 @@ mod test {
|
||||||
|
|
||||||
/// Test that multiplication by the basepoint order kills the basepoint
|
/// Test that multiplication by the basepoint order kills the basepoint
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn basepoint_mult_by_basepoint_order() {
|
fn basepoint_mult_by_basepoint_order() {
|
||||||
let B = &constants::ED25519_BASEPOINT_TABLE;
|
let B = &constants::ED25519_BASEPOINT_TABLE;
|
||||||
let should_be_id = B * &constants::BASEPOINT_ORDER;
|
let should_be_id = B * &constants::BASEPOINT_ORDER;
|
||||||
|
|
@ -1060,11 +984,9 @@ mod test {
|
||||||
|
|
||||||
/// Test precomputed basepoint mult
|
/// Test precomputed basepoint mult
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn test_precomputed_basepoint_mult() {
|
fn test_precomputed_basepoint_mult() {
|
||||||
let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT_POINT);
|
|
||||||
let aB_1 = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
let aB_1 = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR;
|
||||||
let aB_2 = &table * &A_SCALAR;
|
let aB_2 = &constants::ED25519_BASEPOINT_POINT * &A_SCALAR;
|
||||||
assert_eq!(aB_1.compress(), aB_2.compress());
|
assert_eq!(aB_1.compress(), aB_2.compress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1084,7 +1006,6 @@ mod test {
|
||||||
|
|
||||||
/// Test that computing 2*basepoint is the same as basepoint.double()
|
/// Test that computing 2*basepoint is the same as basepoint.double()
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn basepoint_mult_two_vs_basepoint2() {
|
fn basepoint_mult_two_vs_basepoint2() {
|
||||||
let two = Scalar::from_u64(2);
|
let two = Scalar::from_u64(2);
|
||||||
let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two;
|
let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two;
|
||||||
|
|
@ -1177,7 +1098,7 @@ mod test {
|
||||||
/// and enable `debug_assert!()`. This performs many scalar
|
/// and enable `debug_assert!()`. This performs many scalar
|
||||||
/// multiplications to attempt to trigger possible overflows etc.
|
/// multiplications to attempt to trigger possible overflows etc.
|
||||||
///
|
///
|
||||||
/// For instance, the `radix_51` `Mul` implementation for
|
/// For instance, the `u64` `Mul` implementation for
|
||||||
/// `FieldElements` requires the input `Limb`s to be bounded by
|
/// `FieldElements` requires the input `Limb`s to be bounded by
|
||||||
/// 2^54, but we cannot enforce this dynamically at runtime, or
|
/// 2^54, but we cannot enforce this dynamically at runtime, or
|
||||||
/// statically at compile time (until Rust gets type-level
|
/// statically at compile time (until Rust gets type-level
|
||||||
|
|
@ -1210,17 +1131,16 @@ mod test {
|
||||||
|
|
||||||
/// Test double_scalar_mul_vartime vs ed25519.py
|
/// Test double_scalar_mul_vartime vs ed25519.py
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn double_scalar_mul_basepoint_vs_ed25519py() {
|
fn double_scalar_mul_basepoint_vs_ed25519py() {
|
||||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||||
let result = vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
|
let result = EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
|
||||||
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiscalar_mul_vs_ed25519py() {
|
fn multiscalar_mul_vs_ed25519py() {
|
||||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||||
let result = vartime::multiscalar_mul(
|
let result = EdwardsPoint::vartime_multiscalar_mul(
|
||||||
&[A_SCALAR, B_SCALAR],
|
&[A_SCALAR, B_SCALAR],
|
||||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||||
);
|
);
|
||||||
|
|
@ -1230,11 +1150,11 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn multiscalar_mul_vartime_vs_consttime() {
|
fn multiscalar_mul_vartime_vs_consttime() {
|
||||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||||
let result_vartime = vartime::multiscalar_mul(
|
let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
|
||||||
&[A_SCALAR, B_SCALAR],
|
&[A_SCALAR, B_SCALAR],
|
||||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||||
);
|
);
|
||||||
let result_consttime = multiscalar_mul(
|
let result_consttime = EdwardsPoint::multiscalar_mul(
|
||||||
&[A_SCALAR, B_SCALAR],
|
&[A_SCALAR, B_SCALAR],
|
||||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -32,24 +32,24 @@ use subtle::ConstantTimeEq;
|
||||||
use constants;
|
use constants;
|
||||||
use backend;
|
use backend;
|
||||||
|
|
||||||
#[cfg(feature="radix_51")]
|
#[cfg(feature = "u64_backend")]
|
||||||
pub use backend::u64::field::*;
|
pub use backend::u64::field::*;
|
||||||
/// A `FieldElement` represents an element of the field
|
/// A `FieldElement` represents an element of the field
|
||||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||||
///
|
///
|
||||||
/// The `FieldElement` type is an alias for one of the platform-specific
|
/// The `FieldElement` type is an alias for one of the platform-specific
|
||||||
/// implementations.
|
/// implementations.
|
||||||
#[cfg(feature="radix_51")]
|
#[cfg(feature = "u64_backend")]
|
||||||
pub type FieldElement = backend::u64::field::FieldElement64;
|
pub type FieldElement = backend::u64::field::FieldElement64;
|
||||||
|
|
||||||
#[cfg(not(feature="radix_51"))]
|
#[cfg(feature = "u32_backend")]
|
||||||
pub use backend::u32::field::*;
|
pub use backend::u32::field::*;
|
||||||
/// A `FieldElement` represents an element of the field
|
/// A `FieldElement` represents an element of the field
|
||||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||||
///
|
///
|
||||||
/// The `FieldElement` type is an alias for one of the platform-specific
|
/// The `FieldElement` type is an alias for one of the platform-specific
|
||||||
/// implementations.
|
/// implementations.
|
||||||
#[cfg(not(feature="radix_51"))]
|
#[cfg(feature = "u32_backend")]
|
||||||
pub type FieldElement = backend::u32::field::FieldElement32;
|
pub type FieldElement = backend::u32::field::FieldElement32;
|
||||||
|
|
||||||
impl Eq for FieldElement {}
|
impl Eq for FieldElement {}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||||
|
|
||||||
#![cfg_attr(feature = "nightly", feature(i128_type))]
|
|
||||||
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
||||||
#![cfg_attr(feature = "nightly", feature(external_doc))]
|
#![cfg_attr(feature = "nightly", feature(external_doc))]
|
||||||
#![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))]
|
#![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))]
|
||||||
|
|
@ -35,15 +34,11 @@
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
extern crate rand;
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
extern crate rand;
|
||||||
extern crate clear_on_drop;
|
extern crate clear_on_drop;
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
||||||
// The `Digest` trait is implemented using `generic_array`, so we need it
|
// The `Digest` trait is implemented using `generic_array`, so we need it
|
||||||
|
|
|
||||||
|
|
@ -279,12 +279,12 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
|
||||||
// Tests
|
// Tests
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "stage2_build"))]
|
||||||
mod test {
|
mod test {
|
||||||
use constants;
|
use constants;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use rand::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
/// Test Montgomery -> Edwards on the X/Ed25519 basepoint
|
/// Test Montgomery -> Edwards on the X/Ed25519 basepoint
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -338,7 +338,6 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn montgomery_ladder_matches_edwards_scalarmult() {
|
fn montgomery_ladder_matches_edwards_scalarmult() {
|
||||||
let mut csprng: OsRng = OsRng::new().unwrap();
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
177
src/ristretto.rs
177
src/ristretto.rs
|
|
@ -161,7 +161,7 @@
|
||||||
// missing).
|
// missing).
|
||||||
//
|
//
|
||||||
// This hack is also used in the avx2 notes.
|
// This hack is also used in the avx2 notes.
|
||||||
#[cfg_attr(all(feature = "nightly", feature="precomputed_tables"), doc(include = "../docs/ristretto-notes.md"))]
|
#[cfg_attr(all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/ristretto-notes.md"))]
|
||||||
mod notes {
|
mod notes {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,8 +172,7 @@ use core::ops::{Mul, MulAssign};
|
||||||
use core::iter::Sum;
|
use core::iter::Sum;
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
use rand::{Rng, CryptoRng};
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
use generic_array::typenum::U64;
|
use generic_array::typenum::U64;
|
||||||
|
|
@ -186,7 +185,6 @@ use subtle::ConditionallyNegatable;
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
use subtle::Choice;
|
use subtle::Choice;
|
||||||
|
|
||||||
use edwards;
|
|
||||||
use edwards::EdwardsPoint;
|
use edwards::EdwardsPoint;
|
||||||
use edwards::EdwardsBasepointTable;
|
use edwards::EdwardsBasepointTable;
|
||||||
|
|
||||||
|
|
@ -194,7 +192,7 @@ use scalar::Scalar;
|
||||||
|
|
||||||
use curve_models::CompletedPoint;
|
use curve_models::CompletedPoint;
|
||||||
|
|
||||||
use traits::Identity;
|
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Compressed points
|
// Compressed points
|
||||||
|
|
@ -406,7 +404,7 @@ impl RistrettoPoint {
|
||||||
/// # extern crate curve25519_dalek;
|
/// # extern crate curve25519_dalek;
|
||||||
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
/// extern crate rand;
|
/// extern crate rand;
|
||||||
/// use rand::OsRng;
|
/// use rand::rngs::OsRng;
|
||||||
///
|
///
|
||||||
/// # // Need fn main() here in comment so the doctest compiles
|
/// # // Need fn main() here in comment so the doctest compiles
|
||||||
/// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
|
/// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
|
||||||
|
|
@ -576,15 +574,14 @@ impl RistrettoPoint {
|
||||||
/// discrete log of the output point with respect to any other
|
/// discrete log of the output point with respect to any other
|
||||||
/// point should be unknown. The map is applied twice and the
|
/// point should be unknown. The map is applied twice and the
|
||||||
/// results are added, to ensure a uniform distribution.
|
/// results are added, to ensure a uniform distribution.
|
||||||
#[cfg(feature = "std")]
|
pub fn random<T: Rng + CryptoRng>(rng: &mut T) -> Self {
|
||||||
pub fn random<T: Rng>(rng: &mut T) -> Self {
|
|
||||||
let mut field_bytes = [0u8; 32];
|
let mut field_bytes = [0u8; 32];
|
||||||
|
|
||||||
rng.fill_bytes(&mut field_bytes);
|
rng.fill(&mut field_bytes);
|
||||||
let r_1 = FieldElement::from_bytes(&field_bytes);
|
let r_1 = FieldElement::from_bytes(&field_bytes);
|
||||||
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
||||||
|
|
||||||
rng.fill_bytes(&mut field_bytes);
|
rng.fill(&mut field_bytes);
|
||||||
let r_2 = FieldElement::from_bytes(&field_bytes);
|
let r_2 = FieldElement::from_bytes(&field_bytes);
|
||||||
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
||||||
|
|
||||||
|
|
@ -790,60 +787,47 @@ define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar);
|
||||||
define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
|
define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
|
||||||
define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
|
define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Multiscalar Multiplication impls
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// These use iterator combinators to unwrap the underlying points and
|
||||||
|
// forward to the EdwardsPoint implementations.
|
||||||
|
|
||||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
|
||||||
/// (possibly secret) points, compute
|
|
||||||
/// $$
|
|
||||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
|
||||||
/// $$
|
|
||||||
///
|
|
||||||
/// This function has the same behaviour as
|
|
||||||
/// `vartime::multiscalar_mul` but is constant-time.
|
|
||||||
///
|
|
||||||
/// It is an error to call this function with two iterators of different lengths.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
|
||||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
|
||||||
/// must be `Borrow<Scalar>` (or `Borrow<RistrettoPoint>`), to allow
|
|
||||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use curve25519_dalek::{constants, ristretto};
|
|
||||||
/// 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);
|
|
||||||
///
|
|
||||||
/// // Some points
|
|
||||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
|
||||||
/// let Q = P + P;
|
|
||||||
/// let R = P + Q;
|
|
||||||
///
|
|
||||||
/// // A1 = a*P + b*Q + c*R
|
|
||||||
/// let abc = [a,b,c];
|
|
||||||
/// let A1 = ristretto::multiscalar_mul(&abc, &[P,Q,R]);
|
|
||||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
|
||||||
///
|
|
||||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
|
||||||
/// let minus_abc = abc.iter().map(|x| -x);
|
|
||||||
/// let A2 = ristretto::multiscalar_mul(minus_abc, &[P,Q,R]);
|
|
||||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
|
||||||
///
|
|
||||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
|
||||||
/// ```
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
impl MultiscalarMul for RistrettoPoint {
|
||||||
where I: IntoIterator,
|
type Point = RistrettoPoint;
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||||
J::Item: Borrow<RistrettoPoint>,
|
where
|
||||||
{
|
I: IntoIterator,
|
||||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
I::Item: Borrow<Scalar>,
|
||||||
RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points))
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<RistrettoPoint>,
|
||||||
|
{
|
||||||
|
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||||
|
RistrettoPoint(
|
||||||
|
EdwardsPoint::multiscalar_mul(scalars, extended_points)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
impl VartimeMultiscalarMul for RistrettoPoint {
|
||||||
|
type Point = RistrettoPoint;
|
||||||
|
|
||||||
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<RistrettoPoint>,
|
||||||
|
{
|
||||||
|
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||||
|
RistrettoPoint(
|
||||||
|
EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A precomputed table of multiples of a basepoint, used to accelerate
|
/// A precomputed table of multiples of a basepoint, used to accelerate
|
||||||
|
|
@ -947,76 +931,13 @@ impl Debug for RistrettoPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Variable-time functions
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
pub mod vartime {
|
|
||||||
//! Variable-time operations on ristretto points, useful for non-secret data.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
|
||||||
/// $$
|
|
||||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
|
||||||
/// $$
|
|
||||||
///
|
|
||||||
/// This function has the same behaviour as
|
|
||||||
/// `vartime::multiscalar_mul` but is constant-time.
|
|
||||||
///
|
|
||||||
/// It is an error to call this function with two iterators of different lengths.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
|
||||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
|
||||||
/// must be `Borrow<Scalar>` (or `Borrow<RistrettoPoint>`), to allow
|
|
||||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use curve25519_dalek::{constants, ristretto};
|
|
||||||
/// 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);
|
|
||||||
///
|
|
||||||
/// // Some points
|
|
||||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
|
||||||
/// let Q = P + P;
|
|
||||||
/// let R = P + Q;
|
|
||||||
///
|
|
||||||
/// // A1 = a*P + b*Q + c*R
|
|
||||||
/// let abc = [a,b,c];
|
|
||||||
/// let A1 = ristretto::vartime::multiscalar_mul(&abc, &[P,Q,R]);
|
|
||||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
|
||||||
///
|
|
||||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
|
||||||
/// let minus_abc = abc.iter().map(|x| -x);
|
|
||||||
/// let A2 = ristretto::vartime::multiscalar_mul(minus_abc, &[P,Q,R]);
|
|
||||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
|
||||||
///
|
|
||||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
|
||||||
/// ```
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
|
||||||
where I: IntoIterator,
|
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
|
||||||
J::Item: Borrow<RistrettoPoint>,
|
|
||||||
{
|
|
||||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
|
||||||
RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "stage2_build"))]
|
||||||
mod test {
|
mod test {
|
||||||
use rand::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
use constants;
|
use constants;
|
||||||
|
|
@ -1152,7 +1073,6 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn four_torsion_random() {
|
fn four_torsion_random() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
||||||
|
|
@ -1215,7 +1135,6 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="precomputed_tables")]
|
|
||||||
fn random_roundtrip() {
|
fn random_roundtrip() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ use core::cmp::{Eq, PartialEq};
|
||||||
use core::iter::{Product, Sum};
|
use core::iter::{Product, Sum};
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
use rand::{Rng, CryptoRng};
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
use generic_array::typenum::U64;
|
use generic_array::typenum::U64;
|
||||||
|
|
@ -39,14 +38,14 @@ use constants;
|
||||||
///
|
///
|
||||||
/// This is a type alias for one of the scalar types in the `backend`
|
/// This is a type alias for one of the scalar types in the `backend`
|
||||||
/// module.
|
/// module.
|
||||||
#[cfg(feature="radix_51")]
|
#[cfg(feature = "u64_backend")]
|
||||||
type UnpackedScalar = backend::u64::scalar::Scalar64;
|
type UnpackedScalar = backend::u64::scalar::Scalar64;
|
||||||
|
|
||||||
/// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed.
|
/// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed.
|
||||||
///
|
///
|
||||||
/// This is a type alias for one of the scalar types in the `backend`
|
/// This is a type alias for one of the scalar types in the `backend`
|
||||||
/// module.
|
/// module.
|
||||||
#[cfg(not(feature="radix_51"))]
|
#[cfg(feature = "u32_backend")]
|
||||||
type UnpackedScalar = backend::u32::scalar::Scalar32;
|
type UnpackedScalar = backend::u32::scalar::Scalar32;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -324,15 +323,15 @@ impl Scalar {
|
||||||
///
|
///
|
||||||
/// # Inputs
|
/// # Inputs
|
||||||
///
|
///
|
||||||
/// * `rng`: any RNG which implements the `rand::Rng` interface.
|
/// * `rng`: any RNG which implements the `rand::CryptoRng` interface.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A random scalar within ℤ/lℤ.
|
/// A random scalar within ℤ/lℤ.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn random<T: Rng>(rng: &mut T) -> Self {
|
pub fn random<T: Rng + CryptoRng>(rng: &mut T) -> Self {
|
||||||
let mut scalar_bytes = [0u8; 64];
|
let mut scalar_bytes = [0u8; 64];
|
||||||
rng.fill_bytes(&mut scalar_bytes);
|
rng.fill(&mut scalar_bytes);
|
||||||
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
|
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,19 @@
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
|
//! Implementations of various scalar multiplication algorithms.
|
||||||
|
//!
|
||||||
|
//! Note that all of these implementations use serial code for field
|
||||||
|
//! arithmetic with the multi-model strategy described in the
|
||||||
|
//! `curve_models` module. The vectorized AVX2 backend has its own
|
||||||
|
//! scalar multiplication implementations, since it only uses one
|
||||||
|
//! curve model.
|
||||||
|
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
|
||||||
pub mod variable_base;
|
pub mod variable_base;
|
||||||
|
|
||||||
#[cfg(feature="precomputed_tables")]
|
#[cfg(feature = "stage2_build")]
|
||||||
pub mod vartime_double_base;
|
pub mod vartime_double_base;
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub mod straus;
|
pub mod straus;
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub mod vartime_straus;
|
|
||||||
|
|
|
||||||
|
|
@ -7,77 +7,187 @@
|
||||||
// Authors:
|
// Authors:
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
|
//! Implementation of the interleaved window method, also known as Straus' method.
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
use clear_on_drop::ClearOnDrop;
|
|
||||||
|
|
||||||
use traits::Identity;
|
|
||||||
use scalar::Scalar;
|
|
||||||
use edwards::EdwardsPoint;
|
use edwards::EdwardsPoint;
|
||||||
use curve_models::ProjectiveNielsPoint;
|
use scalar::Scalar;
|
||||||
use scalar_mul::window::LookupTable;
|
use traits::MultiscalarMul;
|
||||||
|
use traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
/// Perform constant-time, variable-base scalar multiplication.
|
/// Perform multiscalar multiplication by the interleaved window
|
||||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
/// method, also known as Straus' method (since it was apparently
|
||||||
where
|
/// [first published][solution] by Straus in 1964, as a solution to [a
|
||||||
I: IntoIterator,
|
/// problem][problem] posted in the American Mathematical Monthly in
|
||||||
I::Item: Borrow<Scalar>,
|
/// 1963).
|
||||||
J: IntoIterator,
|
///
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
/// It is easy enough to reinvent, and has been repeatedly. The basic
|
||||||
{
|
/// idea is that when computing
|
||||||
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
|
/// \\[
|
||||||
// for each input point P
|
/// Q = s_1 P_1 + \cdots + s_n P_n
|
||||||
let lookup_tables: Vec<_> = points
|
/// \\]
|
||||||
.into_iter()
|
/// by means of additions and doublings, the doublings can be shared
|
||||||
.map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow()))
|
/// across the \\( P_i \\\).
|
||||||
.collect();
|
///
|
||||||
|
/// We implement two versions, a constant-time algorithm using fixed
|
||||||
|
/// windows and a variable-time algorithm using sliding windows. They
|
||||||
|
/// are slight variations on the same idea, and are described in more
|
||||||
|
/// detail in the respective implementations.
|
||||||
|
///
|
||||||
|
/// [solution]: https://www.jstor.org/stable/2310929
|
||||||
|
/// [problem]: https://www.jstor.org/stable/2312273
|
||||||
|
pub struct Straus {}
|
||||||
|
|
||||||
// Setting s_i = i-th scalar, compute
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
//
|
impl MultiscalarMul for Straus {
|
||||||
// s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63,
|
type Point = EdwardsPoint;
|
||||||
//
|
|
||||||
// with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`.
|
|
||||||
//
|
|
||||||
// This puts the scalar digits into a heap-allocated Vec.
|
|
||||||
// To ensure that these are erased, pass ownership of the Vec into a
|
|
||||||
// ClearOnDrop wrapper.
|
|
||||||
let scalar_digits_vec: Vec<_> = scalars
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.borrow().to_radix_16())
|
|
||||||
.collect();
|
|
||||||
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
|
|
||||||
|
|
||||||
// Compute s_1*P_1 + ... + s_n*P_n: since
|
/// Constant-time Straus using a fixed window of size \\(4\\).
|
||||||
//
|
///
|
||||||
// s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63)
|
/// Our goal is to compute
|
||||||
// s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63
|
/// \\[
|
||||||
// s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...)
|
/// Q = s_1 P_1 + \cdots + s_n P_n.
|
||||||
//
|
/// \\]
|
||||||
// we have the two-dimensional sum
|
///
|
||||||
//
|
/// For each point \\( P_i \\), precompute a lookup table of
|
||||||
// s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...)
|
/// \\[
|
||||||
// + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...)
|
/// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
|
||||||
// ...
|
/// \\]
|
||||||
// + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...)
|
///
|
||||||
//
|
/// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
|
||||||
// We sum column-wise top-to-bottom, then right-to-left,
|
/// signed digits \\( s_{i,j} \\), i.e.,
|
||||||
// multiplying by 16 only once per column.
|
/// \\[
|
||||||
//
|
/// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
|
||||||
// This provides the speedup over doing n independent scalar
|
/// \\]
|
||||||
// mults: we perform 63 multiplications by 16 instead of 63*n
|
/// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}|
|
||||||
// multiplications, saving 252*(n-1) doublings.
|
/// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
|
||||||
let mut Q = EdwardsPoint::identity();
|
/// lookup table with a conditional negation: using signed
|
||||||
for j in (0..64).rev() {
|
/// digits halves the required table size.
|
||||||
Q = Q.mul_by_pow_2(4);
|
///
|
||||||
let it = scalar_digits.iter().zip(lookup_tables.iter());
|
/// Then as in the single-base fixed window case, we have
|
||||||
for (s_i, lookup_table_i) in it {
|
/// \\[
|
||||||
// R_i = s_{i,j} * P_i
|
/// \begin{aligned}
|
||||||
let R_i = lookup_table_i.select(s_i[j]);
|
/// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\
|
||||||
// Q = Q + R_i
|
/// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\
|
||||||
Q = (&Q + &R_i).to_extended();
|
/// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
|
||||||
|
/// \end{aligned}
|
||||||
|
/// \\]
|
||||||
|
/// so each \\( s_i P_i \\) can be computed by alternately adding
|
||||||
|
/// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
|
||||||
|
/// repeatedly doubling.
|
||||||
|
///
|
||||||
|
/// Now consider the two-dimensional sum
|
||||||
|
/// \\[
|
||||||
|
/// \begin{aligned}
|
||||||
|
/// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
|
||||||
|
/// + & & + & & + & & & & + & \\\\
|
||||||
|
/// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
|
||||||
|
/// + & & + & & + & & & & + & \\\\
|
||||||
|
/// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\
|
||||||
|
/// + & & + & & + & & & & + & \\\\
|
||||||
|
/// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
|
||||||
|
/// \end{aligned}
|
||||||
|
/// \\]
|
||||||
|
/// The sum of the left-hand column is the result \\( Q \\); by
|
||||||
|
/// computing the two-dimensional sum on the right column-wise,
|
||||||
|
/// top-to-bottom, then right-to-left, we need to multiply by \\(
|
||||||
|
/// 16\\) only once per column, sharing the doublings across all
|
||||||
|
/// of the input points.
|
||||||
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
|
{
|
||||||
|
use clear_on_drop::ClearOnDrop;
|
||||||
|
|
||||||
|
use curve_models::ProjectiveNielsPoint;
|
||||||
|
use scalar_mul::window::LookupTable;
|
||||||
|
use traits::Identity;
|
||||||
|
|
||||||
|
let lookup_tables: Vec<_> = points
|
||||||
|
.into_iter()
|
||||||
|
.map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// This puts the scalar digits into a heap-allocated Vec.
|
||||||
|
// To ensure that these are erased, pass ownership of the Vec into a
|
||||||
|
// ClearOnDrop wrapper.
|
||||||
|
let scalar_digits_vec: Vec<_> = scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.borrow().to_radix_16())
|
||||||
|
.collect();
|
||||||
|
let scalar_digits = ClearOnDrop::new(scalar_digits_vec);
|
||||||
|
|
||||||
|
let mut Q = EdwardsPoint::identity();
|
||||||
|
for j in (0..64).rev() {
|
||||||
|
Q = Q.mul_by_pow_2(4);
|
||||||
|
let it = scalar_digits.iter().zip(lookup_tables.iter());
|
||||||
|
for (s_i, lookup_table_i) in it {
|
||||||
|
// R_i = s_{i,j} * P_i
|
||||||
|
let R_i = lookup_table_i.select(s_i[j]);
|
||||||
|
// Q = Q + R_i
|
||||||
|
Q = (&Q + &R_i).to_extended();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
impl VartimeMultiscalarMul for Straus {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
/// Variable-time Straus using a non-adjacent form of width \\(5\\).
|
||||||
|
///
|
||||||
|
/// This is completely similar to the constant-time code, but we
|
||||||
|
/// use a non-adjacent form for the scalar, and do not do table
|
||||||
|
/// lookups in constant time.
|
||||||
|
///
|
||||||
|
/// 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<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
|
{
|
||||||
|
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
||||||
|
use scalar_mul::window::NafLookupTable5;
|
||||||
|
use traits::Identity;
|
||||||
|
|
||||||
|
let nafs: Vec<_> = scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect();
|
||||||
|
let lookup_tables: Vec<_> = points
|
||||||
|
.into_iter()
|
||||||
|
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut r = ProjectivePoint::identity();
|
||||||
|
|
||||||
|
for i in (0..255).rev() {
|
||||||
|
let mut t: CompletedPoint = r.double();
|
||||||
|
|
||||||
|
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
||||||
|
if naf[i] > 0 {
|
||||||
|
t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
|
||||||
|
} else if naf[i] < 0 {
|
||||||
|
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = t.to_projective();
|
||||||
|
}
|
||||||
|
|
||||||
|
r.to_extended()
|
||||||
}
|
}
|
||||||
Q
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
// -*- mode: rust; -*-
|
|
||||||
//
|
|
||||||
// This file is part of curve25519-dalek.
|
|
||||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
|
||||||
// See LICENSE for licensing information.
|
|
||||||
//
|
|
||||||
// Authors:
|
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
|
||||||
|
|
||||||
use traits::Identity;
|
|
||||||
use scalar::Scalar;
|
|
||||||
use edwards::EdwardsPoint;
|
|
||||||
use curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint};
|
|
||||||
use scalar_mul::window::NafLookupTable5;
|
|
||||||
|
|
||||||
/// Perform variable-time, variable-base scalar multiplication.
|
|
||||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
|
||||||
where
|
|
||||||
I: IntoIterator,
|
|
||||||
I::Item: Borrow<Scalar>,
|
|
||||||
J: IntoIterator,
|
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
|
||||||
let nafs: Vec<_> = scalars
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| c.borrow().non_adjacent_form(5))
|
|
||||||
.collect();
|
|
||||||
let lookup_tables: Vec<_> = points
|
|
||||||
.into_iter()
|
|
||||||
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut r = ProjectivePoint::identity();
|
|
||||||
|
|
||||||
for i in (0..255).rev() {
|
|
||||||
let mut t: CompletedPoint = r.double();
|
|
||||||
|
|
||||||
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
|
||||||
if naf[i] > 0 {
|
|
||||||
t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
|
|
||||||
} else if naf[i] < 0 {
|
|
||||||
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = t.to_projective();
|
|
||||||
}
|
|
||||||
|
|
||||||
r.to_extended()
|
|
||||||
}
|
|
||||||
121
src/traits.rs
121
src/traits.rs
|
|
@ -10,8 +10,12 @@
|
||||||
|
|
||||||
//! Module for common traits.
|
//! Module for common traits.
|
||||||
|
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
use subtle;
|
use subtle;
|
||||||
|
|
||||||
|
use scalar::Scalar;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Public Traits
|
// Public Traits
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -32,12 +36,127 @@ pub trait IsIdentity {
|
||||||
/// Implement generic identity equality testing for a point representations
|
/// Implement generic identity equality testing for a point representations
|
||||||
/// which have constant-time equality testing and a defined identity
|
/// which have constant-time equality testing and a defined identity
|
||||||
/// constructor.
|
/// constructor.
|
||||||
impl<T> IsIdentity for T where T: subtle::ConstantTimeEq + Identity {
|
impl<T> IsIdentity for T
|
||||||
|
where
|
||||||
|
T: subtle::ConstantTimeEq + Identity,
|
||||||
|
{
|
||||||
fn is_identity(&self) -> bool {
|
fn is_identity(&self) -> bool {
|
||||||
self.ct_eq(&T::identity()).unwrap_u8() == 1u8
|
self.ct_eq(&T::identity()).unwrap_u8() == 1u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for constant-time multiscalar multiplication without precomputation.
|
||||||
|
pub trait MultiscalarMul {
|
||||||
|
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
||||||
|
type Point;
|
||||||
|
|
||||||
|
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||||
|
/// public points, compute
|
||||||
|
/// $$
|
||||||
|
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||||
|
/// $$
|
||||||
|
///
|
||||||
|
/// It is an error to call this function with two iterators of different lengths.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||||
|
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||||
|
/// must be `Borrow<Scalar>` (or `Borrow<Point>`), to allow
|
||||||
|
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use curve25519_dalek::constants;
|
||||||
|
/// use curve25519_dalek::traits::MultiscalarMul;
|
||||||
|
/// 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);
|
||||||
|
///
|
||||||
|
/// // Some points
|
||||||
|
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
|
/// let Q = P + P;
|
||||||
|
/// let R = P + Q;
|
||||||
|
///
|
||||||
|
/// // A1 = a*P + b*Q + c*R
|
||||||
|
/// let abc = [a,b,c];
|
||||||
|
/// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
|
||||||
|
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||||
|
///
|
||||||
|
/// // 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]);
|
||||||
|
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||||
|
///
|
||||||
|
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||||
|
/// ```
|
||||||
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Self::Point>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for variable-time multiscalar multiplication without precomputation.
|
||||||
|
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
|
||||||
|
/// public points, compute
|
||||||
|
/// $$
|
||||||
|
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||||
|
/// $$
|
||||||
|
///
|
||||||
|
/// It is an error to call this function with two iterators of different lengths.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||||
|
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||||
|
/// must be `Borrow<Scalar>` (or `Borrow<Point>`), to allow
|
||||||
|
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use curve25519_dalek::constants;
|
||||||
|
/// use curve25519_dalek::traits::MultiscalarMul;
|
||||||
|
/// 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);
|
||||||
|
///
|
||||||
|
/// // Some points
|
||||||
|
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
|
/// let Q = P + P;
|
||||||
|
/// let R = P + Q;
|
||||||
|
///
|
||||||
|
/// // A1 = a*P + b*Q + c*R
|
||||||
|
/// let abc = [a,b,c];
|
||||||
|
/// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
|
||||||
|
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||||
|
///
|
||||||
|
/// // 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]);
|
||||||
|
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||||
|
///
|
||||||
|
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||||
|
/// ```
|
||||||
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Self::Point>;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Private Traits
|
// Private Traits
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue