diff --git a/Makefile b/Makefile index 5baa02c..eabe38a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ +FEATURES := nightly yolocrypto doc: - cargo rustdoc --features "nightly yolocrypto" -- --html-in-header rustdoc-include-katex-header.html + cargo rustdoc --features "$(FEATURES)" -- --html-in-header rustdoc-include-katex-header.html doc-internal: - cargo rustdoc --features "nightly yolocrypto" -- --html-in-header rustdoc-include-katex-header.html --no-defaults --passes "collapse-docs" --passes "unindent-comments" + cargo rustdoc --features "$(FEATURES)" -- --html-in-header rustdoc-include-katex-header.html --document-private-items diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 3947fb9..be3a347 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -22,11 +22,9 @@ //! `32bit` since identifiers can't start with letters, and the backends //! do use `u32`/`u64`, so this seems like a least-bad option. -/// Code using `u32`s and a `(u32, u32) -> u64` multiplier. #[cfg(not(feature="radix_51"))] pub mod u32; -/// Code using `u64`s and a `(u64, u64) -> u128` multiplier. #[cfg(feature="radix_51")] pub mod u64; diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index 10c17e1..fb6b36f 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -8,19 +8,12 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! Field arithmetic for ℤ/(2²⁵⁵-19), using 32-bit arithmetic with -//! 64-bit products. +//! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(32\\)-bit +//! limbs with \\(64\\)-bit products. //! -//! This code was originally derived from Adam Langley's -//! curve25519-donna and (Golang) ed25519 implementations. -//! -//! This implementation is intended for platforms that can multiply -//! 32-bit inputs to produce 64-bit outputs. -//! -//! This implementation is not preferred for use on x86_64, since the -//! 64-bit implementation is both much simpler and much faster. -//! However, that implementation requires Rust's `u128`, which is not -//! yet stable. +//! This code was originally derived from Adam Langley's Golang ed25519 +//! implementation, and was then rewritten to use unsigned limbs instead +//! of signed limbs. use core::fmt::Debug; use core::ops::{Add, AddAssign}; @@ -30,28 +23,28 @@ use core::ops::Neg; use subtle::ConditionallyAssignable; -/// A `FieldElement32` represents an element of the field GF(2^255 - 19). +/// A `FieldElement32` represents an element of the field +/// \\( \mathbb Z / (2\^{255} - 19)\\). /// -/// In the 32-bit implementation, a `FieldElement32` is represented in -/// radix 2^25.5 as ten `u32`s, so that an element t, entries -/// t[0],...,t[9], represents `sum(t[i]*2^ceil(i*51/2))`. +/// In the 32-bit implementation, a `FieldElement` is represented in +/// radix \\(2\^{25.5}\\) as ten `u32`s. This means that a field +/// element \\(x\\) is represented as +/// $$ +/// x = \sum\_{i=0}\^9 x\_i 2\^{\lceil i \frac {51} 2 \rceil} +/// = x\_0 + x\_1 2\^{26} + x\_2 2\^{51} + x\_3 2\^{77} + \cdots + x\_9 2\^{230}; +/// $$ +/// the coefficients are alternately bounded by \\(2\^{25}\\) and +/// \\(2\^{26}\\). The limbs are allowed to grow between reductions up +/// to \\(2\^{25+b}\\) or \\(2\^{26+b}\\), where \\(b = 1.75\\). /// -/// The coefficients t[i] are allowed to grow between multiplications. -/// -/// XXX document by how much -/// -/// # Warning -/// -/// You almost certainly do not want to use `FieldElement32` directly. Consider -/// using `curve25519_dalek::field::FieldElement`, which will automatically -/// select between `FieldElement32` and `FieldElement64` depending on whether -/// curve25519-dalek was compiled with `--features="nightly"`. -/// -/// This implementation, `FieldElement32`, is intended for platforms that can -/// multiply 32-bit inputs to produce 64-bit outputs, and is not preferred for -/// use on x86_64, since the 64-bit implementation is both much simpler and much -/// faster. However, the `FieldElement64` implementation requires Rust's -/// `u128`, which is not yet stable. +/// # Note +/// +/// The `curve25519_dalek::field` module provides a type alias +/// `curve25519_dalek::field::FieldElement` to either `FieldElement64` +/// or `FieldElement32`. +/// +/// The backend-specific type `FieldElement32` should not be used +/// outside of the `curve25519_dalek::field` module. #[derive(Copy, Clone)] pub struct FieldElement32(pub (crate) [u32; 10]); diff --git a/src/backend/u32/mod.rs b/src/backend/u32/mod.rs index fa54a15..bd4cb75 100644 --- a/src/backend/u32/mod.rs +++ b/src/backend/u32/mod.rs @@ -8,8 +8,14 @@ // - Isis Agora Lovecruft // - Henry de Valence +//! The `u32` backend uses `u32`s and a `(u32, u32) -> u64` multiplier. +//! +//! This code is intended to be portable, but it requires that +//! multiplication of two \\(32\\)-bit values to a \\(64\\)-bit result +//! is constant-time on the target platform. + pub mod field; pub mod scalar; -pub mod constants; \ No newline at end of file +pub mod constants; diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 3af570c..a51ee81 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -8,9 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! This module contains various constants (such as curve parameters -//! and useful field elements like `sqrt(-1)`), as well as -//! lookup tables of pre-computed points. +//! This module contains backend-specific constant values, such as the 64-bit limbs of curve constants. use backend::u64::field::FieldElement64; use backend::u64::scalar::Scalar64; diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index 3c6d4f0..e5b152e 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -8,14 +8,8 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! Field arithmetic for ℤ/(2²⁵⁵-19), using 64-bit arithmetic wuth -//! 128-bit products. -//! -//! On x86_64, the multiplications lower to `MUL` instructions taking -//! 64-bit inputs and producing 128-bit outputs. On other platforms, -//! this implementation is not recommended. On Haswell and newer, the -//! BMI2 instruction set provides `MULX` and friends, which gives even -//! better performance. +//! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(64\\)-bit +//! limbs with \\(128\\)-bit products. use core::fmt::Debug; use core::ops::{Add, AddAssign}; @@ -25,25 +19,21 @@ use core::ops::Neg; use subtle::ConditionallyAssignable; -/// A `FieldElement64` represents an element of the field GF(2^255 - 19). +/// A `FieldElement64` represents an element of the field +/// \\( \mathbb Z / (2\^{255} - 19)\\). /// /// In the 64-bit implementation, a `FieldElement` is represented in -/// radix 2^51 as five `u64`s; the coefficients are allowed to grow up -/// to 2^54 between reductions mod `p`. +/// radix \\(2\^{51}\\) as five `u64`s; the coefficients are allowed to +/// grow up to \\(2\^{54}\\) between reductions modulo \\(p\\). /// -/// # Warning -/// -/// You almost certainly do not want to use `FieldElement64` directly. Consider -/// using `curve25519_dalek::field::FieldElement`, which will automatically -/// select between `FieldElement32` and `FieldElement64` depending on whether -/// curve25519-dalek was compiled with `--features="nightly"`. -/// -/// This implementation, `FieldElement64`, is intended for x64_64 platforms, -/// which have the `MUL` instructions taking 64-bit inputs and producing 128-bit -/// outputs. On other platforms, this implementation is not recommended. On -/// Haswell and newer, the BMI2 instruction set provides `MULX` and friends, -/// which gives even better performance. This implementation requires Rust's -/// `u128`, which is not yet stable. +/// # Note +/// +/// The `curve25519_dalek::field` module provides a type alias +/// `curve25519_dalek::field::FieldElement` to either `FieldElement64` +/// or `FieldElement32`. +/// +/// The backend-specific type `FieldElement64` should not be used +/// outside of the `curve25519_dalek::field` module. #[derive(Copy, Clone)] pub struct FieldElement64(pub (crate) [u64; 5]); diff --git a/src/backend/u64/mod.rs b/src/backend/u64/mod.rs index fa54a15..51980d8 100644 --- a/src/backend/u64/mod.rs +++ b/src/backend/u64/mod.rs @@ -8,8 +8,19 @@ // - Isis Agora Lovecruft // - Henry de Valence +//! The `u64` backend uses `u64`s and a `(u64, u64) -> u128` multiplier. +//! +//! On x86_64, the idiom `(x as u128) * (y as u128)` lowers to `MUL` +//! instructions taking 64-bit inputs and producing 128-bit outputs. On +//! other platforms, this implementation is not recommended. +//! +//! On Haswell and newer, the BMI2 extension provides `MULX`, and on +//! Broadwell and newer, the ADX extension provides `ADCX` and `ADOX` +//! (allowing the CPU to compute two carry chains in parallel). These +//! will be used if available. + pub mod field; pub mod scalar; -pub mod constants; \ No newline at end of file +pub mod constants; diff --git a/src/backend/u64/scalar.rs b/src/backend/u64/scalar.rs index a6d14ad..12da445 100644 --- a/src/backend/u64/scalar.rs +++ b/src/backend/u64/scalar.rs @@ -1,19 +1,23 @@ -//! Arithmetic mod 2^252 + 27742317777372353535851937790883648493 -//! with 5 52-bit unsigned limbs. 51-bit limbs would cover the -//! desired bit range (253 bits), but isn't large enough to reduce -//! a 512 bit number with Montgomery multiplication, so 52 bits is -//! used instead +//! Arithmetic mod \\(2\^{252} + 27742317777372353535851937790883648493\\) +//! with five \\(52\\)-bit unsigned limbs. //! -//! To see that this is safe for intermediate results, note that -//! the largest limb in a 5 by 5 product of 52-bit limbs will be +//! \\(51\\)-bit limbs would cover the desired bit range (\\(253\\) +//! bits), but isn't large enough to reduce a \\(512\\)-bit number with +//! Montgomery multiplication, so \\(52\\) bits is used instead. To see +//! that this is safe for intermediate results, note that the largest +//! limb in a \\(5\times 5\\) product of \\(52\\)-bit limbs will be +//! +//! ```text //! (0xfffffffffffff^2) * 5 = 0x4ffffffffffff60000000000005 (107 bits). +//! ``` use core::fmt::Debug; use core::ops::{Index, IndexMut}; use constants; -/// The `Scalar64` struct represents an element in ℤ/lℤ as 5 52-bit limbs +/// The `Scalar64` struct represents an element in +/// \\(\mathbb Z / \ell \mathbb Z\\) as 5 \\(52\\)-bit limbs. #[derive(Copy,Clone)] pub struct Scalar64(pub [u64; 5]); diff --git a/src/constants.rs b/src/constants.rs index 1ec4850..b4c4646 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -8,9 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! This module contains various constants (such as curve parameters -//! and useful field elements like `sqrt(-1)`), as well as -//! lookup tables of pre-computed points. +//! This module contains various constants, such as the Ristretto and Ed25519 basepoints. //! //! Most of the constants are given with //! `LONG_DESCRIPTIVE_UPPER_CASE_NAMES`, but they can be brought into diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index 0fe5609..ad11a9b 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -15,59 +15,112 @@ //! //! Internally, we use several different models for the curve. Here //! is a sketch of the relationship between the models, following [a -//! post](https://moderncrypto.org/mail-archive/curves/2016/000807.html) -//! by Ben Smith on the moderncrypto mailing list. +//! post][smith-moderncrypto] +//! by Ben Smith on the `moderncrypto` mailing list. This is also briefly +//! discussed in section 2.5 of [_Montgomery curves and their +//! arithmetic_][costello-smith-2017] by Costello and Smith. //! //! Begin with the affine equation for the curve, +//! $$ +//! -x\^2 + y\^2 = 1 + dx\^2y\^2. +//! $$ +//! Next, pass to the projective closure \\(\mathbb P\^1 \times \mathbb +//! P\^1 \\) by setting \\(x=X/Z\\), \\(y=Y/T.\\) Clearing denominators +//! gives the model +//! $$ +//! -X\^2T\^2 + Y\^2Z\^2 = Z\^2T\^2 + dX\^2Y\^2. +//! $$ +//! In `curve25519-dalek`, this is represented as the `CompletedPoint` +//! struct. +//! To map from \\(\mathbb P\^1 \times \mathbb P\^1 \\), a product of +//! two lines, to \\(\mathbb P\^3\\), we use the [Segre +//! embedding](https://en.wikipedia.org/wiki/Segre_embedding) +//! $$ +//! \sigma : ((X:Z),(Y:T)) \mapsto (XY:XT:ZY:ZT). +//! $$ +//! Using coordinates \\( (W_0:W_1:W_2:W_3) \\) for \\(\mathbb P\^3\\), +//! the image \\(\sigma (\mathbb P\^1 \times \mathbb P\^1) \\) is the +//! surface defined by \\( W_0 W_3 = W_1 W_2 \\), and under \\( +//! \sigma\\), the equation above becomes +//! $$ +//! -W\_1\^2 + W\_2\^2 = W\_3\^2 + dW\_0\^2, +//! $$ +//! so that the curve is given by the pair of equations +//! $$ +//! \begin{aligned} +//! -W\_1\^2 + W\_2\^2 &= W\_3\^2 + dW\_0\^2, \\\\ +//! W_0 W_3 &= W_1 W_2. +//! \end{aligned} +//! $$ +//! Up to variable naming, this is exactly the "extended" curve model +//! introduced in [_Twisted Edwards Curves +//! Revisited_][hisil-wong-carter-dawson-2008] by Hisil, Wong, Carter, +//! and Dawson. In `curve25519-dalek`, it is represented as the +//! `ExtendedPoint` struct. We can map from \\(\mathbb P\^3 \\) to +//! \\(\mathbb P\^2 \\) by sending \\( (W\_0:W\_1:W\_2:W\_3) \\) to \\( +//! (W\_1:W\_2:W\_3) \\). Notice that +//! $$ +//! \frac {W\_1} {W\_3} = \frac {XT} {ZT} = \frac X Z = x, +//! $$ +//! and +//! $$ +//! \frac {W\_2} {W\_3} = \frac {YZ} {ZT} = \frac Y T = y, +//! $$ +//! so this is the same as if we had started with the affine model +//! and passed to \\( \mathbb P\^2 \\) by setting \\( x = W\_1 / W\_3 +//! \\), \\(y = W\_2 / W\_3 \\). +//! Up to variable naming, this is the projective representation +//! introduced in in [_Twisted Edwards +//! Curves_][bernstein-birkner-joye-lange-peters-2008] by Bernstein, +//! Birkner, Joye, Lange, and Peters. In `curve25519-dalek`, it is +//! represented by the `ProjectivePoint` struct. //! -//!     -x² + y² = 1 + dx²y².       (1) +//! # Passing between curve models //! -//! Next, pass to the projective closure 𝗣^1 x 𝗣^1 by setting x=X/Z, -//! y=Y/T. Clearing denominators gives the model +//! Although the \\( \mathbb P\^3 \\) model provides faster addition +//! formulas, the \\( \mathbb P\^2 \\) model provides faster doubling +//! formulas. Hisil, Wong, Carter, and Dawson therefore suggest mixing +//! coordinate systems for scalar multiplication, attributing the idea +//! to [a 1998 paper][cohen-miyaji-ono-1998] of Cohen, Miyagi, and Ono. //! -//!     -X²T² + Y²Z² = Z²T² + dX²Y². (2) +//! Their suggestion is to vary the formulas used by context, using a +//! \\( \mathbb P\^2 \rightarrow \mathbb P\^2 \\) doubling formula when +//! a doubling is followed +//! by another doubling, a \\( \mathbb P\^2 \rightarrow \mathbb P\^3 \\) +//! doubling formula when a doubling is followed by an addition, and +//! computing point additions using a \\( \mathbb P\^3 \times \mathbb P\^3 +//! \rightarrow \mathbb P\^2 \\) formula. //! -//! To map from 𝗣^1 x 𝗣^1, a product of two lines, to 𝗣^3, we use the -//! Segre embedding, +//! The `ref10` reference implementation of [Ed25519][ed25519], by +//! Bernstein, Duif, Lange, Schwabe, and Yang, tweaks +//! this strategy, factoring the addition formulas through the +//! completion \\( \mathbb P\^1 \times \mathbb P\^1 \\), so that the +//! output of an addition or doubling always lies in \\( \mathbb P\^1 \times +//! \mathbb P\^1\\), and the choice of which formula to use is replaced +//! by a choice of whether to convert the result to \\( \mathbb P\^2 \\) +//! or \\(\mathbb P\^2 \\). However, this tweak is not described in +//! their paper, only in their software. //! -//!     σ : ((X:Z),(Y:T)) ↦ (XY:XT:ZY:ZT).  (3) +//! Our naming for the `CompletedPoint` (\\(\mathbb P\^1 \times \mathbb +//! P\^1 \\)), `ProjectivePoint` (\\(\mathbb P\^2 \\)), and +//! `ExtendedPoint` (\\(\mathbb P\^3 \\)) structs follows the naming in +//! Adam Langley's [Golang ed25519][agl-ed25519] implementation, which +//! `curve25519-dalek` was originally derived from. //! -//! Using coordinates (W₀:W₁:W₂:W₃) for 𝗣^3, the image of σ(𝗣^1 x 𝗣^1) -//! is the surface defined by W₀W₃=W₁W₂, and under σ, equation (2) -//! becomes +//! Finally, to accelerate readditions, we use two cached point formats +//! in "Niels coordinates", named for Niels Duif, +//! one for the affine model and one for the \\( \mathbb P\^3 \\) model: //! -//!     -W₁² + W₂² = W₃² + dW₀².   (4) +//! * `AffineNielsPoint`: \\( (y+x, y-x, 2dxy) \\) +//! * `ProjectiveNielsPoint`: \\( (Y+X, Y-X, Z, 2dXY) \\) //! -//! Up to variable naming, this is exactly the curve model introduced -//! in ["Twisted Edwards Curves -//! Revisited"](https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf) -//! by Hisil, Wong, Carter, and Dawson. We can map from 𝗣^3 to 𝗣² by -//! sending (W₀:W₁:W₂:W₃) to (W₁:W₂:W₃). Notice that -//! -//!     W₁/W₃ = XT/ZT = X/Z = x    (5) -//! -//!     W₂/W₃ = ZY/ZT = Y/T = y,   (6) -//! -//! so this is the same as if we had started with the affine model (1) -//! and passed to 𝗣^2 by setting `x = W₁/W₃`, `y = W₂/W₃`. Up to -//! variable naming, this is the projective representation introduced -//! in ["Twisted Edwards Curves"](https://eprint.iacr.org/2008/013). -//! -//! Following the implementation strategy in the ref10 reference -//! implementation for [Ed25519](https://ed25519.cr.yp.to/ed25519-20110926.pdf), -//! we use several different models for curve points: -//! -//! * `CompletedPoint`: points in 𝗣^1 x 𝗣^1; -//! * `ExtendedPoint`: points in 𝗣^3; -//! * `ProjectivePoint`: points in 𝗣^2. -//! -//! Finally, to accelerate additions, we use two cached point formats, -//! one for the affine model and one for the 𝗣^3 model: -//! -//! * `AffineNielsPoint`: `(y+x, y-x, 2dxy)` -//! * `ProjectiveNielsPoint`: `(Y+X, Y-X, Z, 2dXY)` -//! -//! [1]: https://moderncrypto.org/mail-archive/curves/2016/000807.html +//! [smith-moderncrypto]: https://moderncrypto.org/mail-archive/curves/2016/000807.html +//! [costello-smith-2017]: https://eprint.iacr.org/2017/212 +//! [hisil-wong-carter-dawson-2008]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf +//! [bernstein-birkner-joye-lange-peters-2008]: https://eprint.iacr.org/2008/013 +//! [cohen-miyaji-ono-1998]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf +//! [ed25519]: https://eprint.iacr.org/2011/368 +//! [agl-ed25519]: https://github.com/agl/ed25519 #![allow(non_snake_case)] @@ -85,8 +138,13 @@ use traits::ValidityCheck; // Internal point representations // ------------------------------------------------------------------------ -/// A `ProjectivePoint` is a point on the curve in 𝗣²(𝔽ₚ). -/// A point (x,y) in the affine model corresponds to (x:y:1). +/// A `ProjectivePoint` is a point \\((X:Y:Z)\\) on the \\(\mathbb +/// P\^2\\) model of the curve. +/// A point \\((x,y)\\) in the affine model corresponds to +/// \\((x:y:1)\\). +/// +/// More details on the relationships between the different curve models +/// can be found in the module-level documentation. #[derive(Copy, Clone)] pub struct ProjectivePoint { pub X: FieldElement, @@ -94,8 +152,13 @@ pub struct ProjectivePoint { pub Z: FieldElement, } -/// A `CompletedPoint` is a point ((X:Z), (Y:T)) in 𝗣¹(𝔽ₚ)×𝗣¹(𝔽ₚ). -/// A point (x,y) in the affine model corresponds to ((x:1),(y:1)). +/// A `CompletedPoint` is a point \\(((X:Z), (Y:T))\\) on the \\(\mathbb +/// P\^1 \times \mathbb P\^1 \\) model of the curve. +/// A point (x,y) in the affine model corresponds to \\( ((x:1),(y:1)) +/// \\). +/// +/// More details on the relationships between the different curve models +/// can be found in the module-level documentation. #[derive(Copy, Clone)] #[allow(missing_docs)] pub struct CompletedPoint { @@ -106,9 +169,10 @@ pub struct CompletedPoint { } /// A pre-computed point in the affine model for the curve, represented as -/// (y+x, y-x, 2dxy). These precomputations accelerate addition and -/// subtraction, and were introduced by Niels Duif in the ed25519 paper -/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). +/// \\((y+x, y-x, 2dxy)\\) in "Niels coordinates". +/// +/// More details on the relationships between the different curve models +/// can be found in the module-level documentation. // Safe to derive Eq because affine coordinates. #[derive(Copy, Clone, Eq, PartialEq)] #[allow(missing_docs)] @@ -118,10 +182,11 @@ pub struct AffineNielsPoint { pub xy2d: FieldElement, } -/// A pre-computed point in the P³(𝔽ₚ) model for the curve, represented as -/// (Y+X, Y-X, Z, 2dXY). These precomputations accelerate addition and -/// subtraction, and were introduced by Niels Duif in the ed25519 paper -/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). +/// A pre-computed point on the \\( \mathbb P\^3 \\) model for the +/// curve, represented as \\((Y+X, Y-X, Z, 2dXY)\\) in "Niels coordinates". +/// +/// More details on the relationships between the different curve models +/// can be found in the module-level documentation. #[derive(Copy, Clone)] pub struct ProjectiveNielsPoint { pub Y_plus_X: FieldElement, @@ -213,14 +278,10 @@ impl ConditionallyAssignable for AffineNielsPoint { // ------------------------------------------------------------------------ impl ProjectivePoint { - /// Convert to the extended twisted Edwards representation of this - /// point. + /// Convert this point from the \\( \mathbb P\^2 \\) model to the + /// \\( \mathbb P\^3 \\) model. /// - /// From §3 in [0]: - /// - /// Given (X:Y:Z) in Ɛ, passing to Ɛₑ can be performed in 3M+1S by - /// computing (XZ,YZ,XY,Z²). (Note that in that paper, points are - /// (X:Y:T:Z) so this really does match the code below). + /// This costs \\(3 \mathrm M + 1 \mathrm S\\). pub fn to_extended(&self) -> ExtendedPoint { ExtendedPoint{ X: &self.X * &self.Z, @@ -232,7 +293,10 @@ impl ProjectivePoint { } impl CompletedPoint { - /// Convert to a ProjectivePoint + /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1 + /// \\) model to the \\( \mathbb P\^2 \\) model. + /// + /// This costs \\(3 \mathrm M \\). pub fn to_projective(&self) -> ProjectivePoint { ProjectivePoint{ X: &self.X * &self.T, @@ -241,7 +305,10 @@ impl CompletedPoint { } } - /// Convert to an ExtendedPoint + /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1 + /// \\) model to the \\( \mathbb P\^3 \\) model. + /// + /// This costs \\(4 \mathrm M \\). pub fn to_extended(&self) -> ExtendedPoint { ExtendedPoint{ X: &self.X * &self.T, @@ -280,8 +347,13 @@ impl ProjectivePoint { // Addition and Subtraction // ------------------------------------------------------------------------ -// These are doc(hidden) so they don't appear in the public API docs. -#[doc(hidden)] +// XXX(hdevalence) These were doc(hidden) so they don't appear in the +// public API docs. +// However, that prevents them being used with --document-private-items, +// so comment out the doc(hidden) for now until this is resolved +// +// upstream rust issue: https://github.com/rust-lang/rust/issues/46380 +//#[doc(hidden)] impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { type Output = CompletedPoint; @@ -303,7 +375,7 @@ impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { } } -#[doc(hidden)] +//#[doc(hidden)] impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { type Output = CompletedPoint; @@ -325,7 +397,7 @@ impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { } } -#[doc(hidden)] +//#[doc(hidden)] impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { type Output = CompletedPoint; @@ -346,7 +418,7 @@ impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { } } -#[doc(hidden)] +//#[doc(hidden)] impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { type Output = CompletedPoint; diff --git a/src/edwards.rs b/src/edwards.rs index 552f696..f8101df 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -52,12 +52,11 @@ use traits::select_precomputed_point; // Compressed points // ------------------------------------------------------------------------ -/// In "Edwards y" format, the point `(x,y)` on the curve is -/// determined by the `y`-coordinate and the sign of `x`, marshalled -/// into a 32-byte array. +/// In "Edwards y" / "Ed25519" format, the curve point \\((x,y)\\) is +/// determined by the \\(y\\)-coordinate and the sign of \\(x\\). /// /// The first 255 bits of a `CompressedEdwardsY` represent the -/// y-coordinate. The high bit of the 32nd byte gives the sign of `x`. +/// \\(y\\)-coordinate. The high bit of the 32nd byte gives the sign of \\(x\\). #[derive(Copy, Clone, Eq, PartialEq)] pub struct CompressedEdwardsY(pub [u8; 32]); @@ -80,9 +79,9 @@ impl CompressedEdwardsY { /// Attempt to decompress to an `ExtendedPoint`. /// - /// Returns `None` if the input is not the `y`-coordinate of a + /// Returns `None` if the input is not the \\(y\\)-coordinate of a /// curve point. - pub fn decompress(&self) -> Option { // FromBytes() + pub fn decompress(&self) -> Option { let Y = FieldElement::from_bytes(self.as_bytes()); let Z = FieldElement::one(); let YY = Y.square(); @@ -160,8 +159,11 @@ impl<'de> Deserialize<'de> for ExtendedPoint { // Internal point representations // ------------------------------------------------------------------------ -/// An `ExtendedPoint` is a point on the curve in 𝗣³(𝔽ₚ). -/// A point (x,y) in the affine model corresponds to (x:y:1:xy). +/// An `ExtendedPoint` represents a point on the Edwards form of Curve25519. +/// +/// The name refers to the extended twisted Edwards coordinates of +/// Hisil, Wong, Carter, and Dawson, and more details on curve models +/// can be found in the `curve25519-dalek` internal documentation. #[derive(Copy, Clone)] #[allow(missing_docs)] pub struct ExtendedPoint { @@ -233,7 +235,7 @@ impl Equal for ExtendedPoint { // ------------------------------------------------------------------------ impl ExtendedPoint { - /// Convert to a ProjectiveNielsPoint + /// Convert to a `ProjectiveNielsPoint` pub(crate) fn to_projective_niels(&self) -> ProjectiveNielsPoint { ProjectiveNielsPoint{ Y_plus_X: &self.Y + &self.X, @@ -243,11 +245,10 @@ impl ExtendedPoint { } } - /// Convert the representation of this point from extended Twisted Edwards - /// coodinates to projective coordinates. + /// Convert the representation of this point from extended + /// coordinates to projective coordinates. /// - /// Given a point in Ɛₑ, we can convert to projective coordinates - /// cost-free by simply ignoring T. + /// Free. pub(crate) fn to_projective(&self) -> ProjectivePoint { ProjectivePoint{ X: self.X, @@ -428,8 +429,8 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { type Output = ExtendedPoint; /// Scalar multiplication: compute `scalar * self`. /// - /// Uses a window of size 4. Note: for scalar multiplication of - /// the basepoint, `basepoint_mult` is approximately 4x faster. + /// For scalar multiplication of a basepoint, + /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &'b Scalar) -> ExtendedPoint { // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] let P = self.to_projective_niels(); @@ -469,27 +470,32 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { type Output = ExtendedPoint; - /// Scalar multiplication: compute `self * point`. + /// Scalar multiplication: compute `scalar * self`. /// - /// Uses a window of size 4. Note: for scalar multiplication of - /// the basepoint, `basepoint_mult` is approximately 4x faster. + /// For scalar multiplication of a basepoint, + /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, point: &'b ExtendedPoint) -> ExtendedPoint { point * &self } } -/// Given a vector of (possibly secret) scalars and a vector of -/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. +/// Given an iterator of (possibly secret) scalars and an iterator of +/// (possibly secret) points, compute +/// $$ +/// Q = c\_1 P\_1 + \cdots + c\_n P\_n. +/// $$ /// /// This function has the same behaviour as /// `vartime::multiscalar_mult` but is constant-time. /// /// # Input /// -/// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an -/// error to call this function with two vectors of different lengths. +/// A iterable of `Scalar`s and a iterable of `ExtendedPoints`. It is an +/// error to call this function with two iterators of different lengths. /// /// XXX need to clear memory +// XXX later when we do more fancy multiscalar mults, we can delegate +// based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, @@ -550,68 +556,76 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint Q } -/// Precomputation +/// A precomputed table of multiples of a basepoint, for accelerating +/// fixed-base scalar multiplication. One table, for the Ed25519 +/// basepoint, is provided in the `constants` module. /// -/// XXX we should box the internals +/// The basepoint tables are reasonably large (30KB), so they should +/// probably be boxed. #[derive(Clone)] pub struct EdwardsBasepointTable(pub(crate) [[AffineNielsPoint; 8]; 32]); +impl EdwardsBasepointTable { + /// The computation uses Pippeneger's algorithm, as described on + /// page 13 of the Ed25519 paper. Write the scalar \\(a\\) in radix \\(16\\) with + /// coefficients in \\([-8,8)\\), i.e., + /// $$ + /// a = a\_0 + a\_1 16\^1 + \cdots + a\_{63} 16\^{63}, + /// $$ + /// with \\(-8 \leq a_i < 8\\). Then + /// $$ + /// a B = a\_0 B + a\_1 16\^1 B + \cdots + a\_{63} 16\^{63} B. + /// $$ + /// Grouping even and odd coefficients gives + /// $$ + /// \begin{aligned} + /// a B = \quad a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B \\\\ + /// + a\_1 16\^1 B +& a\_3 16\^3 B + \cdots + a\_{63} 16\^{63} B \\\\ + /// = \quad(a\_0 16\^0 B +& a\_2 16\^2 B + \cdots + a\_{62} 16\^{62} B) \\\\ + /// + 16(a\_1 16\^0 B +& a\_3 16\^2 B + \cdots + a\_{63} 16\^{62} B). \\\\ + /// \end{aligned} + /// $$ + /// We then use the `select_precomputed_point` function, which + /// takes \\(-8 \leq x < 8\\) and \\([16\^{2i} B, \ldots, 8\cdot16\^{2i} B]\\), + /// and returns \\(x \cdot 16\^{2i} \cdot B\\) in constant time. + /// + /// The radix-\\(16\\) representation requires that the scalar is bounded + /// by \\(2\^{255}\\), which is always the case. + fn basepoint_mul(&self, scalar: &Scalar) -> ExtendedPoint { + let a = scalar.to_radix_16(); + + let mut P = ExtendedPoint::identity(); + + for i in (0..64).filter(|x| x % 2 == 1) { + P = (&P + &select_precomputed_point(a[i], &self.0[i/2])).to_extended(); + } + + P = P.mult_by_pow_2(4); + + for i in (0..64).filter(|x| x % 2 == 0) { + P = (&P + &select_precomputed_point(a[i], &self.0[i/2])).to_extended(); + } + + P + } +} + impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { type Output = ExtendedPoint; - /// Construct an `ExtendedPoint` from a `Scalar`, `scalar`, by - /// computing the multiple `aB` of the basepoint `B`. - /// - /// Precondition: the scalar must be reduced. - /// - /// The computation proceeds as follows, as described on page 13 - /// of the Ed25519 paper. Write the scalar `a` in radix 16 with - /// coefficients in [-8,8), i.e., - /// - /// a = a_0 + a_1*16^1 + ... + a_63*16^63, - /// - /// with -8 ≤ a_i < 8. Then - /// - /// a*B = a_0*B + a_1*16^1*B + ... + a_63*16^63*B. - /// - /// Grouping even and odd coefficients gives - /// - /// a*B = a_0*16^0*B + a_2*16^2*B + ... + a_62*16^62*B - /// + a_1*16^1*B + a_3*16^3*B + ... + a_63*16^63*B - /// = (a_0*16^0*B + a_2*16^2*B + ... + a_62*16^62*B) - /// + 16*(a_1*16^0*B + a_3*16^2*B + ... + a_63*16^62*B). - /// - /// We then use the `select_precomputed_point` function, which - /// takes `-8 ≤ x < 8` and `[16^2i * B, ..., 8 * 16^2i * B]`, - /// and returns `x * 16^2i * B` in constant time. + /// Construct an `ExtendedPoint` from a `Scalar` \\(a\\) by + /// computing the multiple \\(aB\\) of this basepoint \\(B\\). fn mul(self, scalar: &'b Scalar) -> ExtendedPoint { - let e = scalar.to_radix_16(); - let mut h = ExtendedPoint::identity(); - let mut t: CompletedPoint; - - for i in (0..64).filter(|x| x % 2 == 1) { - t = &h + &select_precomputed_point(e[i], &self.0[i/2]); - h = t.to_extended(); - } - - h = h.mult_by_pow_2(4); - - for i in (0..64).filter(|x| x % 2 == 0) { - t = &h + &select_precomputed_point(e[i], &self.0[i/2]); - h = t.to_extended(); - } - - h + // delegate to a private function so that its documentation appears in internal docs + self.basepoint_mul(scalar) } } impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { type Output = ExtendedPoint; - /// Construct an `ExtendedPoint` by via this `Scalar` times - /// a the basepoint, `B` included in a precomputed `basepoint_table`. - /// - /// Precondition: this scalar must be reduced. + /// Construct an `ExtendedPoint` from a `Scalar` \\(a\\) by + /// computing the multiple \\(aB\\) of this basepoint \\(B\\). fn mul(self, basepoint_table: &'a EdwardsBasepointTable) -> ExtendedPoint { basepoint_table * &self } @@ -666,13 +680,20 @@ impl ExtendedPoint { /// Determine if this point is of small order. /// - /// The order of the group of points on the curve Ɛ is |Ɛ| = 8q. Thus, to - /// check if a point P is of small order, we multiply by 8 and then test - /// if the result is equal to the identity. + /// The order of the group of points on the curve \\(\mathcal E\\) + /// is \\(|\mathcal E| = 8\ell \\), so its structure is \\( \mathcal + /// E = \mathcal E[8] \times \mathcal E[\ell]\\). The torsion + /// subgroup \\( \mathcal E[8] \\) consists of eight points of small + /// order. (Technically all of \\(\mathcal E\\) is torsion, but we + /// use the word only to refer to the \\(\mathcal E[8]\\) part, not + /// the prime-order subgroup \\(\mathcal E[\ell]\\). + /// + /// For more information on cofactors and the group structure, see + /// the internal `curve25519-dalek` documentation on Ristretto. /// /// # Return /// - /// True if it is of small order; false otherwise. + /// True if `self` is of small order; false otherwise. pub fn is_small_order(&self) -> bool { self.mult_by_cofactor().is_identity() } @@ -756,13 +777,16 @@ pub mod vartime { } } - /// Given a vector of public scalars and a vector of (possibly secret) - /// points, compute `c_1 P_1 + ... + c_n P_n`. + /// Given an iterable of public scalars and an iterable of public + /// points, compute + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// $$ /// /// # Input /// - /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an - /// error to call this function with two vectors of different lengths. + /// A iterable of `Scalar`s and a iterable of `ExtendedPoints`. It is an + /// error to call this function with two iterators of different lengths. #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, @@ -794,8 +818,8 @@ pub mod vartime { r.to_extended() } - /// Given a point `A` and scalars `a` and `b`, compute the point - /// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)` + /// Given a point \\(A\\) and scalars \\(a\\) and \\(b\\), compute the point + /// \\(aA+bB\\), where \\(B\\) is the Ed25519 basepoint (i.e., \\(B = (x,4/5)\\) /// with x positive). #[cfg(feature="precomputed_tables")] pub fn double_scalar_mult_basepoint(a: &Scalar, diff --git a/src/field.rs b/src/field.rs index 86f9aad..4532820 100644 --- a/src/field.rs +++ b/src/field.rs @@ -8,15 +8,19 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! Field arithmetic for ℤ/(2²⁵⁵-19). +//! Field arithmetic modulo \\(p = 2\^{255} - 19\\). //! -//! Partially based on Adam Langley's curve25519-donna and (Golang) -//! ed25519 implementations, with other techniques inspired by Mike -//! Hamburg's code. +//! The `curve25519_dalek::field` module provides a type alias +//! `curve25519_dalek::field::FieldElement` to a field element type +//! defined in the `backend` module; either `FieldElement64` or +//! `FieldElement32`. //! -//! This module re-exports either the 32-bit or 64-bit implementation, -//! and implements functions that are generic with respect to the -//! basic operations, such as inverses and square roots. +//! Field operations defined in terms of machine +//! operations, such as field multiplication or squaring, are defined in +//! the backend implementation. +//! +//! Field operations defined in terms of other field operations, such as +//! field inversion or square roots, are defined here. use core::cmp::{Eq, PartialEq}; @@ -31,13 +35,21 @@ use backend; #[cfg(feature="radix_51")] pub use backend::u64::field::*; -/// A `FieldElement` represents an element of the field GF(2^255 - 19). +/// A `FieldElement` represents an element of the field +/// \\( \mathbb Z / (2\^{255} - 19)\\). +/// +/// The `FieldElement` type is an alias for one of the platform-specific +/// implementations. #[cfg(feature="radix_51")] pub type FieldElement = backend::u64::field::FieldElement64; #[cfg(not(feature="radix_51"))] pub use backend::u32::field::*; -/// A `FieldElement` represents an element of the field GF(2^255 - 19). +/// A `FieldElement` represents an element of the field +/// \\( \mathbb Z / (2\^{255} - 19)\\). +/// +/// The `FieldElement` type is an alias for one of the platform-specific +/// implementations. #[cfg(not(feature="radix_51"))] pub type FieldElement = backend::u32::field::FieldElement32; diff --git a/src/lib.rs b/src/lib.rs index 1476f3e..2756376 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,7 @@ //! # curve25519-dalek //! -//! **A Rust implementation of field and group operations on an Edwards curve -//! over GF(2^255 - 19).** +//! **A high-performance, pure-Rust implementation of group operations for Ristretto and Curve25519.** //! //! **[SPOILER ALERT]** The Twelfth Doctor's first encounter with the Daleks is //! in his second full episode, "Into the Dalek". A beleaguered ship of the diff --git a/src/montgomery.rs b/src/montgomery.rs index 94476e5..b427ff4 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -8,7 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! Montgomery arithmetic. +//! Group operations for Curve25519, in Montgomery form. //! //! Apart from the compressed point implementation //! (i.e. `CompressedMontgomeryU`), this module is a "clean room" implementation diff --git a/src/scalar.rs b/src/scalar.rs index 3b5fa51..8d12b4e 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -10,29 +10,7 @@ // - Henry de Valence // - Brian Smith -//! Arithmetic for scalar multiplication. -//! -//! Both the Ristretto group and the Ed25519 basepoint have prime order -//! \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). -//! -//! The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which -//! represents an element of \\(\mathbb Z / \ell\\). -//! -//! 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` with a specific bit-pattern (e.g., for -//! compatibility with X25519 "clamping"), use `Scalar::from_bits`. -//! -//! All arithmetic on `Scalars` is done modulo \\( \ell \\). +//! Arithmetic on scalars (integers mod the group order). use core::fmt::Debug; use core::ops::Neg; @@ -70,26 +48,46 @@ type UnpackedScalar = backend::u64::scalar::Scalar64; type UnpackedScalar = backend::u32::scalar::Scalar32; -/// The `Scalar` struct represents an element in ℤ/lℤ, where +/// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which +/// represents an element of \\(\mathbb Z / \ell\\). /// -/// l = 2^252 + 27742317777372353535851937790883648493 +/// Both the Ristretto group and the Ed25519 basepoint have prime order +/// \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). /// -/// is the order of the basepoint. The `Scalar` is stored as bytes. +/// 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. /// /// # 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. /// // 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], } impl Scalar { - /// Construct a `Scalar` by reducing a 256-bit integer modulo the group order. - pub fn from_bytes_mod_order(bytes: [u8;32]) -> Scalar { + /// Construct a `Scalar` by reducing a 256-bit little-endian integer + /// modulo the group order \\( \ell \\). + pub fn from_bytes_mod_order(bytes: [u8; 32]) -> Scalar { // Temporarily allow s_unreduced.bytes > 2^255 ... let s_unreduced = Scalar{bytes: bytes}; @@ -100,6 +98,12 @@ impl Scalar { s } + /// Construct a `Scalar` by reducing a 512-bit little-endian integer + /// modulo the group order \\( \ell \\). + pub fn from_bytes_mod_order_wide(input: &[u8; 64]) -> Scalar { + UnpackedScalar::from_bytes_wide(input).pack() + } + /// Attempt to construct a `Scalar` from a canonical byte representation. /// /// # Return @@ -323,7 +327,7 @@ impl Scalar { pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; rng.fill_bytes(&mut scalar_bytes); - Scalar::reduce_wide(&scalar_bytes) + Scalar::from_bytes_mod_order_wide(&scalar_bytes) } /// Hash a slice of bytes into a scalar. @@ -368,7 +372,7 @@ impl Scalar { // XXX this seems clumsy let mut output = [0u8; 64]; output.copy_from_slice(hash.result().as_slice()); - Scalar::reduce_wide(&output) + Scalar::from_bytes_mod_order_wide(&output) } /// Convert this `Scalar` to its underlying sequence of bytes. @@ -381,12 +385,12 @@ impl Scalar { &self.bytes } - /// Construct the additive identity + /// Construct the scalar \\( 0 \\). pub fn zero() -> Self { Scalar { bytes: [0u8; 32]} } - /// Construct the multiplicative identity + /// Construct the scalar \\( 1 \\). pub fn one() -> Self { Scalar { bytes: [ @@ -423,14 +427,17 @@ impl Scalar { /// Compute a width-5 "Non-Adjacent Form" of this scalar. /// - /// A width-`w` NAF of a positive integer `k` is an expression - /// `k = sum(k[i]*2^i for i in range(l))`, where each nonzero - /// coefficient `k[i]` is odd and bounded by `|k[i]| < 2^(w-1)`, - /// `k[l-1]` is nonzero, and at most one of any `w` consecutive + /// A width-\\(w\\) NAF of a positive integer \\(k\\) is an expression + /// $$ + /// k = \sum_{i=0}\^n k\_i 2\^i, + /// $$ + /// where each nonzero + /// coefficient \\(k\_i\\) is odd and bounded by \\(|k\_i| < 2\^{w-1}\\), + /// \\(k\_{n-1}\\) is nonzero, and at most one of any \\(w\\) consecutive /// coefficients is nonzero. (Hankerson, Menezes, Vanstone; def 3.32). /// /// Intuitively, this is like a binary expansion, except that we - /// allow some coefficients to grow up to `2^(w-1)` so that the + /// allow some coefficients to grow up to \\(2\^{w-1}\\) so that the /// nonzero coefficients are as sparse as possible. pub(crate) fn non_adjacent_form(&self) -> [i8; 256] { // Step 1: write out bits of the scalar @@ -468,15 +475,12 @@ impl Scalar { naf } - /// Write this scalar in radix 16, with coefficients in `[-8,8)`, - /// i.e., compute `a_i` such that - /// - /// a = a_0 + a_1*16^1 + ... + a_63*16^63, - /// - /// with `-8 ≤ a_i < 8` for `0 ≤ i < 63` and `-8 ≤ a_63 ≤ 8`. - /// - /// Precondition: self[31] <= 127. This is the case whenever - /// `self` is reduced. + /// Write this scalar in radix 16, with coefficients in \\([-8,8)\\), + /// i.e., compute \\(a\_i\\) such that + /// $$ + /// a = a\_0 + a\_1 16\^1 + \cdots + a_{63} 16\^{63}, + /// $$ + /// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_63 \leq 8\\). pub(crate) fn to_radix_16(&self) -> [i8; 64] { debug_assert!(self[31] <= 127); let mut output = [0i8; 64]; @@ -506,17 +510,12 @@ impl Scalar { output } - /// Unpack this `Scalar` to an `UnpackedScalar` + /// Unpack this `Scalar` to an `UnpackedScalar` for faster arithmetic. pub(crate) fn unpack(&self) -> UnpackedScalar { UnpackedScalar::from_bytes(&self.bytes) } - /// Compute `(a * b) + c` (mod l). - pub fn multiply_add(a: &Scalar, b: &Scalar, c: &Scalar) -> Scalar { - UnpackedScalar::add(&UnpackedScalar::mul(&a.unpack(), &b.unpack()), &c.unpack()).pack() - } - - /// Reduce this `Scalar` mod l. + /// Reduce this `Scalar` modulo \\(\ell\\). pub fn reduce(&self) -> Scalar { let x = self.unpack(); let xR = UnpackedScalar::mul_internal(&x, &constants::R); @@ -545,11 +544,6 @@ impl Scalar { pub fn is_canonical(&self) -> bool { *self == self.reduce() } - - /// Reduce a 512-bit little endian number mod l - pub fn reduce_wide(input: &[u8; 64]) -> Scalar { - UnpackedScalar::from_bytes_wide(input).pack() - } } impl UnpackedScalar { @@ -726,11 +720,11 @@ mod test { // also_a = (a mod l) tmp[0..32].copy_from_slice(&a_bytes[..]); - let also_a = Scalar::reduce_wide(&tmp); + let also_a = Scalar::from_bytes_mod_order_wide(&tmp); // also_b = (b mod l) tmp[0..32].copy_from_slice(&b_bytes[..]); - let also_b = Scalar::reduce_wide(&tmp); + let also_b = Scalar::from_bytes_mod_order_wide(&tmp); let expected_c = &a * &b; let also_expected_c = &also_a * &also_b; @@ -763,9 +757,7 @@ mod test { #[test] fn scalar_multiply_by_one() { - let one = Scalar::one(); - let zero = Scalar::zero(); - let test_scalar = Scalar::multiply_add(&X, &one, &zero); + let test_scalar = &X * &Scalar::one(); for i in 0..32 { assert!(test_scalar[i] == X[i]); } @@ -792,17 +784,9 @@ mod test { assert_eq!(should_be_X_times_Y, X_TIMES_Y); } - #[test] - fn scalar_multiply_add() { - let test_scalar = Scalar::multiply_add(&X, &Y, &Z); - for i in 0..32 { - assert!(test_scalar[i] == W[i]); - } - } - #[test] fn square() { - let expected = Scalar::multiply_add(&X, &X, &Scalar::zero()); + let expected = &X * &X; let actual = X.unpack().square().pack(); for i in 0..32 { assert!(expected[i] == actual[i]); @@ -816,7 +800,7 @@ mod test { } #[test] - fn reduce_wide() { + fn from_bytes_mod_order_wide() { let mut bignum = [0u8; 64]; // set bignum = x + 2^256x for i in 0..32 { @@ -833,7 +817,7 @@ mod test { 28, 82, 31, 197, 100, 165, 192, 8, ], }; - let test_red = Scalar::reduce_wide(&bignum); + let test_red = Scalar::from_bytes_mod_order_wide(&bignum); for i in 0..32 { assert!(test_red[i] == reduced[i]); } @@ -868,7 +852,7 @@ mod test { } #[test] - fn montgomery_reduce_matches_reduce_wide() { + fn montgomery_reduce_matches_from_bytes_mod_order_wide() { let mut bignum = [0u8; 64]; // set bignum = x + 2^256x @@ -886,7 +870,7 @@ mod test { 28, 82, 31, 197, 100, 165, 192, 8 ], }; - let reduced = Scalar::reduce_wide(&bignum); + let reduced = Scalar::from_bytes_mod_order_wide(&bignum); // The reduced scalar should match the expected assert_eq!(reduced.bytes, expected.bytes);