mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +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'
|
||||
# Tests building without std. We have to select a backend, so we select the one
|
||||
# most likely to be useful in an embedded environment.
|
||||
- TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend'
|
||||
- TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend'
|
||||
# Tests no_std+alloc usage using the most embedded-friendly backend
|
||||
- TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend'
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
# Test the avx2 backend only on nightly
|
||||
- rust: stable
|
||||
env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend'
|
||||
# Test no_std only on nightly.
|
||||
# Test no_std+alloc only on nightly
|
||||
- rust: stable
|
||||
env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend'
|
||||
env: TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend'
|
||||
|
||||
script:
|
||||
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
|
||||
|
|
|
|||
20
Cargo.toml
20
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "curve25519-dalek"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
||||
readme = "README.md"
|
||||
|
|
@ -41,27 +41,29 @@ harness = false
|
|||
# match exactly, since the build.rs uses the crate itself as a library.
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.5.0", default-features = false }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
rand = { version = "0.5", default-features = false }
|
||||
byteorder = { version = "1", default-features = false, features = ["i128"] }
|
||||
digest = "0.7"
|
||||
generic-array = "0.9"
|
||||
clear_on_drop = "=0.2.3"
|
||||
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
||||
subtle = { version = "0.7", features = ["generic-impls"], default-features = false }
|
||||
serde = { version = "1.0", optional = true }
|
||||
packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
rand = { version = "0.5.0", default-features = false }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
rand = { version = "0.5", default-features = false }
|
||||
byteorder = { version = "1", default-features = false, features = ["i128"] }
|
||||
digest = "0.7"
|
||||
generic-array = "0.9"
|
||||
clear_on_drop = "=0.2.3"
|
||||
subtle = { version = "0.6", features = ["generic-impls"], default-features = false }
|
||||
subtle = { version = "0.7", features = ["generic-impls"], default-features = false }
|
||||
serde = { version = "1.0", optional = true }
|
||||
packed_simd = { version = "0.1.0", features = ["into_bits"], optional = true }
|
||||
|
||||
[features]
|
||||
nightly = ["subtle/nightly", "clear_on_drop/nightly"]
|
||||
default = ["std", "u64_backend"]
|
||||
std = ["subtle/std", "rand/std"]
|
||||
std = ["alloc", "subtle/std", "rand/std"]
|
||||
alloc = []
|
||||
yolocrypto = []
|
||||
|
||||
|
|
@ -71,7 +73,7 @@ u32_backend = []
|
|||
u64_backend = []
|
||||
# The AVX2 backend uses u32x8s with u64x4 products.
|
||||
# It uses the u64 code for serial operations.
|
||||
avx2_backend = ["nightly", "u64_backend"]
|
||||
avx2_backend = ["nightly", "u64_backend", "packed_simd"]
|
||||
|
||||
# Signals that we're in the main build stage. This is off by default,
|
||||
# to signal stage 1 of the build, where build.rs loads the library
|
||||
|
|
|
|||
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
|
||||
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
|
||||
cofactor-related abstraction mismatches.
|
||||
|
||||
## WARNING
|
||||
## Stability
|
||||
|
||||
We do not yet consider this code to be production-ready. We intend to
|
||||
stabilize a production-ready version `1.0` soon.
|
||||
We have recently released a `1.0.0-pre.0` version of `curve25519-dalek` and
|
||||
would greatly appreciate testing and feedback on our API and performance.
|
||||
|
||||
# Documentation
|
||||
|
||||
|
|
@ -59,30 +59,81 @@ extern crate curve25519_dalek;
|
|||
|
||||
# Backends and Features
|
||||
|
||||
The `yolocrypto` feature enables experimental features. The name `yolocrypto`
|
||||
is meant to indicate that it is not considered production-ready, and we do not
|
||||
consider `yolocrypto` features to be covered by semver guarantees.
|
||||
|
||||
The `std` feature is enabled by default, but it can be disabled.
|
||||
|
||||
The `nightly` feature enables nightly-only features. **It is recommended for security**.
|
||||
The `nightly` feature enables features available only when using a Rust nightly
|
||||
compiler. **It is recommended for security**.
|
||||
|
||||
Curve arithmetic is implemented using one of the following backends:
|
||||
|
||||
* a `u32` backend using `u64` products;
|
||||
* a `u64` backend using `u128` products;
|
||||
* an `avx2` backend using parallel formulas, available when compiling for a
|
||||
target with `target_feature=+avx2`.
|
||||
* an `avx2` backend using [parallel formulas][parallel_doc], available
|
||||
when compiling for a target with `target_feature=+avx2`.
|
||||
|
||||
By default the `u64` backend is selected. To select a specific backend, use:
|
||||
```sh
|
||||
cargo build --no-default-features --features "std u32_backend"
|
||||
cargo build --no-default-features --features "std u64_backend"
|
||||
# Requires RUSTFLAGS="-C target_feature=+avx2"
|
||||
cargo build --no-default-features --features "std avx2_backend"
|
||||
```
|
||||
Crates using `curve25519-dalek` can either select a backend on behalf of their
|
||||
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]:
|
||||
|
||||
```sh
|
||||
|
|
@ -93,6 +144,9 @@ cargo bench --no-default-features --features "std u64_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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -136,3 +191,5 @@ contributions.
|
|||
[docs-external]: https://doc.dalek.rs/curve25519_dalek/
|
||||
[docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/
|
||||
[criterion]: https://github.com/japaric/criterion.rs
|
||||
[parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/avx2/index.html
|
||||
[subtle_doc]: https://doc.dalek.rs/subtle/
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ mod edwards_benches {
|
|||
|
||||
fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_TABLE;
|
||||
let s = Scalar::from_u64(897987897).invert();
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
c.bench_function("Constant-time fixed-base scalar mul", move |b| {
|
||||
b.iter(|| B * &s)
|
||||
});
|
||||
|
|
@ -47,7 +47,7 @@ mod edwards_benches {
|
|||
|
||||
fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
let s = Scalar::from_u64(897987897).invert();
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
c.bench_function("Constant-time variable-base scalar mul", move |b| {
|
||||
b.iter(|| B * &s)
|
||||
});
|
||||
|
|
@ -56,8 +56,8 @@ mod edwards_benches {
|
|||
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
||||
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
let a = Scalar::from_u64(298374928).invert();
|
||||
let b = Scalar::from_u64(897987897).invert();
|
||||
let a = Scalar::from(298374928u64).invert();
|
||||
let b = Scalar::from(897987897u64).invert();
|
||||
let A = B * (b * a);
|
||||
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
||||
});
|
||||
|
|
@ -157,7 +157,7 @@ mod montgomery_benches {
|
|||
fn montgomery_ladder(c: &mut Criterion) {
|
||||
c.bench_function("Montgomery pseudomultiplication", |b| {
|
||||
let B = constants::X25519_BASEPOINT;
|
||||
let s = Scalar::from_u64(897987897).invert();
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
b.iter(|| B * s);
|
||||
});
|
||||
}
|
||||
|
|
@ -174,7 +174,7 @@ mod scalar_benches {
|
|||
|
||||
fn scalar_inversion(c: &mut Criterion) {
|
||||
c.bench_function("Scalar inversion", |b| {
|
||||
let s = Scalar::from_u64(897987897).invert();
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
b.iter(|| s.invert());
|
||||
});
|
||||
}
|
||||
|
|
|
|||
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(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
extern crate alloc;
|
||||
extern crate byteorder;
|
||||
extern crate clear_on_drop;
|
||||
extern crate core;
|
||||
|
|
@ -12,6 +14,9 @@ extern crate generic_array;
|
|||
extern crate rand;
|
||||
extern crate subtle;
|
||||
|
||||
#[cfg(all(feature = "nightly", feature = "avx2_backend"))]
|
||||
extern crate packed_simd;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
|
@ -53,6 +58,8 @@ mod field;
|
|||
mod curve_models;
|
||||
#[path = "src/backend/mod.rs"]
|
||||
mod backend;
|
||||
#[path = "src/prelude.rs"]
|
||||
mod prelude;
|
||||
#[path = "src/scalar_mul/mod.rs"]
|
||||
mod scalar_mul;
|
||||
|
||||
|
|
|
|||
|
|
@ -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; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
//! This module contains constants used by the AVX2 backend.
|
||||
|
||||
use core::simd::u32x8;
|
||||
use packed_simd::u32x8;
|
||||
|
||||
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
|
||||
use backend::avx2::field::FieldElement32x4;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -431,7 +431,7 @@ mod test {
|
|||
|
||||
println!("Testing B +- kB");
|
||||
let P = constants::ED25519_BASEPOINT_POINT;
|
||||
let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829);
|
||||
let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
|
||||
addition_test_helper(P, Q);
|
||||
}
|
||||
|
||||
|
|
@ -510,7 +510,7 @@ mod test {
|
|||
doubling_test_helper(P);
|
||||
|
||||
println!("Testing [2]([k]B)");
|
||||
let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829);
|
||||
let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
|
||||
doubling_test_helper(P);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; coding: utf-8; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -40,7 +40,7 @@ const C_LANES64: u8 = 0b00_11_00_00;
|
|||
const D_LANES64: u8 = 0b11_00_00_00;
|
||||
|
||||
use core::ops::{Add, Mul, Neg};
|
||||
use core::simd::{i32x8, u32x8, u64x4, IntoBits};
|
||||
use packed_simd::{i32x8, u32x8, u64x4, IntoBits};
|
||||
|
||||
use backend::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO};
|
||||
use backend::u64::field::FieldElement64;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||
// - 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(
|
||||
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 traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
/// Multiscalar multiplication using interleaved window / Straus'
|
||||
/// method. See the `Straus` struct in the serial backend for more
|
||||
/// details.
|
||||
|
|
@ -30,7 +33,7 @@ use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
|||
/// point representation on the fly.
|
||||
pub struct Straus {}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl MultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
|
|
@ -68,25 +71,28 @@ impl MultiscalarMul for Straus {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl VartimeMultiscalarMul for Straus {
|
||||
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
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||
{
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
let lookup_tables: Vec<_> = match points
|
||||
.into_iter()
|
||||
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
{
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
|
||||
|
|
@ -101,6 +107,7 @@ impl VartimeMultiscalarMul for Straus {
|
|||
}
|
||||
}
|
||||
}
|
||||
Q.into()
|
||||
|
||||
Some(Q.into())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -21,6 +21,12 @@
|
|||
//! `32bit` since identifiers can't start with letters, and the backends
|
||||
//! do use `u32`/`u64`, so this seems like a least-bad option.
|
||||
|
||||
#[cfg(not(any(feature = "u32_backend", feature = "u64_backend", feature = "avx2_backend")))]
|
||||
compile_error!(
|
||||
"no curve25519-dalek backend cargo feature enabled! \
|
||||
please enable one of: u32_backend, u64_backend, avx2_backend"
|
||||
);
|
||||
|
||||
#[cfg(feature = "u32_backend")]
|
||||
pub mod u32;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; coding: utf-8; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -39,7 +39,7 @@ pub(crate) const APLUS2_OVER_FOUR: FieldElement64 = FieldElement64([121666, 0, 0
|
|||
/// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493
|
||||
pub(crate) const L: Scalar64 = Scalar64([ 0x0002631a5cf5d3ed, 0x000dea2f79cd6581, 0x000000000014def9, 0x0000000000000000, 0x0000100000000000 ]);
|
||||
|
||||
/// `L` * `LFACTOR` = -1 (mod 2^51)
|
||||
/// `L` * `LFACTOR` = -1 (mod 2^52)
|
||||
pub(crate) const LFACTOR: u64 = 0x51da312547e1b;
|
||||
|
||||
/// `R` = R % L where R = 2^260
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; coding: utf-8; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -212,12 +212,6 @@ impl Identity for ProjectivePoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ProjectivePoint {
|
||||
fn default() -> ProjectivePoint {
|
||||
ProjectivePoint::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Identity for ProjectiveNielsPoint {
|
||||
fn identity() -> ProjectiveNielsPoint {
|
||||
ProjectiveNielsPoint{
|
||||
|
|
|
|||
132
src/edwards.rs
132
src/edwards.rs
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -57,11 +57,13 @@
|
|||
//! `EdwardsBasepointTable`, which performs constant-time fixed-base
|
||||
//! scalar multiplication;
|
||||
//!
|
||||
//! * the `edwards::multiscalar_mul` function, which performs
|
||||
//! * an implementation of the
|
||||
//! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for
|
||||
//! constant-time variable-base multiscalar multiplication;
|
||||
//!
|
||||
//! * the `edwards::vartime::multiscalar_mul` function, which
|
||||
//! performs variable-time variable-base multiscalar multiplication.
|
||||
//! * an implementation of the
|
||||
//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html)
|
||||
//! trait for variable-time variable-base multiscalar multiplication;
|
||||
//!
|
||||
//! ## Implementation
|
||||
//!
|
||||
|
|
@ -90,9 +92,6 @@
|
|||
// affine and projective cakes and eat both of them too.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::Vec;
|
||||
|
||||
use core::fmt::Debug;
|
||||
use core::iter::Iterator;
|
||||
use core::ops::{Add, Sub, Neg};
|
||||
|
|
@ -118,11 +117,17 @@ use curve_models::CompletedPoint;
|
|||
use curve_models::AffineNielsPoint;
|
||||
use curve_models::ProjectiveNielsPoint;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
use scalar_mul::window::LookupTable;
|
||||
|
||||
use traits::{Identity, IsIdentity};
|
||||
use traits::ValidityCheck;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use traits::MultiscalarMul;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -260,6 +265,12 @@ impl Identity for CompressedEdwardsY {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for CompressedEdwardsY {
|
||||
fn default() -> CompressedEdwardsY {
|
||||
CompressedEdwardsY::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Identity for EdwardsPoint {
|
||||
fn identity() -> EdwardsPoint {
|
||||
EdwardsPoint{ X: FieldElement::zero(),
|
||||
|
|
@ -269,14 +280,22 @@ impl Identity for EdwardsPoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for EdwardsPoint {
|
||||
fn default() -> EdwardsPoint {
|
||||
EdwardsPoint::identity()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Validity checks (for debugging, not CT)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
impl ValidityCheck for EdwardsPoint {
|
||||
// XXX this should also check that T is correct
|
||||
fn is_valid(&self) -> bool {
|
||||
self.to_projective().is_valid()
|
||||
let point_on_curve = self.to_projective().is_valid();
|
||||
let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T);
|
||||
|
||||
point_on_curve && on_segre_image
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
// forward to a specific backend implementation.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl MultiscalarMul for EdwardsPoint {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
|
|
@ -537,71 +556,84 @@ impl MultiscalarMul for EdwardsPoint {
|
|||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// XXX later when we do more fancy multiscalar mults, we can
|
||||
// delegate based on the iter's size hint -- hdevalence
|
||||
// Sanity-check lengths of input iterators
|
||||
let mut scalars = scalars.into_iter();
|
||||
let mut points = points.into_iter();
|
||||
|
||||
// Lower and upper bounds on iterators
|
||||
let (s_lo, s_hi) = scalars.by_ref().size_hint();
|
||||
let (p_lo, p_hi) = points.by_ref().size_hint();
|
||||
|
||||
// They should all be equal
|
||||
assert_eq!(s_lo, p_lo);
|
||||
assert_eq!(s_hi, Some(s_lo));
|
||||
assert_eq!(p_hi, Some(p_lo));
|
||||
|
||||
// Now we know there's a single size. When we do
|
||||
// size-dependent algorithm dispatch, use this as the hint.
|
||||
let _size = s_lo;
|
||||
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
Straus::multiscalar_mul(scalars, points)
|
||||
}
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::straus::Straus;
|
||||
Straus::multiscalar_mul(scalars, points)
|
||||
}
|
||||
use scalar_mul::straus::Straus;
|
||||
|
||||
Straus::multiscalar_mul(scalars, points)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl VartimeMultiscalarMul for 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
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||
{
|
||||
// XXX later when we do more fancy multiscalar mults, we can
|
||||
// delegate based on the iter's size hint -- hdevalence
|
||||
// Sanity-check lengths of input iterators
|
||||
let mut scalars = scalars.into_iter();
|
||||
let mut points = points.into_iter();
|
||||
|
||||
// Lower and upper bounds on iterators
|
||||
let (s_lo, s_hi) = scalars.by_ref().size_hint();
|
||||
let (p_lo, p_hi) = points.by_ref().size_hint();
|
||||
|
||||
// They should all be equal
|
||||
assert_eq!(s_lo, p_lo);
|
||||
assert_eq!(s_hi, Some(s_lo));
|
||||
assert_eq!(p_hi, Some(p_lo));
|
||||
|
||||
// Now we know there's a single size. When we do
|
||||
// size-dependent algorithm dispatch, use this as the hint.
|
||||
let _size = s_lo;
|
||||
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
Straus::vartime_multiscalar_mul(scalars, points)
|
||||
}
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::straus::Straus;
|
||||
Straus::vartime_multiscalar_mul(scalars, points)
|
||||
}
|
||||
use scalar_mul::straus::Straus;
|
||||
|
||||
Straus::optional_multiscalar_mul(scalars, points)
|
||||
}
|
||||
}
|
||||
|
||||
impl EdwardsPoint {
|
||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||
///
|
||||
/// XXX eliminate this function when we have the precomputation API
|
||||
#[cfg(feature = "stage2_build")]
|
||||
pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
use backend::avx2::scalar_mul::vartime_double_base;
|
||||
// Otherwise, use the serial backend:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
use scalar_mul::vartime_double_base;
|
||||
|
||||
vartime_double_base::mul(a, A, b)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -698,8 +730,6 @@ impl EdwardsBasepointTable {
|
|||
}
|
||||
|
||||
/// Get the basepoint for this table as an `EdwardsPoint`.
|
||||
///
|
||||
/// XXX maybe this would be better as a `From` impl
|
||||
pub fn basepoint(&self) -> EdwardsPoint {
|
||||
// self.0[0].select(1) = 1*(16^2)^0*B
|
||||
// but as an `AffineNielsPoint`, so add identity to convert to extended.
|
||||
|
|
@ -1007,7 +1037,7 @@ mod test {
|
|||
/// Test that computing 2*basepoint is the same as basepoint.double()
|
||||
#[test]
|
||||
fn basepoint_mult_two_vs_basepoint2() {
|
||||
let two = Scalar::from_u64(2);
|
||||
let two = Scalar::from(2u64);
|
||||
let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two;
|
||||
assert_eq!(bp2.compress(), BASE2_CMPRSSD);
|
||||
}
|
||||
|
|
@ -1033,10 +1063,10 @@ mod test {
|
|||
// Test that sum works for non-empty iterators
|
||||
let BASE = constants::ED25519_BASEPOINT_POINT;
|
||||
|
||||
let s1 = Scalar::from_u64(999);
|
||||
let s1 = Scalar::from(999u64);
|
||||
let P1 = &BASE * &s1;
|
||||
|
||||
let s2 = Scalar::from_u64(333);
|
||||
let s2 = Scalar::from(333u64);
|
||||
let P2 = &BASE * &s2;
|
||||
|
||||
let vec = vec![P1.clone(), P2.clone()];
|
||||
|
|
@ -1051,7 +1081,7 @@ mod test {
|
|||
assert_eq!(sum, EdwardsPoint::identity());
|
||||
|
||||
// Test that sum works on owning iterators
|
||||
let s = Scalar::from_u64(2);
|
||||
let s = Scalar::from(2u64);
|
||||
let mapped = vec.iter().map(|x| x * &s);
|
||||
let sum: EdwardsPoint = mapped.sum();
|
||||
|
||||
|
|
|
|||
98
src/field.rs
98
src/field.rs
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -96,17 +96,10 @@ impl FieldElement {
|
|||
|
||||
/// Compute (self^(2^250-1), self^11), used as a helper function
|
||||
/// within invert() and pow22523().
|
||||
///
|
||||
/// XXX This returns an extra intermediate to save computation in
|
||||
/// finding inverses, at the cost of an extra copy when it's not
|
||||
/// used (e.g., when raising to (p-1)/2 or (p-5)/8). Good idea?
|
||||
fn pow22501(&self) -> (FieldElement, FieldElement) {
|
||||
// Instead of managing which temporary variables are used
|
||||
// for what, we define as many as we need and trust the
|
||||
// compiler to reuse stack space as appropriate.
|
||||
//
|
||||
// XXX testing some examples suggests that this does happen,
|
||||
// but it would be good to check asm for this function.
|
||||
// for what, we define as many as we need and leave stack
|
||||
// allocation to the compiler
|
||||
//
|
||||
// Each temporary variable t_i is of the form (self)^e_i.
|
||||
// Squaring t_i corresponds to multiplying e_i by 2,
|
||||
|
|
@ -142,58 +135,34 @@ impl FieldElement {
|
|||
/// Given a slice of public `FieldElements`, replace each with its inverse.
|
||||
///
|
||||
/// All input `FieldElements` **MUST** be nonzero.
|
||||
///
|
||||
/// This function is most efficient when the batch size (slice
|
||||
/// length) is a power of 2.
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_invert(inputs: &mut [FieldElement]) {
|
||||
// First, compute the product of all inputs using a product
|
||||
// tree:
|
||||
//
|
||||
// Inputs: [x_0, x_1, x_2]
|
||||
//
|
||||
// Tree:
|
||||
//
|
||||
// x_0*x_1*x_2*1 tree[1]
|
||||
// / \
|
||||
// x_0*x_1 x_2*1 tree[2,3]
|
||||
// / \ / \
|
||||
// x_0 x_1 x_2 1 tree[4,5,6,7]
|
||||
//
|
||||
// The leaves of the tree are the inputs. We store the tree in
|
||||
// an array of length 2*n, similar to a binary heap.
|
||||
//
|
||||
// To initialize the tree, set every node to 1, then fill in
|
||||
// the leaf nodes with the input variables. Finally, set every
|
||||
// non-leaf node to be the product of its children.
|
||||
// Montgomery’s Trick and Fast Implementation of Masked AES
|
||||
// Genelle, Prouff and Quisquater
|
||||
// Section 3.2
|
||||
|
||||
let n = inputs.len().next_power_of_two();
|
||||
let mut tree = vec![FieldElement::one(); 2*n];
|
||||
tree[n..n+inputs.len()].copy_from_slice(inputs);
|
||||
for i in (1..n).rev() {
|
||||
tree[i] = &tree[2*i] * &tree[2*i+1];
|
||||
let n = inputs.len();
|
||||
let mut scratch = vec![FieldElement::one(); n];
|
||||
|
||||
// Keep an accumulator of all of the previous products
|
||||
let mut acc = FieldElement::one();
|
||||
|
||||
// Pass through the input vector, recording the previous
|
||||
// products in the scratch space
|
||||
for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) {
|
||||
*scratch = acc;
|
||||
acc = &acc * input;
|
||||
}
|
||||
|
||||
// The root of the tree is the product of all inputs, and is
|
||||
// stored at index 1. Compute its inverse.
|
||||
let allinv = tree[1].invert();
|
||||
// Compute the inverse of all products
|
||||
acc = acc.invert();
|
||||
|
||||
// To compute y_i = 1/x_i, start at the i-th leaf node of the
|
||||
// tree, and walk up to the root of the tree, multiplying
|
||||
// `allinv` by each sibling. This computes
|
||||
//
|
||||
// y_i = y * (all x_j except x_i)
|
||||
//
|
||||
// using lg(n) multiplications for each y_i, taking n*lg(n) in
|
||||
// total.
|
||||
for i in 0..inputs.len() {
|
||||
let mut inv = allinv;
|
||||
let mut node = n + i;
|
||||
while node > 1 {
|
||||
inv *= &tree[node ^ 1];
|
||||
node = node >> 1;
|
||||
}
|
||||
inputs[i] = inv;
|
||||
// Pass through the vector backwards to compute the inverses
|
||||
// in place
|
||||
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) {
|
||||
let tmp = &acc * input;
|
||||
*input = &acc * &scratch;
|
||||
acc = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,12 +170,9 @@ impl FieldElement {
|
|||
///
|
||||
/// The inverse is computed as self^(p-2), since
|
||||
/// x^(p-2)x = x^(p-1) = 1 (mod p).
|
||||
//
|
||||
// XXX do we want the debug assertion to check for zero? it breaks behaviour
|
||||
// such as that such as in curve25519_dalek::montgomery::test::identity_to_monty.
|
||||
///
|
||||
/// This function returns zero on input zero.
|
||||
pub fn invert(&self) -> FieldElement {
|
||||
// debug_assert!(*self != FieldElement::zero());
|
||||
|
||||
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
|
||||
//
|
||||
// nonzero bits of exponent
|
||||
|
|
@ -218,8 +184,7 @@ impl FieldElement {
|
|||
}
|
||||
|
||||
/// Raise this field element to the power (p-5)/8 = 2^252 -3.
|
||||
/// Used in decoding.
|
||||
pub fn pow_p58(&self) -> FieldElement {
|
||||
fn pow_p58(&self) -> FieldElement {
|
||||
// The bits of (p-5)/8 are 101111.....11.
|
||||
//
|
||||
// nonzero bits of exponent
|
||||
|
|
@ -496,4 +461,9 @@ mod test {
|
|||
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; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||
#![no_std]
|
||||
|
||||
#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))]
|
||||
#![cfg_attr(feature = "nightly", feature(cfg_target_feature))]
|
||||
#![cfg_attr(feature = "nightly", feature(external_doc))]
|
||||
#![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))]
|
||||
|
||||
// Refuse to compile if documentation is missing, but only on nightly.
|
||||
//
|
||||
|
|
@ -32,11 +30,17 @@
|
|||
// External dependencies:
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate core;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(all(feature = "nightly", feature = "avx2_backend"))]
|
||||
extern crate packed_simd;
|
||||
|
||||
extern crate rand;
|
||||
extern crate clear_on_drop;
|
||||
extern crate byteorder;
|
||||
|
|
@ -94,5 +98,8 @@ pub(crate) mod backend;
|
|||
// Internal curve models which are not part of the public API.
|
||||
pub(crate) mod curve_models;
|
||||
|
||||
// Crate-local prelude (for alloc-dependent features like `Vec`)
|
||||
pub(crate) mod prelude;
|
||||
|
||||
// Implementations of scalar mul algorithms live here
|
||||
pub(crate) mod scalar_mul;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -155,6 +155,12 @@ impl Identity for ProjectivePoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ProjectivePoint {
|
||||
fn default() -> ProjectivePoint {
|
||||
ProjectivePoint::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallyAssignable for ProjectivePoint {
|
||||
fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) {
|
||||
self.U.conditional_assign(&that.U, choice);
|
||||
|
|
@ -284,6 +290,7 @@ mod test {
|
|||
use constants;
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
/// Test Montgomery -> Edwards on the X/Ed25519 basepoint
|
||||
|
|
@ -337,6 +344,7 @@ mod test {
|
|||
assert_eq!(u18, u18_unred);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn montgomery_ladder_matches_edwards_scalarmult() {
|
||||
let mut csprng: OsRng = OsRng::new().unwrap();
|
||||
|
|
|
|||
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; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -14,7 +14,8 @@
|
|||
// affine and projective cakes and eat both of them too.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
//! An implementation of Ristretto, which provides a prime-order group.
|
||||
//! An implementation of [Ristretto][ristretto_main], which provides a
|
||||
//! prime-order group.
|
||||
//!
|
||||
//! # The Ristretto Group
|
||||
//!
|
||||
|
|
@ -50,8 +51,10 @@
|
|||
//! this [additional restriction][ristretto_coffee] gives the
|
||||
//! _Ristretto_ encoding.
|
||||
//!
|
||||
//! More details
|
||||
//! are described in the *Implementation* section below. Ristretto
|
||||
//! More details on why Ristretto is necessary can be found in the
|
||||
//! [Why Ristretto?][why_ristretto] section of the Ristretto website.
|
||||
//!
|
||||
//! Ristretto
|
||||
//! points are provided in `curve25519-dalek` by the `RistrettoPoint`
|
||||
//! struct.
|
||||
//!
|
||||
|
|
@ -95,11 +98,13 @@
|
|||
//! `RistrettoBasepointTable`, which performs constant-time fixed-base
|
||||
//! scalar multiplication;
|
||||
//!
|
||||
//! * the `ristretto::multiscalar_mul` function, which performs
|
||||
//! * an implementation of the
|
||||
//! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for
|
||||
//! constant-time variable-base multiscalar multiplication;
|
||||
//!
|
||||
//! * the `ristretto::vartime::multiscalar_mul` function, which
|
||||
//! performs variable-time variable-base multiscalar multiplication.
|
||||
//! * an implementation of the
|
||||
//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html)
|
||||
//! trait for variable-time variable-base multiscalar multiplication;
|
||||
//!
|
||||
//! ## Random Points and Hashing to Ristretto
|
||||
//!
|
||||
|
|
@ -137,8 +142,7 @@
|
|||
//! using Edwards formulas.
|
||||
//!
|
||||
//! Notes on the details of the encoding can be found in the
|
||||
//! [`ristretto::notes`][ristretto_notes] submodule of the internal `curve25519-dalek`
|
||||
//! documentation.
|
||||
//! [Details][ristretto_notes] section of the Ristretto website.
|
||||
//!
|
||||
//! [cryptonote]:
|
||||
//! https://moderncrypto.org/mail-archive/curves/2017/000898.html
|
||||
|
|
@ -147,23 +151,11 @@
|
|||
//! [ristretto_coffee]:
|
||||
//! https://en.wikipedia.org/wiki/Ristretto
|
||||
//! [ristretto_notes]:
|
||||
//! https://doc-internal.dalek.rs/curve25519_dalek/ristretto/notes/index.html
|
||||
|
||||
|
||||
// Conditionally include the Ristretto notes if:
|
||||
// - we're on nightly (so we can include docs at all)
|
||||
// - we're in stage 2 of the build.
|
||||
// The latter point prevents a really silly and annoying problem,
|
||||
// where the location of ".." is different depending on whether we're
|
||||
// building the crate for real, or whether we're in build.rs
|
||||
// generating the lookup tables (in which case we're relative to the
|
||||
// location of build.rs, not lib.rs, so the markdown file appears
|
||||
// missing).
|
||||
//
|
||||
// This hack is also used in the avx2 notes.
|
||||
#[cfg_attr(all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/ristretto-notes.md"))]
|
||||
mod notes {
|
||||
}
|
||||
//! https://ristretto.group/details/index.html
|
||||
//! [why_ristretto]:
|
||||
//! https://ristretto.group/why_ristretto.html
|
||||
//! [ristretto_main]:
|
||||
//! https://ristretto.group/
|
||||
|
||||
use core::fmt::Debug;
|
||||
use core::ops::{Add, Sub, Neg};
|
||||
|
|
@ -188,11 +180,16 @@ use subtle::Choice;
|
|||
use edwards::EdwardsPoint;
|
||||
use edwards::EdwardsBasepointTable;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
use scalar::Scalar;
|
||||
|
||||
use curve_models::CompletedPoint;
|
||||
|
||||
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||
use traits::Identity;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use traits::{MultiscalarMul, VartimeMultiscalarMul};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Compressed points
|
||||
|
|
@ -279,6 +276,12 @@ impl Identity for CompressedRistretto {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for CompressedRistretto {
|
||||
fn default() -> CompressedRistretto {
|
||||
CompressedRistretto::identity()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Serde support
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -397,9 +400,6 @@ impl RistrettoPoint {
|
|||
/// \mathrm{enc}( [2]P\_1), \ldots, \mathrm{enc}( [2]P\_n ) \\)
|
||||
/// in a batch.
|
||||
///
|
||||
/// This function has optimal performance when the batch size is a
|
||||
/// power of two, but this is not a requirement.
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate curve25519_dalek;
|
||||
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
|
|
@ -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>
|
||||
where I: IntoIterator<Item = &'a RistrettoPoint>
|
||||
{
|
||||
|
|
@ -575,19 +575,10 @@ impl RistrettoPoint {
|
|||
/// point should be unknown. The map is applied twice and the
|
||||
/// results are added, to ensure a uniform distribution.
|
||||
pub fn random<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);
|
||||
let r_1 = FieldElement::from_bytes(&field_bytes);
|
||||
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
||||
|
||||
rng.fill(&mut field_bytes);
|
||||
let r_2 = FieldElement::from_bytes(&field_bytes);
|
||||
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
||||
|
||||
// Applying Elligator twice and adding the results ensures a
|
||||
// uniform distribution.
|
||||
&R_1 + &R_2
|
||||
RistrettoPoint::from_uniform_bytes(&uniform_bytes)
|
||||
}
|
||||
|
||||
/// Hash a slice of bytes into a `RistrettoPoint`.
|
||||
|
|
@ -638,14 +629,31 @@ impl RistrettoPoint {
|
|||
{
|
||||
// dealing with generic arrays is clumsy, until const generics land
|
||||
let output = hash.result();
|
||||
let mut output_bytes = [0u8; 64];
|
||||
output_bytes.copy_from_slice(&output.as_slice());
|
||||
|
||||
RistrettoPoint::from_uniform_bytes(&output_bytes)
|
||||
}
|
||||
|
||||
/// Construct a `RistrettoPoint` from 64 bytes of data.
|
||||
///
|
||||
/// If the input bytes are uniformly distributed, the resulting
|
||||
/// point will be uniformly distributed over the group, and its
|
||||
/// discrete log with respect to other points should be unknown.
|
||||
///
|
||||
/// # Implementation
|
||||
///
|
||||
/// This function splits the input array into two 32-byte halves,
|
||||
/// takes the low 255 bits of each half mod p, applies the
|
||||
/// Ristretto-flavored Elligator map to each, and adds the results.
|
||||
pub fn from_uniform_bytes(bytes: &[u8; 64]) -> RistrettoPoint {
|
||||
let mut r_1_bytes = [0u8; 32];
|
||||
r_1_bytes.copy_from_slice(&output.as_slice()[0..32]);
|
||||
r_1_bytes.copy_from_slice(&bytes[0..32]);
|
||||
let r_1 = FieldElement::from_bytes(&r_1_bytes);
|
||||
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
||||
|
||||
let mut r_2_bytes = [0u8; 32];
|
||||
r_2_bytes.copy_from_slice(&output.as_slice()[0..32]);
|
||||
r_2_bytes.copy_from_slice(&bytes[32..64]);
|
||||
let r_2 = FieldElement::from_bytes(&r_2_bytes);
|
||||
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
||||
|
||||
|
|
@ -661,6 +669,12 @@ impl Identity for RistrettoPoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for RistrettoPoint {
|
||||
fn default() -> RistrettoPoint {
|
||||
RistrettoPoint::identity()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Equality
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -794,7 +808,7 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint
|
|||
// These use iterator combinators to unwrap the underlying points and
|
||||
// forward to the EdwardsPoint implementations.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl MultiscalarMul for RistrettoPoint {
|
||||
type Point = RistrettoPoint;
|
||||
|
||||
|
|
@ -812,20 +826,34 @@ impl MultiscalarMul for RistrettoPoint {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl VartimeMultiscalarMul for 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
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
J: IntoIterator<Item = Option<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(
|
||||
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::scalar::Scalar;
|
||||
///
|
||||
/// let a = Scalar::from_u64(87329482);
|
||||
/// let a = Scalar::from(87329482u64);
|
||||
/// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE;
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
|
|
@ -937,6 +965,7 @@ impl Debug for RistrettoPoint {
|
|||
|
||||
#[cfg(all(test, feature = "stage2_build"))]
|
||||
mod test {
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use scalar::Scalar;
|
||||
|
|
@ -959,7 +988,7 @@ mod test {
|
|||
#[test]
|
||||
fn scalarmult_ristrettopoint_works_both_ways() {
|
||||
let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
let s = Scalar::from_u64(999);
|
||||
let s = Scalar::from(999u64);
|
||||
|
||||
let P1 = &P * &s;
|
||||
let P2 = &s * &P;
|
||||
|
|
@ -973,10 +1002,10 @@ mod test {
|
|||
// Test that sum works for non-empty iterators
|
||||
let BASE = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
|
||||
let s1 = Scalar::from_u64(999);
|
||||
let s1 = Scalar::from(999u64);
|
||||
let P1 = &BASE * &s1;
|
||||
|
||||
let s2 = Scalar::from_u64(333);
|
||||
let s2 = Scalar::from(333u64);
|
||||
let P2 = &BASE * &s2;
|
||||
|
||||
let vec = vec![P1.clone(), P2.clone()];
|
||||
|
|
@ -991,7 +1020,7 @@ mod test {
|
|||
assert_eq!(sum, RistrettoPoint::identity());
|
||||
|
||||
// Test that sum works on owning iterators
|
||||
let s = Scalar::from_u64(2);
|
||||
let s = Scalar::from(2u64);
|
||||
let mapped = vec.iter().map(|x| x * &s);
|
||||
let sum: RistrettoPoint = mapped.sum();
|
||||
|
||||
|
|
@ -1072,6 +1101,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn four_torsion_random() {
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
|
|
@ -1134,6 +1164,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn random_roundtrip() {
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
|
|
@ -1146,6 +1177,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn double_and_compress_1024_random_points() {
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
|
|
@ -1160,6 +1192,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[test]
|
||||
fn random_is_valid() {
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
|
|
|
|||
487
src/scalar.rs
487
src/scalar.rs
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// Portions Copyright 2017 Brian Smith
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
|
|
@ -11,6 +11,132 @@
|
|||
// - Brian Smith <brian@briansmith.org>
|
||||
|
||||
//! 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::ops::Neg;
|
||||
|
|
@ -22,6 +148,9 @@ use core::cmp::{Eq, PartialEq};
|
|||
use core::iter::{Product, Sum};
|
||||
use core::borrow::Borrow;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
use rand::{Rng, CryptoRng};
|
||||
|
||||
use digest::Digest;
|
||||
|
|
@ -51,38 +180,20 @@ type UnpackedScalar = backend::u32::scalar::Scalar32;
|
|||
|
||||
/// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which
|
||||
/// represents an element of \\(\mathbb Z / \ell\\).
|
||||
///
|
||||
/// Both the Ristretto group and the Ed25519 basepoint have prime order
|
||||
/// \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\).
|
||||
///
|
||||
/// The code is intended to be useful with both the Ristretto group
|
||||
/// (where everything is done modulo \\( \ell \\)), and the X/Ed25519
|
||||
/// setting, which mandates specific bit-twiddles that are not
|
||||
/// well-defined modulo \\( \ell \\).
|
||||
///
|
||||
/// To create a `Scalar` from a supposedly canonical encoding, use
|
||||
/// `Scalar::from_canonical_bytes`.
|
||||
///
|
||||
/// To create a `Scalar` by reducing a \\(256\\)-bit integer mod \\( \ell \\),
|
||||
/// use `Scalar::from_bytes_mod_order`.
|
||||
///
|
||||
/// To create a `Scalar` by reducing a \\(512\\)-bit integer mod \\( \ell \\),
|
||||
/// use `Scalar::from_bytes_mod_order_wide`.
|
||||
///
|
||||
/// To create a `Scalar` with a specific bit-pattern (e.g., for
|
||||
/// compatibility with X25519 "clamping"), use `Scalar::from_bits`.
|
||||
///
|
||||
/// All arithmetic on `Scalars` is done modulo \\( \ell \\).
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Scalar {
|
||||
/// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the group order.
|
||||
/// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the
|
||||
/// group order.
|
||||
///
|
||||
/// # Invariant
|
||||
///
|
||||
/// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or equivalently the high bit of `bytes[31]` must be zero.
|
||||
/// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or
|
||||
/// equivalently the high bit of `bytes[31]` must be zero.
|
||||
///
|
||||
/// This ensures that there is room for a carry bit when computing a NAF representation.
|
||||
// XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead.
|
||||
//
|
||||
// XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could
|
||||
// make the Scalar constructors const fns and use those instead.
|
||||
pub(crate) bytes: [u8; 32],
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
/// Return a `Scalar` chosen uniformly at random using a user-provided RNG.
|
||||
///
|
||||
|
|
@ -328,7 +510,21 @@ impl Scalar {
|
|||
/// # Returns
|
||||
///
|
||||
/// 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 {
|
||||
let mut scalar_bytes = [0u8; 64];
|
||||
rng.fill(&mut scalar_bytes);
|
||||
|
|
@ -348,6 +544,7 @@ impl Scalar {
|
|||
/// # extern crate curve25519_dalek;
|
||||
/// # use curve25519_dalek::scalar::Scalar;
|
||||
/// extern crate sha2;
|
||||
///
|
||||
/// use sha2::Sha512;
|
||||
///
|
||||
/// # // Need fn main() here in comment so the doctest compiles
|
||||
|
|
@ -357,7 +554,6 @@ impl Scalar {
|
|||
/// let s = Scalar::hash_from_bytes::<Sha512>(msg.as_bytes());
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
pub fn hash_from_bytes<D>(input: &[u8]) -> Scalar
|
||||
where D: Digest<OutputSize = U64> + Default
|
||||
{
|
||||
|
|
@ -371,21 +567,70 @@ impl Scalar {
|
|||
/// Use this instead of `hash_from_bytes` if it is more convenient
|
||||
/// to stream data into the `Digest` than to pass a single byte
|
||||
/// slice.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate curve25519_dalek;
|
||||
/// # use curve25519_dalek::scalar::Scalar;
|
||||
/// extern crate sha2;
|
||||
///
|
||||
/// use sha2::Digest;
|
||||
/// use sha2::Sha512;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut h = Sha512::default();
|
||||
///
|
||||
/// h.input(b"To really appreciate architecture, you may even need to commit a murder.");
|
||||
/// h.input(b"While the programs used for The Manhattan Transcripts are of the most extreme");
|
||||
/// h.input(b"nature, they also parallel the most common formula plot: the archetype of");
|
||||
/// h.input(b"murder. Other phantasms were occasionally used to underline the fact that");
|
||||
/// h.input(b"perhaps all architecture, rather than being about functional standards, is");
|
||||
/// h.input(b"about love and death.");
|
||||
///
|
||||
/// let s = Scalar::from_hash(h);
|
||||
///
|
||||
/// println!("{:?}", s.to_bytes());
|
||||
/// assert!(s == Scalar::from_bits([ 21, 88, 208, 252, 63, 122, 210, 152,
|
||||
/// 154, 38, 15, 23, 16, 167, 80, 150,
|
||||
/// 192, 221, 77, 226, 62, 25, 224, 148,
|
||||
/// 239, 48, 176, 10, 185, 69, 168, 11, ]));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_hash<D>(hash: D) -> Scalar
|
||||
where D: Digest<OutputSize = U64> + Default
|
||||
{
|
||||
// XXX this seems clumsy
|
||||
let mut output = [0u8; 64];
|
||||
output.copy_from_slice(hash.result().as_slice());
|
||||
Scalar::from_bytes_mod_order_wide(&output)
|
||||
}
|
||||
|
||||
/// Convert this `Scalar` to its underlying sequence of bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::scalar::Scalar;
|
||||
///
|
||||
/// let s: Scalar = Scalar::zero();
|
||||
///
|
||||
/// assert!(s.to_bytes() == [0u8; 32]);
|
||||
/// ```
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// View this `Scalar` as a sequence of bytes.
|
||||
/// View 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] {
|
||||
&self.bytes
|
||||
}
|
||||
|
|
@ -405,16 +650,43 @@ impl Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct a scalar from the given `u64`.
|
||||
pub fn from_u64(x: u64) -> Scalar {
|
||||
let mut s_bytes = [0u8; 32];
|
||||
for i in 0..8 {
|
||||
s_bytes[i] = (x >> (i*8)) as u8;
|
||||
}
|
||||
Scalar{ bytes: s_bytes }
|
||||
}
|
||||
|
||||
/// Compute the multiplicative inverse of this scalar.
|
||||
/// Given a nonzero `Scalar`, compute its multiplicative inverse.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// `self` **MUST** be nonzero. If you cannot
|
||||
/// *prove* that this is the case, you **SHOULD NOT USE THIS
|
||||
/// FUNCTION**.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The multiplicative inverse of the this `Scalar`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::scalar::Scalar;
|
||||
///
|
||||
/// // x = 2238329342913194256032495932344128051776374960164957527413114840482143558222
|
||||
/// let X: Scalar = Scalar::from_bytes_mod_order([
|
||||
/// 0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84,
|
||||
/// 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52,
|
||||
/// 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44,
|
||||
/// 0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04,
|
||||
/// ]);
|
||||
/// // 1/x = 6859937278830797291664592131120606308688036382723378951768035303146619657244
|
||||
/// let XINV: Scalar = Scalar::from_bytes_mod_order([
|
||||
/// 0x1c, 0xdc, 0x17, 0xfc, 0xe0, 0xe9, 0xa5, 0xbb,
|
||||
/// 0xd9, 0x24, 0x7e, 0x56, 0xbb, 0x01, 0x63, 0x47,
|
||||
/// 0xbb, 0xba, 0x31, 0xed, 0xd5, 0xa9, 0xbb, 0x96,
|
||||
/// 0xd5, 0x0b, 0xcd, 0x7a, 0x3f, 0x96, 0x2a, 0x0f,
|
||||
/// ]);
|
||||
///
|
||||
/// let inv_X: Scalar = X.invert();
|
||||
/// assert!(XINV == inv_X);
|
||||
/// let should_be_one: Scalar = &inv_X * &X;
|
||||
/// assert!(should_be_one == Scalar::one());
|
||||
/// ```
|
||||
pub fn invert(&self) -> Scalar {
|
||||
self.unpack().invert().pack()
|
||||
}
|
||||
|
|
@ -434,33 +706,29 @@ impl Scalar {
|
|||
/// *prove* that this is the case, you **SHOULD NOT USE THIS
|
||||
/// FUNCTION**.
|
||||
///
|
||||
/// This function is most efficient when the batch size (slice
|
||||
/// length) is a power of 2.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate curve25519_dalek;
|
||||
/// # use curve25519_dalek::scalar::Scalar;
|
||||
/// # fn main() {
|
||||
///
|
||||
/// let mut scalars = [
|
||||
/// Scalar::from_u64(3),
|
||||
/// Scalar::from_u64(5),
|
||||
/// Scalar::from_u64(7),
|
||||
/// Scalar::from_u64(11),
|
||||
/// Scalar::from(3u64),
|
||||
/// Scalar::from(5u64),
|
||||
/// Scalar::from(7u64),
|
||||
/// Scalar::from(11u64),
|
||||
/// ];
|
||||
///
|
||||
/// let allinv = Scalar::batch_invert(&mut scalars);
|
||||
///
|
||||
/// assert_eq!(allinv, Scalar::from_u64(3*5*7*11).invert());
|
||||
/// assert_eq!(scalars[0], Scalar::from_u64(3).invert());
|
||||
/// assert_eq!(scalars[1], Scalar::from_u64(5).invert());
|
||||
/// assert_eq!(scalars[2], Scalar::from_u64(7).invert());
|
||||
/// assert_eq!(scalars[3], Scalar::from_u64(11).invert());
|
||||
/// assert_eq!(allinv, Scalar::from(3*5*7*11u64).invert());
|
||||
/// assert_eq!(scalars[0], Scalar::from(3u64).invert());
|
||||
/// assert_eq!(scalars[1], Scalar::from(5u64).invert());
|
||||
/// assert_eq!(scalars[2], Scalar::from(7u64).invert());
|
||||
/// assert_eq!(scalars[3], Scalar::from(11u64).invert());
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar {
|
||||
// This code is essentially identical to the FieldElement
|
||||
// implementation, and is documented there. Unfortunately,
|
||||
|
|
@ -474,38 +742,47 @@ impl Scalar {
|
|||
// Mark UnpackedScalars as zeroable.
|
||||
unsafe impl ZeroSafe for UnpackedScalar {}
|
||||
|
||||
let n = inputs.len().next_power_of_two();
|
||||
let n = inputs.len();
|
||||
let one: UnpackedScalar = Scalar::one().unpack().to_montgomery();
|
||||
|
||||
// Wrap the tree storage in a ClearOnDrop to wipe it when we
|
||||
// pass out of scope.
|
||||
let tree_vec = vec![one; 2*n];
|
||||
let mut tree = ClearOnDrop::new(tree_vec);
|
||||
// Wrap the scratch storage in a ClearOnDrop to wipe it when
|
||||
// we pass out of scope.
|
||||
let scratch_vec = vec![one; n];
|
||||
let mut scratch = ClearOnDrop::new(scratch_vec);
|
||||
|
||||
for i in 0..inputs.len() {
|
||||
tree[n+i] = inputs[i].unpack().to_montgomery();
|
||||
// Keep an accumulator of all of the previous products
|
||||
let mut acc = Scalar::one().unpack().to_montgomery();
|
||||
|
||||
// Pass through the input vector, recording the previous
|
||||
// products in the scratch space
|
||||
for (input, scratch) in inputs.iter_mut().zip(scratch.iter_mut()) {
|
||||
*scratch = acc;
|
||||
|
||||
// Avoid unnecessary Montgomery multiplication in second pass by
|
||||
// keeping inputs in Montgomery form
|
||||
let tmp = input.unpack().to_montgomery();
|
||||
*input = tmp.pack();
|
||||
acc = UnpackedScalar::montgomery_mul(&acc, &tmp);
|
||||
}
|
||||
|
||||
for i in (1..n).rev() {
|
||||
tree[i] = UnpackedScalar::montgomery_mul(&tree[2*i], &tree[2*i+1]);
|
||||
// acc is nonzero iff all inputs are nonzero
|
||||
debug_assert!(acc.pack() != Scalar::zero());
|
||||
|
||||
// Compute the inverse of all products
|
||||
acc = acc.montgomery_invert().from_montgomery();
|
||||
|
||||
// We need to return the product of all inverses later
|
||||
let ret = acc.pack();
|
||||
|
||||
// Pass through the vector backwards to compute the inverses
|
||||
// in place
|
||||
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) {
|
||||
let tmp = UnpackedScalar::montgomery_mul(&acc, &input.unpack());
|
||||
*input = UnpackedScalar::montgomery_mul(&acc, &scratch).pack();
|
||||
acc = tmp;
|
||||
}
|
||||
|
||||
// tree[1] is zero iff any of the inputs are zero.
|
||||
debug_assert!(tree[1].from_montgomery().pack() != Scalar::zero());
|
||||
|
||||
let allinv = tree[1].montgomery_invert();
|
||||
|
||||
for i in 0..inputs.len() {
|
||||
let mut inv = allinv;
|
||||
let mut node = n + i;
|
||||
while node > 1 {
|
||||
inv = UnpackedScalar::montgomery_mul(&inv, &tree[node ^1]);
|
||||
node = node >> 1;
|
||||
}
|
||||
inputs[i] = inv.from_montgomery().pack();
|
||||
}
|
||||
|
||||
allinv.from_montgomery().pack()
|
||||
ret
|
||||
}
|
||||
|
||||
/// Get the bits of the scalar.
|
||||
|
|
@ -901,9 +1178,9 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn from_unsigned() {
|
||||
let val = 0xdeadbeefdeadbeef;
|
||||
let s = Scalar::from_u64(val);
|
||||
fn from_u64() {
|
||||
let val: u64 = 0xdeadbeefdeadbeef;
|
||||
let s = Scalar::from(val);
|
||||
assert_eq!(s[7], 0xde);
|
||||
assert_eq!(s[6], 0xad);
|
||||
assert_eq!(s[5], 0xbe);
|
||||
|
|
@ -924,7 +1201,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn impl_add() {
|
||||
let two = Scalar::from_u64(2);
|
||||
let two = Scalar::from(2u64);
|
||||
let one = Scalar::one();
|
||||
let should_be_two = &one + &one;
|
||||
assert_eq!(should_be_two, two);
|
||||
|
|
@ -952,8 +1229,8 @@ mod test {
|
|||
assert_eq!(should_be_one, one);
|
||||
|
||||
// Test that product works for iterators where Item = Scalar
|
||||
let xs = [Scalar::from_u64(2); 10];
|
||||
let ys = [Scalar::from_u64(3); 10];
|
||||
let xs = [Scalar::from(2u64); 10];
|
||||
let ys = [Scalar::from(3u64); 10];
|
||||
// now zs is an iterator with Item = Scalar
|
||||
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x * y);
|
||||
|
||||
|
|
@ -961,9 +1238,9 @@ mod test {
|
|||
let y_prod: Scalar = ys.iter().product();
|
||||
let z_prod: Scalar = zs.product();
|
||||
|
||||
assert_eq!(x_prod, Scalar::from_u64(1024));
|
||||
assert_eq!(y_prod, Scalar::from_u64(59049));
|
||||
assert_eq!(z_prod, Scalar::from_u64(60466176));
|
||||
assert_eq!(x_prod, Scalar::from(1024u64));
|
||||
assert_eq!(y_prod, Scalar::from(59049u64));
|
||||
assert_eq!(z_prod, Scalar::from(60466176u64));
|
||||
assert_eq!(x_prod * y_prod, z_prod);
|
||||
|
||||
}
|
||||
|
|
@ -972,7 +1249,7 @@ mod test {
|
|||
fn impl_sum() {
|
||||
|
||||
// Test that sum works for non-empty iterators
|
||||
let two = Scalar::from_u64(2);
|
||||
let two = Scalar::from(2u64);
|
||||
let one_vector = vec![Scalar::one(), Scalar::one()];
|
||||
let should_be_two: Scalar = one_vector.iter().sum();
|
||||
assert_eq!(should_be_two, two);
|
||||
|
|
@ -984,8 +1261,8 @@ mod test {
|
|||
assert_eq!(should_be_zero, zero);
|
||||
|
||||
// Test that sum works for owned types
|
||||
let xs = [Scalar::from_u64(1); 10];
|
||||
let ys = [Scalar::from_u64(2); 10];
|
||||
let xs = [Scalar::from(1u64); 10];
|
||||
let ys = [Scalar::from(2u64); 10];
|
||||
// now zs is an iterator with Item = Scalar
|
||||
let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x + y);
|
||||
|
||||
|
|
@ -993,9 +1270,9 @@ mod test {
|
|||
let y_sum: Scalar = ys.iter().sum();
|
||||
let z_sum: Scalar = zs.sum();
|
||||
|
||||
assert_eq!(x_sum, Scalar::from_u64(10));
|
||||
assert_eq!(y_sum, Scalar::from_u64(20));
|
||||
assert_eq!(z_sum, Scalar::from_u64(30));
|
||||
assert_eq!(x_sum, Scalar::from(10u64));
|
||||
assert_eq!(y_sum, Scalar::from(20u64));
|
||||
assert_eq!(z_sum, Scalar::from(30u64));
|
||||
assert_eq!(x_sum + y_sum, z_sum);
|
||||
}
|
||||
|
||||
|
|
@ -1130,6 +1407,7 @@ mod test {
|
|||
assert_eq!(parsed, X);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn batch_invert_with_a_zero_input_panics() {
|
||||
|
|
@ -1138,4 +1416,25 @@ mod test {
|
|||
// This should panic in debug mode.
|
||||
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)]
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use core::borrow::Borrow;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use edwards::EdwardsPoint;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use scalar::Scalar;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use traits::MultiscalarMul;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
/// Perform multiscalar multiplication by the interleaved window
|
||||
/// method, also known as Straus' method (since it was apparently
|
||||
/// [first published][solution] by Straus in 1964, as a solution to [a
|
||||
|
|
@ -40,9 +48,10 @@ use traits::VartimeMultiscalarMul;
|
|||
///
|
||||
/// [solution]: https://www.jstor.org/stable/2310929
|
||||
/// [problem]: https://www.jstor.org/stable/2312273
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub struct Straus {}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl MultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
|
|
@ -139,7 +148,7 @@ impl MultiscalarMul for Straus {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
impl VartimeMultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
|
|
@ -152,12 +161,11 @@ impl VartimeMultiscalarMul for Straus {
|
|||
/// The non-adjacent form has signed, odd digits. Using only odd
|
||||
/// digits halves the table size (since we only need odd
|
||||
/// multiples), or gives fewer additions for the same table size.
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||
{
|
||||
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
||||
use scalar_mul::window::NafLookupTable5;
|
||||
|
|
@ -167,10 +175,15 @@ impl VartimeMultiscalarMul for Straus {
|
|||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
|
||||
let lookup_tables = match points
|
||||
.into_iter()
|
||||
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(P.borrow()))
|
||||
.collect();
|
||||
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(&P)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
{
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let mut r = ProjectivePoint::identity();
|
||||
|
||||
|
|
@ -188,6 +201,6 @@ impl VartimeMultiscalarMul for Straus {
|
|||
r = t.to_projective();
|
||||
}
|
||||
|
||||
r.to_extended()
|
||||
Some(r.to_extended())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
||||
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
|
|
@ -72,9 +72,9 @@ pub trait MultiscalarMul {
|
|||
/// use curve25519_dalek::scalar::Scalar;
|
||||
///
|
||||
/// // Some scalars
|
||||
/// let a = Scalar::from_u64(87329482);
|
||||
/// let b = Scalar::from_u64(37264829);
|
||||
/// let c = Scalar::from_u64(98098098);
|
||||
/// let a = Scalar::from(87329482u64);
|
||||
/// let b = Scalar::from(37264829u64);
|
||||
/// let c = Scalar::from(98098098u64);
|
||||
///
|
||||
/// // Some points
|
||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
|
|
@ -106,11 +106,70 @@ pub trait VartimeMultiscalarMul {
|
|||
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
||||
type Point;
|
||||
|
||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||
/// Given an iterator of public scalars and an iterator of
|
||||
/// `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
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n,
|
||||
/// $$
|
||||
/// using variable-time operations.
|
||||
///
|
||||
/// It is an error to call this function with two iterators of different lengths.
|
||||
///
|
||||
|
|
@ -123,14 +182,14 @@ pub trait VartimeMultiscalarMul {
|
|||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::constants;
|
||||
/// use curve25519_dalek::traits::MultiscalarMul;
|
||||
/// use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||
/// use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// use curve25519_dalek::scalar::Scalar;
|
||||
///
|
||||
/// // Some scalars
|
||||
/// let a = Scalar::from_u64(87329482);
|
||||
/// let b = Scalar::from_u64(37264829);
|
||||
/// let c = Scalar::from_u64(98098098);
|
||||
/// let a = Scalar::from(87329482u64);
|
||||
/// let b = Scalar::from(37264829u64);
|
||||
/// let c = Scalar::from(98098098u64);
|
||||
///
|
||||
/// // Some points
|
||||
/// let P = constants::RISTRETTO_BASEPOINT_POINT;
|
||||
|
|
@ -139,22 +198,30 @@ pub trait VartimeMultiscalarMul {
|
|||
///
|
||||
/// // A1 = a*P + b*Q + c*R
|
||||
/// let abc = [a,b,c];
|
||||
/// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
|
||||
/// let A1 = RistrettoPoint::vartime_multiscalar_mul(&abc, &[P,Q,R]);
|
||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||
///
|
||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
||||
/// let minus_abc = abc.iter().map(|x| -x);
|
||||
/// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]);
|
||||
/// let A2 = RistrettoPoint::vartime_multiscalar_mul(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<Self::Point>;
|
||||
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