mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-12 21:20:28 +00:00
Merge branch 'release/0.19.0'
This commit is contained in:
commit
ba3b6178f0
32 changed files with 884 additions and 841 deletions
|
|
@ -15,16 +15,18 @@ env:
|
||||||
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde'
|
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde'
|
||||||
# Tests building without std. We have to select a backend, so we select the one
|
# Tests building without std. We have to select a backend, so we select the one
|
||||||
# most likely to be useful in an embedded environment.
|
# 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:
|
matrix:
|
||||||
exclude:
|
exclude:
|
||||||
# Test the avx2 backend only on nightly
|
# Test the avx2 backend only on nightly
|
||||||
- rust: stable
|
- rust: stable
|
||||||
env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend'
|
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
|
- 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:
|
script:
|
||||||
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
|
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
|
||||||
|
|
|
||||||
20
Cargo.toml
20
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "0.18.0"
|
version = "0.19.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,27 +41,29 @@ 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]
|
||||||
rand = { version = "0.5.0", default-features = false }
|
rand = { version = "0.5", default-features = false }
|
||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false, features = ["i128"] }
|
||||||
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.7", features = ["generic-impls"], default-features = false }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
|
packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rand = { version = "0.5.0", default-features = false }
|
rand = { version = "0.5", default-features = false }
|
||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false, features = ["i128"] }
|
||||||
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.7", features = ["generic-impls"], default-features = false }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
|
packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = ["subtle/nightly", "clear_on_drop/nightly"]
|
nightly = ["subtle/nightly", "clear_on_drop/nightly"]
|
||||||
default = ["std", "u64_backend"]
|
default = ["std", "u64_backend"]
|
||||||
std = ["subtle/std", "rand/std"]
|
std = ["alloc", "subtle/std", "rand/std"]
|
||||||
alloc = []
|
alloc = []
|
||||||
yolocrypto = []
|
yolocrypto = []
|
||||||
|
|
||||||
|
|
@ -71,7 +73,7 @@ u32_backend = []
|
||||||
u64_backend = []
|
u64_backend = []
|
||||||
# The AVX2 backend uses u32x8s with u64x4 products.
|
# The AVX2 backend uses u32x8s with u64x4 products.
|
||||||
# It uses the u64 code for serial operations.
|
# 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,
|
# 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
|
# to signal stage 1 of the build, where build.rs loads the library
|
||||||
|
|
|
||||||
2
LICENSE
2
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
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|
|
||||||
83
README.md
83
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
|
speed and safety benefits of Edwards curve arithmetic, without the pitfalls of
|
||||||
cofactor-related abstraction mismatches.
|
cofactor-related abstraction mismatches.
|
||||||
|
|
||||||
## WARNING
|
## Stability
|
||||||
|
|
||||||
We do not yet consider this code to be production-ready. We intend to
|
We have recently released a `1.0.0-pre.0` version of `curve25519-dalek` and
|
||||||
stabilize a production-ready version `1.0` soon.
|
would greatly appreciate testing and feedback on our API and performance.
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
|
|
@ -59,30 +59,81 @@ extern crate curve25519_dalek;
|
||||||
|
|
||||||
# Backends and Features
|
# Backends and Features
|
||||||
|
|
||||||
The `yolocrypto` feature enables experimental features. The name `yolocrypto`
|
The `nightly` feature enables features available only when using a Rust nightly
|
||||||
is meant to indicate that it is not considered production-ready, and we do not
|
compiler. **It is recommended for security**.
|
||||||
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;
|
* a `u64` backend using `u128` products;
|
||||||
* an `avx2` backend using parallel formulas, available when compiling for a
|
* an `avx2` backend using [parallel formulas][parallel_doc], available
|
||||||
target with `target_feature=+avx2`.
|
when compiling for a target with `target_feature=+avx2`.
|
||||||
|
|
||||||
By default the `u64` backend is selected. To select a specific backend, use:
|
By default the `u64` backend is selected. To select a specific backend, use:
|
||||||
```sh
|
```sh
|
||||||
cargo build --no-default-features --features "std u32_backend"
|
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 u64_backend"
|
||||||
|
# Requires RUSTFLAGS="-C target_feature=+avx2"
|
||||||
cargo build --no-default-features --features "std avx2_backend"
|
cargo build --no-default-features --features "std avx2_backend"
|
||||||
```
|
```
|
||||||
Crates using `curve25519-dalek` can either select a backend on behalf of their
|
Crates using `curve25519-dalek` can either select a backend on behalf of their
|
||||||
users, or expose feature flags that control the `curve25519-dalek` backend.
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
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]:
|
Benchmarks are run using [`criterion.rs`][criterion]:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
@ -93,6 +144,9 @@ cargo bench --no-default-features --features "std u64_backend"
|
||||||
cargo bench --no-default-features --features "std avx2_backend"
|
cargo bench --no-default-features --features "std avx2_backend"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Performance is a secondary goal behind correctness, safety, and
|
||||||
|
clarity, but we aim to be competitive with other implementations.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Please see [CONTRIBUTING.md][contributing].
|
Please see [CONTRIBUTING.md][contributing].
|
||||||
|
|
@ -123,7 +177,8 @@ turn a port of the reference `ref10` implementation. Most of this code,
|
||||||
including the 32-bit field arithmetic, has since been rewritten.
|
including the 32-bit field arithmetic, has since been rewritten.
|
||||||
|
|
||||||
The fast `u32` and `u64` scalar arithmetic was implemented by Andrew Moon, and
|
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.
|
The `no_std` support was contributed by Tony Arcieri.
|
||||||
|
|
||||||
|
|
@ -136,3 +191,5 @@ contributions.
|
||||||
[docs-external]: https://doc.dalek.rs/curve25519_dalek/
|
[docs-external]: https://doc.dalek.rs/curve25519_dalek/
|
||||||
[docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/
|
[docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/
|
||||||
[criterion]: https://github.com/japaric/criterion.rs
|
[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/
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ mod edwards_benches {
|
||||||
|
|
||||||
fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
|
fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
|
||||||
let B = &constants::ED25519_BASEPOINT_TABLE;
|
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| {
|
c.bench_function("Constant-time fixed-base scalar mul", move |b| {
|
||||||
b.iter(|| B * &s)
|
b.iter(|| B * &s)
|
||||||
});
|
});
|
||||||
|
|
@ -47,7 +47,7 @@ mod edwards_benches {
|
||||||
|
|
||||||
fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
|
fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
|
||||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
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| {
|
c.bench_function("Constant-time variable-base scalar mul", move |b| {
|
||||||
b.iter(|| B * &s)
|
b.iter(|| B * &s)
|
||||||
});
|
});
|
||||||
|
|
@ -56,8 +56,8 @@ mod edwards_benches {
|
||||||
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
||||||
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
||||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||||
let a = Scalar::from_u64(298374928).invert();
|
let a = Scalar::from(298374928u64).invert();
|
||||||
let b = Scalar::from_u64(897987897).invert();
|
let b = Scalar::from(897987897u64).invert();
|
||||||
let A = B * (b * a);
|
let A = B * (b * a);
|
||||||
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
||||||
});
|
});
|
||||||
|
|
@ -157,7 +157,7 @@ mod montgomery_benches {
|
||||||
fn montgomery_ladder(c: &mut Criterion) {
|
fn montgomery_ladder(c: &mut Criterion) {
|
||||||
c.bench_function("Montgomery pseudomultiplication", |b| {
|
c.bench_function("Montgomery pseudomultiplication", |b| {
|
||||||
let B = constants::X25519_BASEPOINT;
|
let B = constants::X25519_BASEPOINT;
|
||||||
let s = Scalar::from_u64(897987897).invert();
|
let s = Scalar::from(897987897u64).invert();
|
||||||
b.iter(|| B * s);
|
b.iter(|| B * s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +174,7 @@ mod scalar_benches {
|
||||||
|
|
||||||
fn scalar_inversion(c: &mut Criterion) {
|
fn scalar_inversion(c: &mut Criterion) {
|
||||||
c.bench_function("Scalar inversion", |b| {
|
c.bench_function("Scalar inversion", |b| {
|
||||||
let s = Scalar::from_u64(897987897).invert();
|
let s = Scalar::from(897987897u64).invert();
|
||||||
b.iter(|| s.invert());
|
b.iter(|| s.invert());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
build.rs
9
build.rs
|
|
@ -1,9 +1,11 @@
|
||||||
|
#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))]
|
||||||
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
||||||
#![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))]
|
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
|
extern crate alloc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate clear_on_drop;
|
extern crate clear_on_drop;
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
@ -12,6 +14,9 @@ extern crate generic_array;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate subtle;
|
extern crate subtle;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "nightly", feature = "avx2_backend"))]
|
||||||
|
extern crate packed_simd;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -53,6 +58,8 @@ mod field;
|
||||||
mod curve_models;
|
mod curve_models;
|
||||||
#[path = "src/backend/mod.rs"]
|
#[path = "src/backend/mod.rs"]
|
||||||
mod backend;
|
mod backend;
|
||||||
|
#[path = "src/prelude.rs"]
|
||||||
|
mod prelude;
|
||||||
#[path = "src/scalar_mul/mod.rs"]
|
#[path = "src/scalar_mul/mod.rs"]
|
||||||
mod scalar_mul;
|
mod scalar_mul;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
//! This module contains constants used by the AVX2 backend.
|
//! 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::edwards::{CachedPoint, ExtendedPoint};
|
||||||
use backend::avx2::field::FieldElement32x4;
|
use backend::avx2::field::FieldElement32x4;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -431,7 +431,7 @@ mod test {
|
||||||
|
|
||||||
println!("Testing B +- kB");
|
println!("Testing B +- kB");
|
||||||
let P = constants::ED25519_BASEPOINT_POINT;
|
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);
|
addition_test_helper(P, Q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -510,7 +510,7 @@ mod test {
|
||||||
doubling_test_helper(P);
|
doubling_test_helper(P);
|
||||||
|
|
||||||
println!("Testing [2]([k]B)");
|
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);
|
doubling_test_helper(P);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; coding: utf-8; -*-
|
// -*- mode: rust; coding: utf-8; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -40,7 +40,7 @@ const C_LANES64: u8 = 0b00_11_00_00;
|
||||||
const D_LANES64: u8 = 0b11_00_00_00;
|
const D_LANES64: u8 = 0b11_00_00_00;
|
||||||
|
|
||||||
use core::ops::{Add, Mul, Neg};
|
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::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO};
|
||||||
use backend::u64::field::FieldElement64;
|
use backend::u64::field::FieldElement64;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// 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>
|
||||||
|
|
||||||
// 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(
|
#![cfg_attr(
|
||||||
all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md")
|
all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md")
|
||||||
)]
|
)]
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ use scalar::Scalar;
|
||||||
use scalar_mul::window::{LookupTable, NafLookupTable5};
|
use scalar_mul::window::{LookupTable, NafLookupTable5};
|
||||||
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
/// Multiscalar multiplication using interleaved window / Straus'
|
/// Multiscalar multiplication using interleaved window / Straus'
|
||||||
/// method. See the `Straus` struct in the serial backend for more
|
/// method. See the `Straus` struct in the serial backend for more
|
||||||
/// details.
|
/// details.
|
||||||
|
|
@ -30,7 +33,7 @@ use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||||
/// point representation on the fly.
|
/// point representation on the fly.
|
||||||
pub struct Straus {}
|
pub struct Straus {}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl MultiscalarMul for Straus {
|
impl MultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -68,25 +71,28 @@ impl MultiscalarMul for Straus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl VartimeMultiscalarMul for Straus {
|
impl VartimeMultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
{
|
||||||
let nafs: Vec<_> = scalars
|
let nafs: Vec<_> = scalars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.borrow().non_adjacent_form(5))
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
.collect();
|
.collect();
|
||||||
let lookup_tables: Vec<_> = points
|
let lookup_tables: Vec<_> = match points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
|
||||||
.collect();
|
.collect::<Option<Vec<_>>>()
|
||||||
|
{
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
let mut Q = ExtendedPoint::identity();
|
let mut Q = ExtendedPoint::identity();
|
||||||
|
|
||||||
|
|
@ -101,6 +107,7 @@ impl VartimeMultiscalarMul for Straus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Q.into()
|
|
||||||
|
Some(Q.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -21,6 +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(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")]
|
#[cfg(feature = "u32_backend")]
|
||||||
pub mod u32;
|
pub mod u32;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; coding: utf-8; -*-
|
// -*- mode: rust; coding: utf-8; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -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
|
/// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493
|
||||||
pub(crate) const L: Scalar64 = Scalar64([ 0x0002631a5cf5d3ed, 0x000dea2f79cd6581, 0x000000000014def9, 0x0000000000000000, 0x0000100000000000 ]);
|
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;
|
pub(crate) const LFACTOR: u64 = 0x51da312547e1b;
|
||||||
|
|
||||||
/// `R` = R % L where R = 2^260
|
/// `R` = R % L where R = 2^260
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; coding: utf-8; -*-
|
// -*- mode: rust; coding: utf-8; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -212,12 +212,6 @@ impl Identity for ProjectivePoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProjectivePoint {
|
|
||||||
fn default() -> ProjectivePoint {
|
|
||||||
ProjectivePoint::identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Identity for ProjectiveNielsPoint {
|
impl Identity for ProjectiveNielsPoint {
|
||||||
fn identity() -> ProjectiveNielsPoint {
|
fn identity() -> ProjectiveNielsPoint {
|
||||||
ProjectiveNielsPoint{
|
ProjectiveNielsPoint{
|
||||||
|
|
|
||||||
132
src/edwards.rs
132
src/edwards.rs
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -57,11 +57,13 @@
|
||||||
//! `EdwardsBasepointTable`, which performs constant-time fixed-base
|
//! `EdwardsBasepointTable`, which performs constant-time fixed-base
|
||||||
//! scalar multiplication;
|
//! 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;
|
//! constant-time variable-base multiscalar multiplication;
|
||||||
//!
|
//!
|
||||||
//! * the `edwards::vartime::multiscalar_mul` function, which
|
//! * an implementation of the
|
||||||
//! performs variable-time variable-base multiscalar multiplication.
|
//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html)
|
||||||
|
//! trait for variable-time variable-base multiscalar multiplication;
|
||||||
//!
|
//!
|
||||||
//! ## Implementation
|
//! ## Implementation
|
||||||
//!
|
//!
|
||||||
|
|
@ -90,9 +92,6 @@
|
||||||
// affine and projective cakes and eat both of them too.
|
// affine and projective cakes and eat both of them too.
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::Vec;
|
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::iter::Iterator;
|
use core::iter::Iterator;
|
||||||
use core::ops::{Add, Sub, Neg};
|
use core::ops::{Add, Sub, Neg};
|
||||||
|
|
@ -118,11 +117,17 @@ use curve_models::CompletedPoint;
|
||||||
use curve_models::AffineNielsPoint;
|
use curve_models::AffineNielsPoint;
|
||||||
use curve_models::ProjectiveNielsPoint;
|
use curve_models::ProjectiveNielsPoint;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
use scalar_mul::window::LookupTable;
|
use scalar_mul::window::LookupTable;
|
||||||
|
|
||||||
use traits::{Identity, IsIdentity};
|
use traits::{Identity, IsIdentity};
|
||||||
use traits::ValidityCheck;
|
use traits::ValidityCheck;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::MultiscalarMul;
|
use traits::MultiscalarMul;
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::VartimeMultiscalarMul;
|
use traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -260,6 +265,12 @@ impl Identity for CompressedEdwardsY {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for CompressedEdwardsY {
|
||||||
|
fn default() -> CompressedEdwardsY {
|
||||||
|
CompressedEdwardsY::identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Identity for EdwardsPoint {
|
impl Identity for EdwardsPoint {
|
||||||
fn identity() -> EdwardsPoint {
|
fn identity() -> EdwardsPoint {
|
||||||
EdwardsPoint{ X: FieldElement::zero(),
|
EdwardsPoint{ X: FieldElement::zero(),
|
||||||
|
|
@ -269,14 +280,22 @@ impl Identity for EdwardsPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for EdwardsPoint {
|
||||||
|
fn default() -> EdwardsPoint {
|
||||||
|
EdwardsPoint::identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Validity checks (for debugging, not CT)
|
// Validity checks (for debugging, not CT)
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
impl ValidityCheck for EdwardsPoint {
|
impl ValidityCheck for EdwardsPoint {
|
||||||
// XXX this should also check that T is correct
|
|
||||||
fn is_valid(&self) -> bool {
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,7 +545,7 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
|
||||||
// These use the iterator's size hint and the target settings to
|
// These use the iterator's size hint and the target settings to
|
||||||
// forward to a specific backend implementation.
|
// forward to a specific backend implementation.
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl MultiscalarMul for EdwardsPoint {
|
impl MultiscalarMul for EdwardsPoint {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -537,71 +556,84 @@ impl MultiscalarMul for EdwardsPoint {
|
||||||
J: IntoIterator,
|
J: IntoIterator,
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
J::Item: Borrow<EdwardsPoint>,
|
||||||
{
|
{
|
||||||
// XXX later when we do more fancy multiscalar mults, we can
|
// Sanity-check lengths of input iterators
|
||||||
// delegate based on the iter's size hint -- hdevalence
|
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.
|
// If we built with AVX2, use the AVX2 backend.
|
||||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||||
{
|
use backend::avx2::scalar_mul::straus::Straus;
|
||||||
use backend::avx2::scalar_mul::straus::Straus;
|
|
||||||
Straus::multiscalar_mul(scalars, points)
|
|
||||||
}
|
|
||||||
// Otherwise, proceed as normal:
|
// Otherwise, proceed as normal:
|
||||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
{
|
use scalar_mul::straus::Straus;
|
||||||
use scalar_mul::straus::Straus;
|
|
||||||
Straus::multiscalar_mul(scalars, points)
|
Straus::multiscalar_mul(scalars, points)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl VartimeMultiscalarMul for EdwardsPoint {
|
impl VartimeMultiscalarMul for EdwardsPoint {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
{
|
||||||
// XXX later when we do more fancy multiscalar mults, we can
|
// Sanity-check lengths of input iterators
|
||||||
// delegate based on the iter's size hint -- hdevalence
|
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.
|
// If we built with AVX2, use the AVX2 backend.
|
||||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||||
{
|
use backend::avx2::scalar_mul::straus::Straus;
|
||||||
use backend::avx2::scalar_mul::straus::Straus;
|
|
||||||
Straus::vartime_multiscalar_mul(scalars, points)
|
|
||||||
}
|
|
||||||
// Otherwise, proceed as normal:
|
// Otherwise, proceed as normal:
|
||||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
{
|
use scalar_mul::straus::Straus;
|
||||||
use scalar_mul::straus::Straus;
|
|
||||||
Straus::vartime_multiscalar_mul(scalars, points)
|
Straus::optional_multiscalar_mul(scalars, points)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EdwardsPoint {
|
impl EdwardsPoint {
|
||||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
/// 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")]
|
#[cfg(feature = "stage2_build")]
|
||||||
pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||||
// If we built with AVX2, use the AVX2 backend.
|
// If we built with AVX2, use the AVX2 backend.
|
||||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||||
{
|
use backend::avx2::scalar_mul::vartime_double_base;
|
||||||
use backend::avx2::scalar_mul::vartime_double_base::mul;
|
// Otherwise, use the serial backend:
|
||||||
mul(a, A, b)
|
|
||||||
}
|
|
||||||
// Otherwise, proceed as normal:
|
|
||||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||||
{
|
use scalar_mul::vartime_double_base;
|
||||||
use scalar_mul::vartime_double_base::mul;
|
|
||||||
mul(a, A, b)
|
vartime_double_base::mul(a, A, b)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -698,8 +730,6 @@ impl EdwardsBasepointTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the basepoint for this table as an `EdwardsPoint`.
|
/// Get the basepoint for this table as an `EdwardsPoint`.
|
||||||
///
|
|
||||||
/// XXX maybe this would be better as a `From` impl
|
|
||||||
pub fn basepoint(&self) -> EdwardsPoint {
|
pub fn basepoint(&self) -> EdwardsPoint {
|
||||||
// self.0[0].select(1) = 1*(16^2)^0*B
|
// self.0[0].select(1) = 1*(16^2)^0*B
|
||||||
// but as an `AffineNielsPoint`, so add identity to convert to extended.
|
// but as an `AffineNielsPoint`, so add identity to convert to extended.
|
||||||
|
|
@ -1007,7 +1037,7 @@ 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]
|
||||||
fn basepoint_mult_two_vs_basepoint2() {
|
fn basepoint_mult_two_vs_basepoint2() {
|
||||||
let two = Scalar::from_u64(2);
|
let two = Scalar::from(2u64);
|
||||||
let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two;
|
let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two;
|
||||||
assert_eq!(bp2.compress(), BASE2_CMPRSSD);
|
assert_eq!(bp2.compress(), BASE2_CMPRSSD);
|
||||||
}
|
}
|
||||||
|
|
@ -1033,10 +1063,10 @@ mod test {
|
||||||
// Test that sum works for non-empty iterators
|
// Test that sum works for non-empty iterators
|
||||||
let BASE = constants::ED25519_BASEPOINT_POINT;
|
let BASE = constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
||||||
let s1 = Scalar::from_u64(999);
|
let s1 = Scalar::from(999u64);
|
||||||
let P1 = &BASE * &s1;
|
let P1 = &BASE * &s1;
|
||||||
|
|
||||||
let s2 = Scalar::from_u64(333);
|
let s2 = Scalar::from(333u64);
|
||||||
let P2 = &BASE * &s2;
|
let P2 = &BASE * &s2;
|
||||||
|
|
||||||
let vec = vec![P1.clone(), P2.clone()];
|
let vec = vec![P1.clone(), P2.clone()];
|
||||||
|
|
@ -1051,7 +1081,7 @@ mod test {
|
||||||
assert_eq!(sum, EdwardsPoint::identity());
|
assert_eq!(sum, EdwardsPoint::identity());
|
||||||
|
|
||||||
// Test that sum works on owning iterators
|
// 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 mapped = vec.iter().map(|x| x * &s);
|
||||||
let sum: EdwardsPoint = mapped.sum();
|
let sum: EdwardsPoint = mapped.sum();
|
||||||
|
|
||||||
|
|
|
||||||
98
src/field.rs
98
src/field.rs
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -96,17 +96,10 @@ impl FieldElement {
|
||||||
|
|
||||||
/// Compute (self^(2^250-1), self^11), used as a helper function
|
/// Compute (self^(2^250-1), self^11), used as a helper function
|
||||||
/// within invert() and pow22523().
|
/// 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) {
|
fn pow22501(&self) -> (FieldElement, FieldElement) {
|
||||||
// Instead of managing which temporary variables are used
|
// Instead of managing which temporary variables are used
|
||||||
// for what, we define as many as we need and trust the
|
// for what, we define as many as we need and leave stack
|
||||||
// compiler to reuse stack space as appropriate.
|
// allocation to the compiler
|
||||||
//
|
|
||||||
// XXX testing some examples suggests that this does happen,
|
|
||||||
// but it would be good to check asm for this function.
|
|
||||||
//
|
//
|
||||||
// Each temporary variable t_i is of the form (self)^e_i.
|
// Each temporary variable t_i is of the form (self)^e_i.
|
||||||
// Squaring t_i corresponds to multiplying e_i by 2,
|
// Squaring t_i corresponds to multiplying e_i by 2,
|
||||||
|
|
@ -142,58 +135,34 @@ impl FieldElement {
|
||||||
/// Given a slice of public `FieldElements`, replace each with its inverse.
|
/// Given a slice of public `FieldElements`, replace each with its inverse.
|
||||||
///
|
///
|
||||||
/// All input `FieldElements` **MUST** be nonzero.
|
/// All input `FieldElements` **MUST** be nonzero.
|
||||||
///
|
#[cfg(feature = "alloc")]
|
||||||
/// 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]) {
|
pub fn batch_invert(inputs: &mut [FieldElement]) {
|
||||||
// First, compute the product of all inputs using a product
|
// Montgomery’s Trick and Fast Implementation of Masked AES
|
||||||
// tree:
|
// Genelle, Prouff and Quisquater
|
||||||
//
|
// Section 3.2
|
||||||
// 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.
|
|
||||||
|
|
||||||
let n = inputs.len().next_power_of_two();
|
let n = inputs.len();
|
||||||
let mut tree = vec![FieldElement::one(); 2*n];
|
let mut scratch = vec![FieldElement::one(); n];
|
||||||
tree[n..n+inputs.len()].copy_from_slice(inputs);
|
|
||||||
for i in (1..n).rev() {
|
// Keep an accumulator of all of the previous products
|
||||||
tree[i] = &tree[2*i] * &tree[2*i+1];
|
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
|
// Compute the inverse of all products
|
||||||
// stored at index 1. Compute its inverse.
|
acc = acc.invert();
|
||||||
let allinv = tree[1].invert();
|
|
||||||
|
|
||||||
// To compute y_i = 1/x_i, start at the i-th leaf node of the
|
// Pass through the vector backwards to compute the inverses
|
||||||
// tree, and walk up to the root of the tree, multiplying
|
// in place
|
||||||
// `allinv` by each sibling. This computes
|
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) {
|
||||||
//
|
let tmp = &acc * input;
|
||||||
// y_i = y * (all x_j except x_i)
|
*input = &acc * &scratch;
|
||||||
//
|
acc = tmp;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,12 +170,9 @@ impl FieldElement {
|
||||||
///
|
///
|
||||||
/// The inverse is computed as self^(p-2), since
|
/// The inverse is computed as self^(p-2), since
|
||||||
/// x^(p-2)x = x^(p-1) = 1 (mod p).
|
/// x^(p-2)x = x^(p-1) = 1 (mod p).
|
||||||
//
|
///
|
||||||
// XXX do we want the debug assertion to check for zero? it breaks behaviour
|
/// This function returns zero on input zero.
|
||||||
// such as that such as in curve25519_dalek::montgomery::test::identity_to_monty.
|
|
||||||
pub fn invert(&self) -> FieldElement {
|
pub fn invert(&self) -> FieldElement {
|
||||||
// debug_assert!(*self != FieldElement::zero());
|
|
||||||
|
|
||||||
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
|
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
|
||||||
//
|
//
|
||||||
// nonzero bits of exponent
|
// nonzero bits of exponent
|
||||||
|
|
@ -218,8 +184,7 @@ impl FieldElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raise this field element to the power (p-5)/8 = 2^252 -3.
|
/// Raise this field element to the power (p-5)/8 = 2^252 -3.
|
||||||
/// Used in decoding.
|
fn pow_p58(&self) -> FieldElement {
|
||||||
pub fn pow_p58(&self) -> FieldElement {
|
|
||||||
// The bits of (p-5)/8 are 101111.....11.
|
// The bits of (p-5)/8 are 101111.....11.
|
||||||
//
|
//
|
||||||
// nonzero bits of exponent
|
// nonzero bits of exponent
|
||||||
|
|
@ -496,4 +461,9 @@ mod test {
|
||||||
assert_eq!(one_bytes[i], 0);
|
assert_eq!(one_bytes[i], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn batch_invert_empty() {
|
||||||
|
FieldElement::batch_invert(&mut []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
src/lib.rs
23
src/lib.rs
|
|
@ -1,20 +1,18 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// 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>
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![no_std]
|
||||||
|
|
||||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
|
||||||
|
|
||||||
|
#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))]
|
||||||
#![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 = "avx2_backend"), feature(stdsimd))]
|
|
||||||
|
|
||||||
// Refuse to compile if documentation is missing, but only on nightly.
|
// Refuse to compile if documentation is missing, but only on nightly.
|
||||||
//
|
//
|
||||||
|
|
@ -32,11 +30,17 @@
|
||||||
// External dependencies:
|
// External dependencies:
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
extern crate core;
|
#[macro_use]
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "nightly", feature = "avx2_backend"))]
|
||||||
|
extern crate packed_simd;
|
||||||
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate clear_on_drop;
|
extern crate clear_on_drop;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
@ -94,5 +98,8 @@ pub(crate) mod backend;
|
||||||
// Internal curve models which are not part of the public API.
|
// Internal curve models which are not part of the public API.
|
||||||
pub(crate) mod curve_models;
|
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
|
// Implementations of scalar mul algorithms live here
|
||||||
pub(crate) mod scalar_mul;
|
pub(crate) mod scalar_mul;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -155,6 +155,12 @@ impl Identity for ProjectivePoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ProjectivePoint {
|
||||||
|
fn default() -> ProjectivePoint {
|
||||||
|
ProjectivePoint::identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ConditionallyAssignable for ProjectivePoint {
|
impl ConditionallyAssignable for ProjectivePoint {
|
||||||
fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) {
|
fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) {
|
||||||
self.U.conditional_assign(&that.U, choice);
|
self.U.conditional_assign(&that.U, choice);
|
||||||
|
|
@ -284,6 +290,7 @@ mod test {
|
||||||
use constants;
|
use constants;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
/// Test Montgomery -> Edwards on the X/Ed25519 basepoint
|
/// Test Montgomery -> Edwards on the X/Ed25519 basepoint
|
||||||
|
|
@ -337,6 +344,7 @@ mod test {
|
||||||
assert_eq!(u18, u18_unred);
|
assert_eq!(u18, u18_unred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
#[test]
|
#[test]
|
||||||
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();
|
||||||
|
|
|
||||||
8
src/prelude.rs
Normal file
8
src/prelude.rs
Normal file
|
|
@ -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;
|
||||||
147
src/ristretto.rs
147
src/ristretto.rs
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
// affine and projective cakes and eat both of them too.
|
// affine and projective cakes and eat both of them too.
|
||||||
#![allow(non_snake_case)]
|
#![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
|
//! # The Ristretto Group
|
||||||
//!
|
//!
|
||||||
|
|
@ -50,8 +51,10 @@
|
||||||
//! this [additional restriction][ristretto_coffee] gives the
|
//! this [additional restriction][ristretto_coffee] gives the
|
||||||
//! _Ristretto_ encoding.
|
//! _Ristretto_ encoding.
|
||||||
//!
|
//!
|
||||||
//! More details
|
//! More details on why Ristretto is necessary can be found in the
|
||||||
//! are described in the *Implementation* section below. Ristretto
|
//! [Why Ristretto?][why_ristretto] section of the Ristretto website.
|
||||||
|
//!
|
||||||
|
//! Ristretto
|
||||||
//! points are provided in `curve25519-dalek` by the `RistrettoPoint`
|
//! points are provided in `curve25519-dalek` by the `RistrettoPoint`
|
||||||
//! struct.
|
//! struct.
|
||||||
//!
|
//!
|
||||||
|
|
@ -95,11 +98,13 @@
|
||||||
//! `RistrettoBasepointTable`, which performs constant-time fixed-base
|
//! `RistrettoBasepointTable`, which performs constant-time fixed-base
|
||||||
//! scalar multiplication;
|
//! 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;
|
//! constant-time variable-base multiscalar multiplication;
|
||||||
//!
|
//!
|
||||||
//! * the `ristretto::vartime::multiscalar_mul` function, which
|
//! * an implementation of the
|
||||||
//! performs variable-time variable-base multiscalar multiplication.
|
//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html)
|
||||||
|
//! trait for variable-time variable-base multiscalar multiplication;
|
||||||
//!
|
//!
|
||||||
//! ## Random Points and Hashing to Ristretto
|
//! ## Random Points and Hashing to Ristretto
|
||||||
//!
|
//!
|
||||||
|
|
@ -137,8 +142,7 @@
|
||||||
//! using Edwards formulas.
|
//! using Edwards formulas.
|
||||||
//!
|
//!
|
||||||
//! Notes on the details of the encoding can be found in the
|
//! Notes on the details of the encoding can be found in the
|
||||||
//! [`ristretto::notes`][ristretto_notes] submodule of the internal `curve25519-dalek`
|
//! [Details][ristretto_notes] section of the Ristretto website.
|
||||||
//! documentation.
|
|
||||||
//!
|
//!
|
||||||
//! [cryptonote]:
|
//! [cryptonote]:
|
||||||
//! https://moderncrypto.org/mail-archive/curves/2017/000898.html
|
//! https://moderncrypto.org/mail-archive/curves/2017/000898.html
|
||||||
|
|
@ -147,23 +151,11 @@
|
||||||
//! [ristretto_coffee]:
|
//! [ristretto_coffee]:
|
||||||
//! https://en.wikipedia.org/wiki/Ristretto
|
//! https://en.wikipedia.org/wiki/Ristretto
|
||||||
//! [ristretto_notes]:
|
//! [ristretto_notes]:
|
||||||
//! https://doc-internal.dalek.rs/curve25519_dalek/ristretto/notes/index.html
|
//! https://ristretto.group/details/index.html
|
||||||
|
//! [why_ristretto]:
|
||||||
|
//! https://ristretto.group/why_ristretto.html
|
||||||
// Conditionally include the Ristretto notes if:
|
//! [ristretto_main]:
|
||||||
// - we're on nightly (so we can include docs at all)
|
//! https://ristretto.group/
|
||||||
// - 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 {
|
|
||||||
}
|
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::ops::{Add, Sub, Neg};
|
use core::ops::{Add, Sub, Neg};
|
||||||
|
|
@ -188,11 +180,16 @@ use subtle::Choice;
|
||||||
use edwards::EdwardsPoint;
|
use edwards::EdwardsPoint;
|
||||||
use edwards::EdwardsBasepointTable;
|
use edwards::EdwardsBasepointTable;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
||||||
use curve_models::CompletedPoint;
|
use curve_models::CompletedPoint;
|
||||||
|
|
||||||
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
use traits::Identity;
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
|
use traits::{MultiscalarMul, VartimeMultiscalarMul};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Compressed points
|
// Compressed points
|
||||||
|
|
@ -279,6 +276,12 @@ impl Identity for CompressedRistretto {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for CompressedRistretto {
|
||||||
|
fn default() -> CompressedRistretto {
|
||||||
|
CompressedRistretto::identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Serde support
|
// Serde support
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -397,9 +400,6 @@ impl RistrettoPoint {
|
||||||
/// \mathrm{enc}( [2]P\_1), \ldots, \mathrm{enc}( [2]P\_n ) \\)
|
/// \mathrm{enc}( [2]P\_1), \ldots, \mathrm{enc}( [2]P\_n ) \\)
|
||||||
/// in a batch.
|
/// 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;
|
/// # extern crate curve25519_dalek;
|
||||||
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
|
|
@ -420,7 +420,7 @@ impl RistrettoPoint {
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto>
|
pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto>
|
||||||
where I: IntoIterator<Item = &'a RistrettoPoint>
|
where I: IntoIterator<Item = &'a RistrettoPoint>
|
||||||
{
|
{
|
||||||
|
|
@ -575,19 +575,10 @@ impl RistrettoPoint {
|
||||||
/// 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.
|
||||||
pub fn random<T: Rng + CryptoRng>(rng: &mut T) -> Self {
|
pub fn random<T: Rng + CryptoRng>(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);
|
RistrettoPoint::from_uniform_bytes(&uniform_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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hash a slice of bytes into a `RistrettoPoint`.
|
/// Hash a slice of bytes into a `RistrettoPoint`.
|
||||||
|
|
@ -638,14 +629,31 @@ impl RistrettoPoint {
|
||||||
{
|
{
|
||||||
// dealing with generic arrays is clumsy, until const generics land
|
// dealing with generic arrays is clumsy, until const generics land
|
||||||
let output = hash.result();
|
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];
|
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 = FieldElement::from_bytes(&r_1_bytes);
|
||||||
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
||||||
|
|
||||||
let mut r_2_bytes = [0u8; 32];
|
let mut r_2_bytes = [0u8; 32];
|
||||||
r_2_bytes.copy_from_slice(&output.as_slice()[0..32]);
|
r_2_bytes.copy_from_slice(&bytes[32..64]);
|
||||||
let r_2 = FieldElement::from_bytes(&r_2_bytes);
|
let r_2 = FieldElement::from_bytes(&r_2_bytes);
|
||||||
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
||||||
|
|
||||||
|
|
@ -661,6 +669,12 @@ impl Identity for RistrettoPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for RistrettoPoint {
|
||||||
|
fn default() -> RistrettoPoint {
|
||||||
|
RistrettoPoint::identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Equality
|
// Equality
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -794,7 +808,7 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint
|
||||||
// These use iterator combinators to unwrap the underlying points and
|
// These use iterator combinators to unwrap the underlying points and
|
||||||
// forward to the EdwardsPoint implementations.
|
// forward to the EdwardsPoint implementations.
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl MultiscalarMul for RistrettoPoint {
|
impl MultiscalarMul for RistrettoPoint {
|
||||||
type Point = RistrettoPoint;
|
type Point = RistrettoPoint;
|
||||||
|
|
||||||
|
|
@ -812,20 +826,34 @@ impl MultiscalarMul for RistrettoPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl VartimeMultiscalarMul for RistrettoPoint {
|
impl VartimeMultiscalarMul for RistrettoPoint {
|
||||||
type Point = RistrettoPoint;
|
type Point = RistrettoPoint;
|
||||||
|
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<RistrettoPoint>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator<Item = Option<RistrettoPoint>>,
|
||||||
J::Item: Borrow<RistrettoPoint>,
|
|
||||||
{
|
{
|
||||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
RistrettoPoint(
|
||||||
EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points)
|
EdwardsPoint::vartime_double_scalar_mul_basepoint(a, &A.0, b)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -839,7 +867,7 @@ impl VartimeMultiscalarMul for RistrettoPoint {
|
||||||
/// use curve25519_dalek::constants;
|
/// use curve25519_dalek::constants;
|
||||||
/// use curve25519_dalek::scalar::Scalar;
|
/// use curve25519_dalek::scalar::Scalar;
|
||||||
///
|
///
|
||||||
/// let a = Scalar::from_u64(87329482);
|
/// let a = Scalar::from(87329482u64);
|
||||||
/// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE;
|
/// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE;
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -937,6 +965,7 @@ impl Debug for RistrettoPoint {
|
||||||
|
|
||||||
#[cfg(all(test, feature = "stage2_build"))]
|
#[cfg(all(test, feature = "stage2_build"))]
|
||||||
mod test {
|
mod test {
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
@ -959,7 +988,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn scalarmult_ristrettopoint_works_both_ways() {
|
fn scalarmult_ristrettopoint_works_both_ways() {
|
||||||
let P = constants::RISTRETTO_BASEPOINT_POINT;
|
let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
let s = Scalar::from_u64(999);
|
let s = Scalar::from(999u64);
|
||||||
|
|
||||||
let P1 = &P * &s;
|
let P1 = &P * &s;
|
||||||
let P2 = &s * &P;
|
let P2 = &s * &P;
|
||||||
|
|
@ -973,10 +1002,10 @@ mod test {
|
||||||
// Test that sum works for non-empty iterators
|
// Test that sum works for non-empty iterators
|
||||||
let BASE = constants::RISTRETTO_BASEPOINT_POINT;
|
let BASE = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
|
|
||||||
let s1 = Scalar::from_u64(999);
|
let s1 = Scalar::from(999u64);
|
||||||
let P1 = &BASE * &s1;
|
let P1 = &BASE * &s1;
|
||||||
|
|
||||||
let s2 = Scalar::from_u64(333);
|
let s2 = Scalar::from(333u64);
|
||||||
let P2 = &BASE * &s2;
|
let P2 = &BASE * &s2;
|
||||||
|
|
||||||
let vec = vec![P1.clone(), P2.clone()];
|
let vec = vec![P1.clone(), P2.clone()];
|
||||||
|
|
@ -991,7 +1020,7 @@ mod test {
|
||||||
assert_eq!(sum, RistrettoPoint::identity());
|
assert_eq!(sum, RistrettoPoint::identity());
|
||||||
|
|
||||||
// Test that sum works on owning iterators
|
// 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 mapped = vec.iter().map(|x| x * &s);
|
||||||
let sum: RistrettoPoint = mapped.sum();
|
let sum: RistrettoPoint = mapped.sum();
|
||||||
|
|
||||||
|
|
@ -1072,6 +1101,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
#[test]
|
#[test]
|
||||||
fn four_torsion_random() {
|
fn four_torsion_random() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
@ -1134,6 +1164,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
#[test]
|
#[test]
|
||||||
fn random_roundtrip() {
|
fn random_roundtrip() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
@ -1146,6 +1177,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
#[test]
|
#[test]
|
||||||
fn double_and_compress_1024_random_points() {
|
fn double_and_compress_1024_random_points() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
@ -1160,6 +1192,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rand")]
|
||||||
#[test]
|
#[test]
|
||||||
fn random_is_valid() {
|
fn random_is_valid() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
|
|
||||||
487
src/scalar.rs
487
src/scalar.rs
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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
|
// Portions Copyright 2017 Brian Smith
|
||||||
// See LICENSE for licensing information.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
|
|
@ -11,6 +11,132 @@
|
||||||
// - Brian Smith <brian@briansmith.org>
|
// - Brian Smith <brian@briansmith.org>
|
||||||
|
|
||||||
//! Arithmetic on scalars (integers mod the group order).
|
//! 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 \\).
|
||||||
|
//!
|
||||||
|
//! 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).
|
||||||
|
//!
|
||||||
|
//! 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:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use curve25519_dalek::scalar::Scalar;
|
||||||
|
//!
|
||||||
|
//! let one_as_bytes: [u8; 32] = Scalar::one().to_bytes();
|
||||||
|
//! let a: Option<Scalar> = 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> = 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);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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).
|
||||||
|
//!
|
||||||
|
//! 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 sha2;
|
||||||
|
//! #
|
||||||
|
//! # fn main() {
|
||||||
|
//! use sha2::{Digest, Sha512};
|
||||||
|
//! use curve25519_dalek::scalar::Scalar;
|
||||||
|
//!
|
||||||
|
//! // Hashing a single byte slice
|
||||||
|
//! let a = Scalar::hash_from_bytes::<Sha512>(b"Abolish ICE");
|
||||||
|
//!
|
||||||
|
//! // 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);
|
||||||
|
//!
|
||||||
|
//! assert_eq!(a, a2);
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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
|
||||||
|
//! 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.
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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::fmt::Debug;
|
||||||
use core::ops::Neg;
|
use core::ops::Neg;
|
||||||
|
|
@ -22,6 +148,9 @@ use core::cmp::{Eq, PartialEq};
|
||||||
use core::iter::{Product, Sum};
|
use core::iter::{Product, Sum};
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
use rand::{Rng, CryptoRng};
|
use rand::{Rng, CryptoRng};
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
|
|
@ -51,38 +180,20 @@ type UnpackedScalar = backend::u32::scalar::Scalar32;
|
||||||
|
|
||||||
/// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which
|
/// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which
|
||||||
/// represents an element of \\(\mathbb Z / \ell\\).
|
/// 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)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Scalar {
|
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
|
/// # 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.
|
/// 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],
|
pub(crate) bytes: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,6 +429,77 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Scalar {
|
||||||
|
fn default() -> Scalar {
|
||||||
|
Scalar::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for Scalar {
|
||||||
|
fn from(x: u8) -> Scalar {
|
||||||
|
let mut s_bytes = [0u8; 32];
|
||||||
|
s_bytes[0] = x;
|
||||||
|
Scalar{ bytes: s_bytes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> 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<u32> 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<u64> for 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(42u64);
|
||||||
|
/// let six = Scalar::from(6u64);
|
||||||
|
/// let seven = Scalar::from(7u64);
|
||||||
|
///
|
||||||
|
/// assert!(fourtytwo == six * seven);
|
||||||
|
/// ```
|
||||||
|
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<u128> 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 {
|
impl Scalar {
|
||||||
/// Return a `Scalar` chosen uniformly at random using a user-provided RNG.
|
/// Return a `Scalar` chosen uniformly at random using a user-provided RNG.
|
||||||
///
|
///
|
||||||
|
|
@ -328,7 +510,21 @@ impl Scalar {
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A random scalar within ℤ/lℤ.
|
/// A random scalar within ℤ/lℤ.
|
||||||
#[cfg(feature = "std")]
|
///
|
||||||
|
/// # 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);
|
||||||
|
/// # }
|
||||||
pub fn random<T: Rng + CryptoRng>(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(&mut scalar_bytes);
|
rng.fill(&mut scalar_bytes);
|
||||||
|
|
@ -348,6 +544,7 @@ impl Scalar {
|
||||||
/// # extern crate curve25519_dalek;
|
/// # extern crate curve25519_dalek;
|
||||||
/// # use curve25519_dalek::scalar::Scalar;
|
/// # use curve25519_dalek::scalar::Scalar;
|
||||||
/// extern crate sha2;
|
/// extern crate sha2;
|
||||||
|
///
|
||||||
/// use sha2::Sha512;
|
/// use sha2::Sha512;
|
||||||
///
|
///
|
||||||
/// # // Need fn main() here in comment so the doctest compiles
|
/// # // Need fn main() here in comment so the doctest compiles
|
||||||
|
|
@ -357,7 +554,6 @@ impl Scalar {
|
||||||
/// let s = Scalar::hash_from_bytes::<Sha512>(msg.as_bytes());
|
/// let s = Scalar::hash_from_bytes::<Sha512>(msg.as_bytes());
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
pub fn hash_from_bytes<D>(input: &[u8]) -> Scalar
|
pub fn hash_from_bytes<D>(input: &[u8]) -> Scalar
|
||||||
where D: Digest<OutputSize = U64> + Default
|
where D: Digest<OutputSize = U64> + Default
|
||||||
{
|
{
|
||||||
|
|
@ -371,21 +567,70 @@ impl Scalar {
|
||||||
/// Use this instead of `hash_from_bytes` if it is more convenient
|
/// Use this instead of `hash_from_bytes` if it is more convenient
|
||||||
/// to stream data into the `Digest` than to pass a single byte
|
/// to stream data into the `Digest` than to pass a single byte
|
||||||
/// slice.
|
/// 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<D>(hash: D) -> Scalar
|
pub fn from_hash<D>(hash: D) -> Scalar
|
||||||
where D: Digest<OutputSize = U64> + Default
|
where D: Digest<OutputSize = U64> + Default
|
||||||
{
|
{
|
||||||
// XXX this seems clumsy
|
|
||||||
let mut output = [0u8; 64];
|
let mut output = [0u8; 64];
|
||||||
output.copy_from_slice(hash.result().as_slice());
|
output.copy_from_slice(hash.result().as_slice());
|
||||||
Scalar::from_bytes_mod_order_wide(&output)
|
Scalar::from_bytes_mod_order_wide(&output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this `Scalar` to its underlying sequence of bytes.
|
/// 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] {
|
pub fn to_bytes(&self) -> [u8; 32] {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// View this `Scalar` as a sequence of bytes.
|
/// View the little-endian byte encoding of the integer representing this Scalar.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use curve25519_dalek::scalar::Scalar;
|
||||||
|
///
|
||||||
|
/// let s: Scalar = Scalar::zero();
|
||||||
|
///
|
||||||
|
/// assert!(s.as_bytes() == &[0u8; 32]);
|
||||||
|
/// ```
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||||
&self.bytes
|
&self.bytes
|
||||||
}
|
}
|
||||||
|
|
@ -405,16 +650,43 @@ impl Scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a scalar from the given `u64`.
|
/// Given a nonzero `Scalar`, compute its multiplicative inverse.
|
||||||
pub fn from_u64(x: u64) -> Scalar {
|
///
|
||||||
let mut s_bytes = [0u8; 32];
|
/// # Warning
|
||||||
for i in 0..8 {
|
///
|
||||||
s_bytes[i] = (x >> (i*8)) as u8;
|
/// `self` **MUST** be nonzero. If you cannot
|
||||||
}
|
/// *prove* that this is the case, you **SHOULD NOT USE THIS
|
||||||
Scalar{ bytes: s_bytes }
|
/// FUNCTION**.
|
||||||
}
|
///
|
||||||
|
/// # Returns
|
||||||
/// Compute the multiplicative inverse of this scalar.
|
///
|
||||||
|
/// 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 {
|
pub fn invert(&self) -> Scalar {
|
||||||
self.unpack().invert().pack()
|
self.unpack().invert().pack()
|
||||||
}
|
}
|
||||||
|
|
@ -434,33 +706,29 @@ impl Scalar {
|
||||||
/// *prove* that this is the case, you **SHOULD NOT USE THIS
|
/// *prove* that this is the case, you **SHOULD NOT USE THIS
|
||||||
/// FUNCTION**.
|
/// FUNCTION**.
|
||||||
///
|
///
|
||||||
/// This function is most efficient when the batch size (slice
|
|
||||||
/// length) is a power of 2.
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate curve25519_dalek;
|
/// # extern crate curve25519_dalek;
|
||||||
/// # use curve25519_dalek::scalar::Scalar;
|
/// # use curve25519_dalek::scalar::Scalar;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
///
|
|
||||||
/// let mut scalars = [
|
/// let mut scalars = [
|
||||||
/// Scalar::from_u64(3),
|
/// Scalar::from(3u64),
|
||||||
/// Scalar::from_u64(5),
|
/// Scalar::from(5u64),
|
||||||
/// Scalar::from_u64(7),
|
/// Scalar::from(7u64),
|
||||||
/// Scalar::from_u64(11),
|
/// Scalar::from(11u64),
|
||||||
/// ];
|
/// ];
|
||||||
///
|
///
|
||||||
/// let allinv = Scalar::batch_invert(&mut scalars);
|
/// let allinv = Scalar::batch_invert(&mut scalars);
|
||||||
///
|
///
|
||||||
/// assert_eq!(allinv, Scalar::from_u64(3*5*7*11).invert());
|
/// assert_eq!(allinv, Scalar::from(3*5*7*11u64).invert());
|
||||||
/// assert_eq!(scalars[0], Scalar::from_u64(3).invert());
|
/// assert_eq!(scalars[0], Scalar::from(3u64).invert());
|
||||||
/// assert_eq!(scalars[1], Scalar::from_u64(5).invert());
|
/// assert_eq!(scalars[1], Scalar::from(5u64).invert());
|
||||||
/// assert_eq!(scalars[2], Scalar::from_u64(7).invert());
|
/// assert_eq!(scalars[2], Scalar::from(7u64).invert());
|
||||||
/// assert_eq!(scalars[3], Scalar::from_u64(11).invert());
|
/// 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 {
|
pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar {
|
||||||
// This code is essentially identical to the FieldElement
|
// This code is essentially identical to the FieldElement
|
||||||
// implementation, and is documented there. Unfortunately,
|
// implementation, and is documented there. Unfortunately,
|
||||||
|
|
@ -474,38 +742,47 @@ impl Scalar {
|
||||||
// Mark UnpackedScalars as zeroable.
|
// Mark UnpackedScalars as zeroable.
|
||||||
unsafe impl ZeroSafe for UnpackedScalar {}
|
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();
|
let one: UnpackedScalar = Scalar::one().unpack().to_montgomery();
|
||||||
|
|
||||||
// Wrap the tree storage in a ClearOnDrop to wipe it when we
|
// Wrap the scratch storage in a ClearOnDrop to wipe it when
|
||||||
// pass out of scope.
|
// we pass out of scope.
|
||||||
let tree_vec = vec![one; 2*n];
|
let scratch_vec = vec![one; n];
|
||||||
let mut tree = ClearOnDrop::new(tree_vec);
|
let mut scratch = ClearOnDrop::new(scratch_vec);
|
||||||
|
|
||||||
for i in 0..inputs.len() {
|
// Keep an accumulator of all of the previous products
|
||||||
tree[n+i] = inputs[i].unpack().to_montgomery();
|
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() {
|
// acc is nonzero iff all inputs are nonzero
|
||||||
tree[i] = UnpackedScalar::montgomery_mul(&tree[2*i], &tree[2*i+1]);
|
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.
|
ret
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bits of the scalar.
|
/// Get the bits of the scalar.
|
||||||
|
|
@ -901,9 +1178,9 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_unsigned() {
|
fn from_u64() {
|
||||||
let val = 0xdeadbeefdeadbeef;
|
let val: u64 = 0xdeadbeefdeadbeef;
|
||||||
let s = Scalar::from_u64(val);
|
let s = Scalar::from(val);
|
||||||
assert_eq!(s[7], 0xde);
|
assert_eq!(s[7], 0xde);
|
||||||
assert_eq!(s[6], 0xad);
|
assert_eq!(s[6], 0xad);
|
||||||
assert_eq!(s[5], 0xbe);
|
assert_eq!(s[5], 0xbe);
|
||||||
|
|
@ -924,7 +1201,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn impl_add() {
|
fn impl_add() {
|
||||||
let two = Scalar::from_u64(2);
|
let two = Scalar::from(2u64);
|
||||||
let one = Scalar::one();
|
let one = Scalar::one();
|
||||||
let should_be_two = &one + &one;
|
let should_be_two = &one + &one;
|
||||||
assert_eq!(should_be_two, two);
|
assert_eq!(should_be_two, two);
|
||||||
|
|
@ -952,8 +1229,8 @@ mod test {
|
||||||
assert_eq!(should_be_one, one);
|
assert_eq!(should_be_one, one);
|
||||||
|
|
||||||
// Test that product works for iterators where Item = Scalar
|
// Test that product works for iterators where Item = Scalar
|
||||||
let xs = [Scalar::from_u64(2); 10];
|
let xs = [Scalar::from(2u64); 10];
|
||||||
let ys = [Scalar::from_u64(3); 10];
|
let ys = [Scalar::from(3u64); 10];
|
||||||
// now zs is an iterator with Item = Scalar
|
// now zs is an iterator with Item = Scalar
|
||||||
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x * y);
|
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x * y);
|
||||||
|
|
||||||
|
|
@ -961,9 +1238,9 @@ mod test {
|
||||||
let y_prod: Scalar = ys.iter().product();
|
let y_prod: Scalar = ys.iter().product();
|
||||||
let z_prod: Scalar = zs.product();
|
let z_prod: Scalar = zs.product();
|
||||||
|
|
||||||
assert_eq!(x_prod, Scalar::from_u64(1024));
|
assert_eq!(x_prod, Scalar::from(1024u64));
|
||||||
assert_eq!(y_prod, Scalar::from_u64(59049));
|
assert_eq!(y_prod, Scalar::from(59049u64));
|
||||||
assert_eq!(z_prod, Scalar::from_u64(60466176));
|
assert_eq!(z_prod, Scalar::from(60466176u64));
|
||||||
assert_eq!(x_prod * y_prod, z_prod);
|
assert_eq!(x_prod * y_prod, z_prod);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -972,7 +1249,7 @@ mod test {
|
||||||
fn impl_sum() {
|
fn impl_sum() {
|
||||||
|
|
||||||
// Test that sum works for non-empty iterators
|
// 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 one_vector = vec![Scalar::one(), Scalar::one()];
|
||||||
let should_be_two: Scalar = one_vector.iter().sum();
|
let should_be_two: Scalar = one_vector.iter().sum();
|
||||||
assert_eq!(should_be_two, two);
|
assert_eq!(should_be_two, two);
|
||||||
|
|
@ -984,8 +1261,8 @@ mod test {
|
||||||
assert_eq!(should_be_zero, zero);
|
assert_eq!(should_be_zero, zero);
|
||||||
|
|
||||||
// Test that sum works for owned types
|
// Test that sum works for owned types
|
||||||
let xs = [Scalar::from_u64(1); 10];
|
let xs = [Scalar::from(1u64); 10];
|
||||||
let ys = [Scalar::from_u64(2); 10];
|
let ys = [Scalar::from(2u64); 10];
|
||||||
// now zs is an iterator with Item = Scalar
|
// now zs is an iterator with Item = Scalar
|
||||||
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x + y);
|
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x + y);
|
||||||
|
|
||||||
|
|
@ -993,9 +1270,9 @@ mod test {
|
||||||
let y_sum: Scalar = ys.iter().sum();
|
let y_sum: Scalar = ys.iter().sum();
|
||||||
let z_sum: Scalar = zs.sum();
|
let z_sum: Scalar = zs.sum();
|
||||||
|
|
||||||
assert_eq!(x_sum, Scalar::from_u64(10));
|
assert_eq!(x_sum, Scalar::from(10u64));
|
||||||
assert_eq!(y_sum, Scalar::from_u64(20));
|
assert_eq!(y_sum, Scalar::from(20u64));
|
||||||
assert_eq!(z_sum, Scalar::from_u64(30));
|
assert_eq!(z_sum, Scalar::from(30u64));
|
||||||
assert_eq!(x_sum + y_sum, z_sum);
|
assert_eq!(x_sum + y_sum, z_sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1130,6 +1407,7 @@ mod test {
|
||||||
assert_eq!(parsed, X);
|
assert_eq!(parsed, X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn batch_invert_with_a_zero_input_panics() {
|
fn batch_invert_with_a_zero_input_panics() {
|
||||||
|
|
@ -1138,4 +1416,25 @@ mod test {
|
||||||
// This should panic in debug mode.
|
// This should panic in debug mode.
|
||||||
Scalar::batch_invert(&mut xs);
|
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(1u64);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,21 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use edwards::EdwardsPoint;
|
use edwards::EdwardsPoint;
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::MultiscalarMul;
|
use traits::MultiscalarMul;
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::VartimeMultiscalarMul;
|
use traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
/// Perform multiscalar multiplication by the interleaved window
|
/// Perform multiscalar multiplication by the interleaved window
|
||||||
/// method, also known as Straus' method (since it was apparently
|
/// method, also known as Straus' method (since it was apparently
|
||||||
/// [first published][solution] by Straus in 1964, as a solution to [a
|
/// [first published][solution] by Straus in 1964, as a solution to [a
|
||||||
|
|
@ -40,9 +48,10 @@ use traits::VartimeMultiscalarMul;
|
||||||
///
|
///
|
||||||
/// [solution]: https://www.jstor.org/stable/2310929
|
/// [solution]: https://www.jstor.org/stable/2310929
|
||||||
/// [problem]: https://www.jstor.org/stable/2312273
|
/// [problem]: https://www.jstor.org/stable/2312273
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
pub struct Straus {}
|
pub struct Straus {}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl MultiscalarMul for Straus {
|
impl MultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -139,7 +148,7 @@ impl MultiscalarMul for Straus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
impl VartimeMultiscalarMul for Straus {
|
impl VartimeMultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -152,12 +161,11 @@ impl VartimeMultiscalarMul for Straus {
|
||||||
/// The non-adjacent form has signed, odd digits. Using only odd
|
/// The non-adjacent form has signed, odd digits. Using only odd
|
||||||
/// digits halves the table size (since we only need odd
|
/// digits halves the table size (since we only need odd
|
||||||
/// multiples), or gives fewer additions for the same table size.
|
/// multiples), or gives fewer additions for the same table size.
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||||
J::Item: Borrow<EdwardsPoint>,
|
|
||||||
{
|
{
|
||||||
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
||||||
use scalar_mul::window::NafLookupTable5;
|
use scalar_mul::window::NafLookupTable5;
|
||||||
|
|
@ -167,10 +175,15 @@ impl VartimeMultiscalarMul for Straus {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.borrow().non_adjacent_form(5))
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
.collect();
|
.collect();
|
||||||
let lookup_tables: Vec<_> = points
|
|
||||||
|
let lookup_tables = match points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
|
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(&P)))
|
||||||
.collect();
|
.collect::<Option<Vec<_>>>()
|
||||||
|
{
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
let mut r = ProjectivePoint::identity();
|
let mut r = ProjectivePoint::identity();
|
||||||
|
|
||||||
|
|
@ -188,6 +201,6 @@ impl VartimeMultiscalarMul for Straus {
|
||||||
r = t.to_projective();
|
r = t.to_projective();
|
||||||
}
|
}
|
||||||
|
|
||||||
r.to_extended()
|
Some(r.to_extended())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// -*- mode: rust; -*-
|
// -*- mode: rust; -*-
|
||||||
//
|
//
|
||||||
// This file is part of curve25519-dalek.
|
// 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.
|
// See LICENSE for licensing information.
|
||||||
//
|
//
|
||||||
// Authors:
|
// Authors:
|
||||||
|
|
@ -72,9 +72,9 @@ pub trait MultiscalarMul {
|
||||||
/// use curve25519_dalek::scalar::Scalar;
|
/// use curve25519_dalek::scalar::Scalar;
|
||||||
///
|
///
|
||||||
/// // Some scalars
|
/// // Some scalars
|
||||||
/// let a = Scalar::from_u64(87329482);
|
/// let a = Scalar::from(87329482u64);
|
||||||
/// let b = Scalar::from_u64(37264829);
|
/// let b = Scalar::from(37264829u64);
|
||||||
/// let c = Scalar::from_u64(98098098);
|
/// let c = Scalar::from(98098098u64);
|
||||||
///
|
///
|
||||||
/// // Some points
|
/// // Some points
|
||||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
|
|
@ -106,11 +106,70 @@ pub trait VartimeMultiscalarMul {
|
||||||
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
||||||
type Point;
|
type Point;
|
||||||
|
|
||||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
/// 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<Point>` 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(87329482u64);
|
||||||
|
/// let b = Scalar::from(37264829u64);
|
||||||
|
/// let c = Scalar::from(98098098u64);
|
||||||
|
/// 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<I, J>(scalars: I, points: J) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator<Item = Option<Self::Point>>;
|
||||||
|
|
||||||
|
/// Given an iterator of public scalars and an iterator of
|
||||||
/// public points, compute
|
/// 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.
|
/// It is an error to call this function with two iterators of different lengths.
|
||||||
///
|
///
|
||||||
|
|
@ -123,14 +182,14 @@ pub trait VartimeMultiscalarMul {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use curve25519_dalek::constants;
|
/// use curve25519_dalek::constants;
|
||||||
/// use curve25519_dalek::traits::MultiscalarMul;
|
/// use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||||
/// use curve25519_dalek::ristretto::RistrettoPoint;
|
/// use curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
/// use curve25519_dalek::scalar::Scalar;
|
/// use curve25519_dalek::scalar::Scalar;
|
||||||
///
|
///
|
||||||
/// // Some scalars
|
/// // Some scalars
|
||||||
/// let a = Scalar::from_u64(87329482);
|
/// let a = Scalar::from(87329482u64);
|
||||||
/// let b = Scalar::from_u64(37264829);
|
/// let b = Scalar::from(37264829u64);
|
||||||
/// let c = Scalar::from_u64(98098098);
|
/// let c = Scalar::from(98098098u64);
|
||||||
///
|
///
|
||||||
/// // Some points
|
/// // Some points
|
||||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
|
|
@ -139,22 +198,30 @@ pub trait VartimeMultiscalarMul {
|
||||||
///
|
///
|
||||||
/// // A1 = a*P + b*Q + c*R
|
/// // A1 = a*P + b*Q + c*R
|
||||||
/// let abc = [a,b,c];
|
/// 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<Item=&Scalar>
|
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||||
///
|
///
|
||||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
||||||
/// let minus_abc = abc.iter().map(|x| -x);
|
/// 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<Item=Scalar>
|
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||||
///
|
///
|
||||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator,
|
||||||
J::Item: Borrow<Self::Point>;
|
J::Item: Borrow<Self::Point>,
|
||||||
|
Self::Point: Clone,
|
||||||
|
{
|
||||||
|
Self::optional_multiscalar_mul(
|
||||||
|
scalars,
|
||||||
|
points.into_iter().map(|P| Some(P.borrow().clone()))
|
||||||
|
).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue