diff --git a/.travis.yml b/.travis.yml index 50e9a3c..fc5f2dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,16 +15,18 @@ env: - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' # Tests building without std. We have to select a backend, so we select the one # most likely to be useful in an embedded environment. - - TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' + - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend' + # Tests no_std+alloc usage using the most embedded-friendly backend + - TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' matrix: exclude: # Test the avx2 backend only on nightly - rust: stable env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend' - # Test no_std only on nightly. + # Test no_std+alloc only on nightly - rust: stable - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' + env: TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS diff --git a/Cargo.toml b/Cargo.toml index 3978ee5..0976cdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.18.0" +version = "0.19.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" @@ -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 diff --git a/LICENSE b/LICENSE index 33ed368..d94fdb5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. +Copyright (c) 2016-2018 Isis Agora Lovecruft, Henry de Valence. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/README.md b/README.md index ddf4283..f241f81 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ prime-order group from a non-prime-order Edwards curve. This provides the speed and safety benefits of Edwards curve arithmetic, without the pitfalls of cofactor-related abstraction mismatches. -## WARNING +## Stability -We do not yet consider this code to be production-ready. We intend to -stabilize a production-ready version `1.0` soon. +We have recently released a `1.0.0-pre.0` version of `curve25519-dalek` and +would greatly appreciate testing and feedback on our API and performance. # Documentation @@ -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/ diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 75017cf..71792a3 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -39,7 +39,7 @@ mod edwards_benches { fn consttime_fixed_base_scalar_mul(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_TABLE; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time fixed-base scalar mul", move |b| { b.iter(|| B * &s) }); @@ -47,7 +47,7 @@ mod edwards_benches { fn consttime_variable_base_scalar_mul(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_POINT; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); c.bench_function("Constant-time variable-base scalar mul", move |b| { b.iter(|| B * &s) }); @@ -56,8 +56,8 @@ mod edwards_benches { fn vartime_double_base_scalar_mul(c: &mut Criterion) { c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| { let B = &constants::ED25519_BASEPOINT_POINT; - let a = Scalar::from_u64(298374928).invert(); - let b = Scalar::from_u64(897987897).invert(); + let a = Scalar::from(298374928u64).invert(); + let b = Scalar::from(897987897u64).invert(); let A = B * (b * a); bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b)); }); @@ -157,7 +157,7 @@ mod montgomery_benches { fn montgomery_ladder(c: &mut Criterion) { c.bench_function("Montgomery pseudomultiplication", |b| { let B = constants::X25519_BASEPOINT; - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); b.iter(|| B * s); }); } @@ -174,7 +174,7 @@ mod scalar_benches { fn scalar_inversion(c: &mut Criterion) { c.bench_function("Scalar inversion", |b| { - let s = Scalar::from_u64(897987897).invert(); + let s = Scalar::from(897987897u64).invert(); b.iter(|| s.invert()); }); } diff --git a/build.rs b/build.rs index e0a2da7..cc26334 100644 --- a/build.rs +++ b/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; diff --git a/docs/ristretto-notes.md b/docs/ristretto-notes.md deleted file mode 100644 index 8999137..0000000 --- a/docs/ristretto-notes.md +++ /dev/null @@ -1,475 +0,0 @@ -Below are some notes on Ristretto, which are not an authoritative -writeup and which may have errors. See also the [Decaf -paper][decaf_paper], the [libdecaf -implementation of Ristretto][ristretto_libdecaf], and its [Sage -script][ristretto_sage]. - -Decaf constructs a prime-order group from a cofactor-\\(4\\) Edwards -curve by defining an encoding of a related Jacobi quartic, then -transporting the encoding from the Jacobi quartic to the Edwards curve -by means of an isogeny. Ristretto uses a different Jacobi quartic and -a different isogeny, but is otherwise similar. - -These notes only describe Ristretto, and focus on the cofactor-\\(8\\) -case. - -# The Jacobi Quartic - -The Jacobi quartic curve is parameterized by \\(e, A\\), and is of the -form $$ \mathcal J\_{e,A} : t\^2 = es\^4 + 2As\^2 + 1, $$ with -identity point \\((0,1)\\). For more details on the Jacobi quartic, -see the [Decaf paper][decaf_paper] or -[_Jacobi Quartic Curves Revisited_][hwcd_jacobi] by Hisil, Wong, -Carter, and Dawson). - -When \\(e = a\^2\\) is a square, \\(\mathcal J\_{e,A}\\) has full -\\(2\\)-torsion (i.e., \\(\mathcal J[2] \cong \mathbb Z /2 \times -\mathbb Z/2\\)), and -we can write the \\(\mathcal J[2]\\)-coset of a point \\(P = -(s,t)\\) as -$$ -P + \mathcal J[2] = \left\\{ -(s,t), -(-s,-t), -(1/as, -t/as\^2), -(-1/as, t/as\^2) -\right\\}. -$$ -Notice that replacing \\(a\\) by \\(-a\\) just swaps the last two -points, so this set does not depend on the choice of \\(a\\). In -what follows we require \\(a = \pm 1\\). - -# Encoding \\(\mathcal J / \mathcal J[2]\\) - -To encode points on \\(\mathcal J\\) modulo \\(\mathcal J[2]\\), -we need to choose a canonical representative of the above coset. -To do this, it's sufficient to make two independent sign choices: -the Decaf paper suggests choosing \\((s,t)\\) with \\(s\\) -non-negative and finite, and \\(t/s\\) non-negative or infinite. - -The encoding is then the (canonical byte encoding of the) -\\(s\\)-value of the canonical representative. - -# The Edwards Curve - -The primary internal model in `curve25519-dalek` for Curve25519 points -is the [_Extended Twisted Edwards Coordinates_][hwcd_edwards] of -Hisil, Wong, Carter, and Dawson. These correspond to the affine model -$$ -\mathcal E\_{a,d} : ax\^2 + y\^2 = 1 + dx\^2y\^2. -$$ -In projective coordinates, we represent a point as \\((X:Y:Z:T)\\) -with -$$ -XY = ZT, \quad aX\^2 + Y\^2 = Z\^2 + dT\^2. -$$ -(For more details on this model, see the -[`curve_models`][curve_models] documentation). The case \\(a = 1\\) is -the _untwisted_ case; we only consider \\(a = \pm 1\\), and in -particular we focus on the twisted Edwards form of Curve25519, which -has \\(a = -1, d = -121665/121666\\). When not otherwise specified, -we write \\(\mathcal E\\) for \\(\mathcal E\_{a,d}\\). - -When both \\(d\\) and \\(ad\\) are nonsquare (which forces \\(a\\) -to be square), the curve is *complete*. In this case the -four-torsion subgroup is cyclic, and we -can write it explicitly as -$$ -\mathcal E\_{a,d}[4] = \\{ (0,1),\\; (1/\sqrt a, 0),\\; (0, -1),\\; (-1/\sqrt{a}, 0)\\}. -$$ -These are the only points with \\(xy = 0\\); the points with -\\( y \neq 0 \\) are \\(2\\)-torsion. - -# The Ristretto Group - -We consider two cases: - -* cofactor \\(4\\), where \\( \\# \mathcal E(\mathbb F_p) = 4\cdot \ell \\); -* cofactor \\(8\\) with cyclic \\(8\\)-torsion, - where \\( \\# \mathcal E(\mathbb F_p) = 8 \cdot \ell \\) - and \\( \mathcal E[8] \cong \mathbb Z / 8 \\). - -In the cofactor \\(4\\) case, we have \\( \[2\](\mathcal E[4]) = -\mathcal E[2] \\), so that \\( \mathcal E[2] \subseteq \[2\](\mathcal -E) \\), and the group we will construct is -$$ -\frac{\[2\](\mathcal E)}{\mathcal E[2]} -$$ -which has prime order \\( (4\ell/2)/2 = \ell \\). - -In the cofactor \\(8\\) case, since the \\(8\\)-torsion is cyclic, we -have \\( \[2\](\mathcal E[8]) = \mathcal E[4] \\), so that \\(\mathcal -E[4] \subseteq \[2\](\mathcal E)\\), and the group we will construct -is -$$ -\frac{\[2\](\mathcal E)}{\mathcal E[4]} -$$ -which has prime order \\( (8\ell/2)/4 = \ell \\). - -In particular, Curve25519 has \\( \mathcal E(\mathbb -F\_p) \cong \mathbb Z / 8 \times \mathbb Z / \ell\\), where \\( \ell -= 2\^{252} + \cdots \\) is a large prime, and meets the requirements -for the cofactor \\(8\\) case. - -# Torquing points to lift from \\(\mathcal E[4]\\) to \\(\mathcal E[2]\\) - -To bridge the gap between the cofactor \\(4\\) and cofactor \\(8\\) -cases, we need a way to canonically select a representative modulo -\\(\mathcal E[2] \\), given a representative modulo \\(\mathcal E[4] \\). - -Using the description of \\(\mathcal E[4]\\) above, we can write the -\\(\mathcal E[4]\\)-coset of a point \\(P = (x,y)\\) as -$$ -P + \mathcal E\_{a,d}[4] = \\{ (x,y),\\; (y/\sqrt a, -x\sqrt a),\\; (-x, -y),\\; (-y/\sqrt a, x\sqrt a)\\}. -$$ -Notice that if \\(xy \neq 0 \\), then exactly two of these points have -\\( xy \\) non-negative, and they differ by the \\(2\\)-torsion point -\\( (0,-1) \\). - -This means that we can select a representative modulo -\\(\mathcal E[2]\\) by requiring \\(xy\\) nonnegative and \\(y \neq -0\\), and we can ensure that this condition holds by conditionally -adding a \\(4\\)-torsion point \\(Q_4\\) if \\(xy\\) is negative or -\\(y = 0\\). - -The points of exact order \\(4\\) are \\( (\pm 1/\sqrt{a}, 0 )\\); -convenient choices for \\( Q_4 \\) are \\((1,0)\\) when \\( a = 1 \\) -and \\( (i, 0) \\) when \\( a = -1 \\), although the choice of which -\\(4\\)-torsion point to use doesn't matter. - -This procedure gives a canonical lift from \\(\mathcal E / \mathcal -E[4]\\) to \\(\mathcal E / \mathcal E[2]\\). Since it involves a -conditional rotation, we refer to it as *torquing* the point. - -# The Isogeny - -For \\(a = \pm 1\\), we have a \\(2\\)-isogeny -$$ -\theta\_{a,d} : \mathcal J\_{a\^2, -a(a+d)/(a-d)} \longrightarrow \mathcal E\_{a,d} -$$ -(or simply \\(\theta\\)) defined by -$$ -\theta\_{a,d} : (s,t) \mapsto \left( \frac{1}{\sqrt{ad-1}} \cdot \frac{2s}{t},\quad \frac{1+as\^2}{1-as\^2} \right). -$$ -Its dual is -$$ -\hat{\theta}\_{a,d} : \mathcal E\_{a,d} \longrightarrow \mathcal J\_{a\^2, -a(a+d)/(a-d)}, -$$ -defined by -$$ -\hat{\theta}\_{a,d} : (x,y) \mapsto \left( \sqrt{ad-1} \cdot \frac{xy}{1-ax\^2}, \frac{y^2 + ax^2}{1-ax^2} \right) -$$ - -The kernel of the isogeny is \\( \{(0, \pm 1)\} \\). -The image of the isogeny is \\(\[2\](\mathcal E)\\). To see this, -first note that because \\( \theta \circ \hat{\theta} = [2] \\), we -know that \\( \[2\](\mathcal E) \subseteq \theta(\mathcal J)\\); then, to see that -\\(\theta(\mathcal J)\\) is exactly \\(\[2\](\mathcal E)\\), -recall that isogenous elliptic curves over a finite field have the -same number of points (exercise 5.4 of Silverman), so that -$$ -\\# \theta(\mathcal J) = \frac {\\# \mathcal J} {\\# \ker \theta} -= \frac {\\# \mathcal E}{2} = \\# \[2\](\mathcal E). -$$ - -To determine the image \\(\theta(\mathcal J[2])\\) of the -\\(2\\)-torsion, we consider the image of the coset -\\(\theta((s,t) + \mathcal J[2])\\). -Let \\((x,y) = \theta(s,t)\\); then -\\(\theta(-s,-t) = (x,y)\\) and -\\(\theta(1/as, -t/as\^2) = (-x, -y)\\), -so that \\(\theta(\mathcal J[2]) = \mathcal E[2]\\). - -# Encoding with the Isogeny - -The Decaf paper recalls that, for a group \\( G \\) with normal -subgroup \\(G' \leq G\\), a group homomorphism \\( \phi : G -\rightarrow H \\) induces a homomorphism -$$ -\bar{\phi} : \frac G {G'} \longrightarrow \frac {\phi(G)}{\phi(G')} \leq \frac {H} {\phi(G')}, -$$ -and that the induced homomorphism \\(\bar{\phi}\\) is injective if -\\( \ker \phi \leq G' \\). In our context, the kernel of -\\(\theta\\) is \\( \\{(0, \pm 1)\\} \leq \mathcal J[2] \\), -so \\(\theta\\) gives an isomorphism -$$ -\frac {\mathcal J} {\mathcal J[2]} -\cong -\frac {\theta(\mathcal J)} {\theta(\mathcal J[2])} -\cong -\frac {\[2\](\mathcal E)} {\mathcal E[2]}. -$$ - -We can use the isomorphism to transfer the encoding of \\(\mathcal -J / \mathcal J[2] \\) defined above to \\(\[2\](\mathcal E)/\mathcal -E[2]\\), by encoding the Edwards point \\((x,y)\\) using the Jacobi -quartic encoding of \\(\theta\^{-1}(x,y)\\). - -Since \\(\\# (\[2\](\mathcal E) / \mathcal E[2]) = (\\#\mathcal -E)/4\\), if \\(\mathcal E\\) has cofactor \\(4\\), we're done. -Otherwise, if \\(\mathcal E\\) has cofactor \\(8\\), as in the -Curve25519 case, we use the torquing procedure to lift \\(\mathcal E -/ \mathcal E[4]\\) to \\(\mathcal E / \mathcal E[2]\\), and then -apply the encoding for \\( \[2\](\mathcal E) / \mathcal E[2] \\). - -# The Ristretto Encoding - -We can write the above encoding/decoding procedure in affine -coordinates, before describing optimized formulas to and from -projective coordinates. - -## Encoding in Affine Coordinates - -On input \\( (x,y) \in \[2\](\mathcal E)\\), a representative for a -coset in \\( \[2\](\mathcal E) / \mathcal E[4] \\): - -1. Check if \\( xy \\) is negative or \\( x = 0 \\); if so, torque - the point by setting \\( (x,y) \gets (x,y) + Q_4 \\), where - \\(Q_4\\) is a \\(4\\)-torsion point. - -2. Check if \\(x\\) is negative or \\( y = -1 \\); if so, set - \\( (x,y) \gets (x,y) + (0,-1) = (-x, -y) \\). - -3. Compute $$ s = +\sqrt {(-a) \frac {1 - y} {1 + y} }, $$ choosing - the positive square root. - -The output is then the (canonical) byte-encoding of \\(s\\). - -If \\(\mathcal E\\) has cofactor \\(4\\), we skip the first step, -since our input already represents a coset in -\\( \[2\](\mathcal E) / \mathcal E[2] \\). - -## Interpreting the Encoding Procedure - -How does this procedure correspond to the description involving -\\( \theta \\)? - -The first step lifts from \\( \mathcal E / \mathcal E[4] \\) to -\\(\mathcal E / \mathcal E[2]\\). To understand steps 2 and 3, -notice that the \\(y\\)-coordinate of \\(\theta(s,t)\\) is -$$ -y = \frac {1 + as\^2}{1 - as\^2}, -$$ -so that the \\(s\\)-coordinate of \\(\theta\^{-1}(x,y)\\) has -$$ -s\^2 = (-a)\frac {1-y}{1+y}. -$$ -Since -$$ -x = \frac 1 {\sqrt {ad - 1}} \frac {2s} {t}, -$$ -we also have -$$ -\frac s t = x \frac {\sqrt {ad-1}} 2, -$$ -so that the sign of \\(s/t\\) is determined by the sign of \\(x\\). - -Recall that to choose a canonical representative of \\( (s,t) + -\mathcal J[2] \\), it's sufficient to make two sign choices: the -sign of \\(s\\) and the sign of \\(s/t\\). Step 2 determines the -sign of \\(s/t\\), while step 3 computes \\(s\\) and determines its -sign (by choosing the positive square root). Finally, the check -that \\(y \neq -1\\) prevents division-by-zero when encoding the -identity; it falls out of the optimized formulas below. - -## Decoding to Affine Coordinates - -On input `s_bytes`, decoding proceeds as follows: - -1. Decode `s_bytes` to \\(s\\); reject if `s_bytes` is not the - canonical encoding of \\(s\\). - -2. Check whether \\(s\\) is negative; if so, reject. - -3. Compute -$$ -y \gets \frac {1 + as\^2}{1 - as\^2}. -$$ - -4. Compute -$$ -x \gets +\sqrt{ \frac{4s\^2} {ad(1+as\^2)\^2 - (1-as\^2)\^2}}, -$$ -choosing the positive square root, or reject if the square root does -not exist. - -5. Check whether \\(xy\\) is negative or \\(y = 0\\); if so, reject. - -# Encoding in Extended Coordinates - -The formulas above are given in affine coordinates, but the usual -internal representation is extended twisted Edwards coordinates \\( -(X:Y:Z:T) \\) with \\( x = X/Z \\), \\(y = Y/Z\\), \\(xy = T/Z \\). - -This section only covers the cofactor-\\(8\\) case, since it is more complicated: -selecting the distinguished representative of the coset -requires the affine coordinates \\( (x,y) \\), and computing \\( s -\\) requires an inverse square root. -As inversions are expensive, we'd like to be able to do this -whole computation with only one inverse square root, by batching -together the inversion and the inverse square root. - -It is not obvious how to do this, since we need the inverse square -root of one of two values, depending on what the distinguished -representative is, but the choice of representative depends on the -affine coordinates. However, an ingenious trick (due to Mike Hamburg) -allows recovering either of the inverse square roots we want. - -## Batching the Inversion and Inverse Square Root - -Write \\( (X\_0 : Y\_0 : Z\_0 : T\_0) \\) -for the coordinates of the initial representative, and write -\\( (X:Y:Z:T) \\) for the coordinates of the distinguished -representative of the coset. - -Since \\(y = Y/Z\\), in extended coordinates the formula for \\(s\\) becomes -$$ -s -= \sqrt{ (-a) \frac{ 1 - Y/Z}{1+Y/Z}} = \sqrt{\frac{Z - Y}{Z+Y}} \sqrt{-a} -= \frac {Z - Y} {\sqrt{Z\^2 - Y\^2}} \sqrt{-a}, -$$ -so we need to compute \\( 1 / \sqrt{Z^2 - Y^2} \\). - -The distinguished representative \\( (X:Y:Z:T) \\) is selected by the -torquing procedure in step 1, which conditionally adds a -\\(4\\)-torsion point \\(Q_4\\). As noted in the torquing section -above, \\( Q_4 = (\pm 1/\sqrt{a}, 0) \\), so we obtain -$$ -(X : Y : Z : T ) = -\begin{cases} -(X\_0 : Y\_0 : Z\_0 : T\_0) \\\\ -(\pm Y\_0 / \sqrt{a} : \mp X\_0 \sqrt{a} : Z\_0 : -T\_0) -\end{cases} -. -$$ -This means we want to compute either of -$$ -\frac {1} {\sqrt{Z^2 - Y^2}} -= -\begin{cases} -1 / \sqrt{Z\_0^2 - Y\_0^2} \\\\ -1 / \sqrt{Z\_0^2 - aX\_0^2} -\end{cases} -. -$$ -To relate these quantities, recall from the curve equation that -$$ --dX\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2, -$$ -so -$$ -(a-d)X\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2 + aX\^2Y\^2. -$$ -Factoring the right-hand side gives -$$ -(a-d)X\^2Y\^2 = (Z\^2 - Y\^2)(Z\^2 - aX\^2), -$$ -which relates the two quantities we want to compute: -$$ -\frac 1 {Z^2 - aX^2} = \frac 1 {a - d} \frac {Z^2 - Y^2} {X^2 Y^2} -$$ -so -$$ -\frac 1 {\sqrt{Z^2 - aX^2}} = \frac 1 {\sqrt{a - d}} \sqrt{ \frac {Z^2 - Y^2} {X^2 Y^2} } -$$ - -## Explicit Encoding Formulas - -Using this trick, we can write the encoding procedure explicitly: - -1. \\(u\_1 \gets (Z\_0 + Y\_0)(Z\_0 - Y\_0) - \textcolor{gray}{= Z\_0\^2 - Y\_0\^2} - \\) -2. \\(u\_2 \gets X\_0 Y\_0 \\) -3. \\(I \gets \mathrm{invsqrt}(u\_1 u\_2\^2) - \textcolor{gray}{= 1/\sqrt{X\_0\^2 Y\_0\^2 (Z\_0\^2 - Y\_0\^2)}} - \\) -4. \\(D\_1 \gets u\_1 I - \textcolor{gray}{= \sqrt{(Z\_0\^2 - Y\_0\^2)/(X\_0\^2 Y\_0\^2)} } - \\) -5. \\(D\_2 \gets u\_2 I - \textcolor{gray}{= \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} } - \\) -6. \\(Z\_{inv} \gets D\_1 D\_2 T\_0 - \textcolor{gray}{= (u\_1 u\_2)/(u\_1 u\_2\^2) T\_0 = T\_0 / X\_0 Y\_0 = 1/Z\_0} - \\) -7. If \\( T\_0 Z\_{inv} \textcolor{gray}{= x\_0 y\_0 }\\) is negative: - 1. \\( (X, Y) \gets (Y\_0 (\pm 1/\sqrt{a}), X\_0 (\mp \sqrt{a})) \\) - 2. \\( D \gets D\_1 / \sqrt{a-d} - \textcolor{gray}{= 1/\sqrt{Z\_0\^2 - a X\_0\^2} = 1/\sqrt{Z^2 -Y^2} } - \\) -8. Otherwise: - 1. \\( (X, Y) \gets (X\_0, Y\_0) \\) - 2. \\( D \gets D\_2 - \textcolor{gray}{= \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} = \pm 1/\sqrt{Z^2 - Y^2}} - \\) -9. If \\( X Z\_{inv} \textcolor{gray}{= x} \\) is negative, set \\( Y \gets - Y\\) -10. Compute \\( s \gets |\sqrt{-a} (Z - Y) D| \textcolor{gray}{= |\sqrt{-a} (Z - Y) / \sqrt{Z\^2 - Y\^2}| } \\) -11. Return the canonical byte encoding of \\( s \\). - -The choice of \\( Q\_4 = (i, 0) \\) when \\( a = -1 \\) is convenient -since it simplifies 7.1 to \\( (X,Y) \gets (iY_0, iX_0) \\). - -## Explicit Decoding Formulas - -As with encoding, we want to batch operations to use only a single -inverse square root. However, the procedure is much simpler since -there's no torquing. - -On input `s_bytes`: - -1. Check that `s_bytes` is the canonical byte-encoding of a field -element \\(s\\), otherwise reject. -2. Decode `s_bytes` to \\(s\\). -3. Check that \\( s \\) is nonnegative, otherwise reject. -4. \\( u_1 \gets 1 + as^2 \\) -5. \\( u_2 \gets 1 - as^2 \\) -6. \\( v \gets (ad)u_1^2 - u_2^2 \textcolor{gray}{= ad(1+as^2)^2 - (1-as^2)^2} \\) -7. \\( I \gets \mathrm{invsqrt}( v u_2^2 ) \textcolor{gray}{= 1/\sqrt{v u_2^2} } \\) -8. \\( D_x \gets Iu_2 \textcolor{gray}{= 1/\sqrt{v} } \\) -9. \\( D_y \gets ID_x v \textcolor{gray}{= I^2 u_2 v = (v u_2) / (v u_2^2) = 1/u_2 } \\) -10. \\( x \gets |2sD_x| \textcolor{gray}{= +\sqrt{ 4s^2 / (ad(1+as^2)^2 - (1-as^2)^2 )}}\\) -11. \\( y \gets u_1 D_y \textcolor{gray}{= (1+as^2)/(1-as^2) } \\) -12. \\( t \gets xy \\) -12. Check that \\(t \\) is nonnegative and that \\( y \neq 0 \\), otherwise reject. -13. Return \\( P = (x: y: 1: t) \\) - -# Batched Double-and-Encode Using \\( \hat \theta \\) - -The encoding is not batchable, since it requires an inverse square -root. However, since \\( \theta \circ \hat \theta = [2] P \\), it's -possible to compute the encoding of \\( [2]P \\) by using \\( \hat -\theta \\) instead of \\( \theta^{-1} \\). Since \\( \hat \theta \\) only -requires inversions, given \\( P\_1, \ldots, P\_n \\), it's possible -to compute the encodings of \\( [2]P\_1, \ldots, [2]P\_n \\) in a -batch. - -XXX write up details - -# Equality Testing - -Testing equality of two Ristretto points means testing whether they -are equal in the quotient group, i.e., whether they lie in the same -coset of \\(\mathcal E[4] \\) (for the cofactor-\\(8\\) case) or -\\(\mathcal E[2] \\) (for the cofactor-\\(4\\) case). - -Equality testing of points on the Edwards curve requires comparing to -affine coordinates, which requires an expensive inversion. However, -testing whether two points lie in the same coset can be done in -projective coordinates, making it actually *easier* than equality -testing in the original non-quotient group. - -XXX write up details - -# Elligator - -XXX write up details - -[ristretto_sage]: https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/aux/ristretto/ristretto.sage -[ristretto_libdecaf]: https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/ -[decaf_paper]: https://eprint.iacr.org/2015/673.pdf -[hwcd_jacobi]: https://eprint.iacr.org/2009/312.pdf -[hwcd_edwards]: https://eprint.iacr.org/2008/522.pdf -[edwards_edwards]: https://www.ams.org/journals/bull/2007-44-03/S0273-0979-07-01153-6/S0273-0979-07-01153-6.pdf -[twisted_edwards]: https://eprint.iacr.org/2008/013.pdf -[curve_models]: ../../curve_models/index.html \ No newline at end of file diff --git a/src/backend/avx2/constants.rs b/src/backend/avx2/constants.rs index 304a1be..76a644a 100644 --- a/src/backend/avx2/constants.rs +++ b/src/backend/avx2/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -10,7 +10,7 @@ //! This module contains constants used by the AVX2 backend. -use core::simd::u32x8; +use packed_simd::u32x8; use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; use backend::avx2::field::FieldElement32x4; diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 43f02f7..1510bda 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -431,7 +431,7 @@ mod test { println!("Testing B +- kB"); let P = constants::ED25519_BASEPOINT_POINT; - let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); + let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64); addition_test_helper(P, Q); } @@ -510,7 +510,7 @@ mod test { doubling_test_helper(P); println!("Testing [2]([k]B)"); - let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); + let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64); doubling_test_helper(P); } } diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 175ff14..6f4e1fd 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -40,7 +40,7 @@ const C_LANES64: u8 = 0b00_11_00_00; const D_LANES64: u8 = 0b11_00_00_00; use core::ops::{Add, Mul, Neg}; -use core::simd::{i32x8, u32x8, u64x4, IntoBits}; +use packed_simd::{i32x8, u32x8, u64x4, IntoBits}; use backend::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO}; use backend::u64::field::FieldElement64; diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index b13ea1d..10886af 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -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 // - Henry de Valence -// See the comment above the ristretto::notes module. +// Conditionally include the AVX2 notes if: +// - we're on nightly (so we can include docs at all) +// - we're in stage 2 of the build. +// The latter point prevents a really silly and annoying problem, +// where the location of ".." is different depending on whether we're +// building the crate for real, or whether we're in build.rs +// generating the lookup tables (in which case we're relative to the +// location of build.rs, not lib.rs, so the markdown file appears +// missing). #![cfg_attr( all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md") )] diff --git a/src/backend/avx2/scalar_mul/straus.rs b/src/backend/avx2/scalar_mul/straus.rs index 053713f..eed9084 100644 --- a/src/backend/avx2/scalar_mul/straus.rs +++ b/src/backend/avx2/scalar_mul/straus.rs @@ -20,6 +20,9 @@ use scalar::Scalar; use scalar_mul::window::{LookupTable, NafLookupTable5}; use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; +#[allow(unused_imports)] +use prelude::*; + /// Multiscalar multiplication using interleaved window / Straus' /// method. See the `Straus` struct in the serial backend for more /// details. @@ -30,7 +33,7 @@ use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; /// point representation on the fly. pub struct Straus {} -#[cfg(any(feature = "alloc", feature = "std"))] +#[cfg(feature = "alloc")] impl MultiscalarMul for Straus { type Point = EdwardsPoint; @@ -68,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(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { let nafs: Vec<_> = scalars .into_iter() .map(|c| c.borrow().non_adjacent_form(5)) .collect(); - let lookup_tables: Vec<_> = points + let lookup_tables: Vec<_> = match points .into_iter() - .map(|point| NafLookupTable5::::from(point.borrow())) - .collect(); + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>() + { + Some(x) => x, + None => return None, + }; let mut Q = ExtendedPoint::identity(); @@ -101,6 +107,7 @@ impl VartimeMultiscalarMul for Straus { } } } - Q.into() + + Some(Q.into()) } } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index aa44d52..75fa6c0 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -21,6 +21,12 @@ //! `32bit` since identifiers can't start with letters, and the backends //! do use `u32`/`u64`, so this seems like a least-bad option. +#[cfg(not(any(feature = "u32_backend", feature = "u64_backend", feature = "avx2_backend")))] +compile_error!( + "no curve25519-dalek backend cargo feature enabled! \ + please enable one of: u32_backend, u64_backend, avx2_backend" +); + #[cfg(feature = "u32_backend")] pub mod u32; diff --git a/src/backend/u32/constants.rs b/src/backend/u32/constants.rs index f68f662..e0e0525 100644 --- a/src/backend/u32/constants.rs +++ b/src/backend/u32/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index 8c65a46..9de460a 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u32/mod.rs b/src/backend/u32/mod.rs index bc1148e..4d6bc8b 100644 --- a/src/backend/u32/mod.rs +++ b/src/backend/u32/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 1cc23fa..0ac7fe3 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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 diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index d685ff3..25a013e 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; coding: utf-8; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/backend/u64/mod.rs b/src/backend/u64/mod.rs index a72dc0f..d329a89 100644 --- a/src/backend/u64/mod.rs +++ b/src/backend/u64/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/constants.rs b/src/constants.rs index fd8298c..c990b09 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index a88c37d..d2ded64 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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{ diff --git a/src/edwards.rs b/src/edwards.rs index 5f03929..68e6b9f 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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, { - // 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(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { - // XXX later when we do more fancy multiscalar mults, we can - // delegate based on the iter's size hint -- hdevalence + // 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(); diff --git a/src/field.rs b/src/field.rs index 938fa03..e523df9 100644 --- a/src/field.rs +++ b/src/field.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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 []); + } } diff --git a/src/lib.rs b/src/lib.rs index 70f80f5..862c81a 100644 --- a/src/lib.rs +++ b/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 // - Henry de Valence -#![cfg_attr(not(feature = "std"), no_std)] - -#![cfg_attr(feature = "alloc", feature(alloc))] +#![no_std] +#![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(feature = "nightly", feature(external_doc))] -#![cfg_attr(all(feature = "nightly", feature = "avx2_backend"), feature(stdsimd))] // 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; diff --git a/src/macros.rs b/src/macros.rs index 448d32c..3ec9d77 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/montgomery.rs b/src/montgomery.rs index 0b8450d..fe7423b 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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(); diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..be2f600 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,8 @@ +//! Crate-local prelude (for alloc-dependent features like `Vec`) + +// TODO: switch to alloc::prelude +#[cfg(all(feature = "alloc", not(feature = "std")))] +pub use alloc::vec::Vec; + +#[cfg(feature = "std")] +pub use std::vec::Vec; diff --git a/src/ristretto.rs b/src/ristretto.rs index 78cd178..ea54d27 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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 where I: IntoIterator { @@ -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(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(scalars: I, points: J) -> RistrettoPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { - let extended_points = points.into_iter().map(|P| P.borrow().0); + 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(); diff --git a/src/scalar.rs b/src/scalar.rs index 5b67732..703369b 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // Portions Copyright 2017 Brian Smith // See LICENSE for licensing information. // @@ -11,6 +11,132 @@ // - Brian Smith //! Arithmetic on scalars (integers mod the group order). +//! +//! Both the Ristretto group and the Ed25519 basepoint have prime order +//! \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). +//! +//! This code is intended to be useful with both the Ristretto group +//! (where everything is done modulo \\( \ell \\)), and the X/Ed25519 +//! setting, which mandates specific bit-twiddles that are not +//! well-defined modulo \\( \ell \\). +//! +//! 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::from_canonical_bytes(one_as_bytes); +//! +//! assert!(a.is_some()); +//! ``` +//! +//! However, if we give it bytes representing a scalar larger than \\( \ell \\) +//! (in this case, \\( \ell + 2 \\)), we'll get `None` back: +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let l_plus_two_bytes: [u8; 32] = [ +//! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, +//! 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +//! ]; +//! let a: Option = Scalar::from_canonical_bytes(l_plus_two_bytes); +//! +//! assert!(a.is_none()); +//! ``` +//! +//! Another way to create a `Scalar` is by reducing a \\(256\\)-bit integer mod +//! \\( \ell \\), for which one may use the +//! [`Scalar::from_bytes_mod_order`](struct.Scalar.html#method.from_bytes_mod_order) +//! method. In the case of the second example above, this would reduce the +//! resultant scalar \\( \mod \ell \\), producing \\( 2 \\): +//! +//! ``` +//! use curve25519_dalek::scalar::Scalar; +//! +//! let l_plus_two_bytes: [u8; 32] = [ +//! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, +//! 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//! 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +//! ]; +//! let a: Scalar = Scalar::from_bytes_mod_order(l_plus_two_bytes); +//! +//! let two: Scalar = Scalar::one() + Scalar::one(); +//! +//! assert!(a == two); +//! ``` +//! +//! 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::(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 for Scalar { + fn from(x: u8) -> Scalar { + let mut s_bytes = [0u8; 32]; + s_bytes[0] = x; + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u16) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u16(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + fn from(x: u32) -> Scalar { + use byteorder::{ByteOrder, LittleEndian}; + let mut s_bytes = [0u8; 32]; + LittleEndian::write_u32(&mut s_bytes, x); + Scalar{ bytes: s_bytes } + } +} + +impl From for Scalar { + /// 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 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(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::(msg.as_bytes()); /// # } /// ``` - /// pub fn hash_from_bytes(input: &[u8]) -> Scalar where D: Digest + 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(hash: D) -> Scalar where D: Digest + Default { - // XXX this seems clumsy let mut output = [0u8; 64]; output.copy_from_slice(hash.result().as_slice()); Scalar::from_bytes_mod_order_wide(&output) } /// 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()); + } + } } diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index 21bf29e..0053570 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -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(scalars: I, points: J) -> EdwardsPoint + fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, + J: IntoIterator>, { use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint}; use scalar_mul::window::NafLookupTable5; @@ -167,10 +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::::from(P.borrow())) - .collect(); + .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) + .collect::>>() + { + Some(x) => x, + None => return None, + }; let mut r = ProjectivePoint::identity(); @@ -188,6 +201,6 @@ impl VartimeMultiscalarMul for Straus { r = t.to_projective(); } - r.to_extended() + Some(r.to_extended()) } } diff --git a/src/scalar_mul/window.rs b/src/scalar_mul/window.rs index 91ebb65..c116136 100644 --- a/src/scalar_mul/window.rs +++ b/src/scalar_mul/window.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: diff --git a/src/traits.rs b/src/traits.rs index e706348..8db963a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,7 +1,7 @@ // -*- mode: rust; -*- // // This file is part of curve25519-dalek. -// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: @@ -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` 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(scalars: I, points: J) -> Option + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator>; + + /// Given an iterator of public scalars and an iterator of /// public points, compute /// $$ - /// 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 /// /// // A2 = (-a)*P + (-b)*Q + (-c)*R /// let minus_abc = abc.iter().map(|x| -x); - /// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]); + /// let A2 = RistrettoPoint::vartime_multiscalar_mul(minus_abc, &[P,Q,R]); /// // Note: minus_abc.into_iter(): Iterator /// /// assert_eq!(A1.compress(), (-A2).compress()); /// ``` + #[allow(non_snake_case)] fn vartime_multiscalar_mul(scalars: I, points: J) -> Self::Point where I: IntoIterator, I::Item: Borrow, J: IntoIterator, - J::Item: Borrow; + J::Item: Borrow, + Self::Point: Clone, + { + Self::optional_multiscalar_mul( + scalars, + points.into_iter().map(|P| Some(P.borrow().clone())) + ).unwrap() + } } // ------------------------------------------------------------------------