diff --git a/README.md b/README.md index 402924a..09077d3 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,91 @@ # curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://docs.rs/curve25519-dalek/badge.svg)](https://docs.rs/curve25519-dalek) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) -**A low-level cryptographic library for point, group, field, and scalar -operations on a curve isomorphic to the twisted Edwards curve defined by -x²+y² -= 1 - 121665/121666 x²y² over GF(2²⁵⁵ - 19).** +**A pure-Rust implementation of group operations on Ristretto and Curve25519.** + +`curve25519-dalek` is a library providing group operations on the Edwards and +Montgomery forms of Curve25519, and on the prime-order Ristretto group. + +`curve25519-dalek` is not intended to provide implementations of any particular +crypto protocol. Rather, implementations of those protocols (such as +[`x25519-dalek`][x25519-dalek] and [`ed25519-dalek`][ed25519-dalek]) should use +`curve25519-dalek` as a library. + +`curve25519-dalek` is intended to provide a clean and safe _mid-level_ API for use +implementing a wide range of ECC-based crypto protocols, such as key agreement, +signatures, anonymous credentials, rangeproofs, and zero-knowledge proof +systems. + +In particular, `curve25519-dalek` implements Ristretto, which constructs a +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 + +We do not yet consider this code to be production-ready. We intend to +stabilize a production-ready version `1.0` soon. + +# Documentation + +The semver-stable, public-facing `curve25519-dalek` API is documented +[here][docs-external]. In addition, the unstable internal implementation +details are documented [here][docs-internal]. + +The `curve25519-dalek` documentation requires a custom HTML header to include +KaTeX for math support. Unfortunately `cargo doc` does not currently support +this, but docs can be built using +```sh +make doc +make doc-internal +``` + +# Use + +To import `curve25519-dalek`, add the following to the dependencies section of +your project's `Cargo.toml`: +```toml +curve25519-dalek = "^0.14" +``` +Then import the crate as: +```rust,no_run +extern crate curve25519_dalek; +``` + +# Backends and Features + +Curve arithmetic is implemented using one of the following backends: + +* a `u32` backend using `u64` products; +* a `u64` backend using `u128` products, available using the `nightly` feature; +* an experimental AVX2 backend, available using the `yolocrypto` feature when + compiling for a target with `target_feature=+avx2`. + +By default, the benchmarks are not compiled without the `bench` +feature. Benchmarks can be run via: + +```sh +cargo bench --features="bench" # u32 backend +cargo bench --features="bench nightly" # u64 backend +cargo bench --features="bench nightly yolocrypto" # u64 or avx2 if available +``` + +The `yolocrypto` feature enables experimental features. The name `yolocrypto` +is meant to indicate that it is not considered production-ready, and we do not +consider `yolocrypto` features to be covered by semver guarantees. + +# Contributing + +Please see [CONTRIBUTING.md][contributing]. + +Patches and pull requests should be make against the `develop` +branch, **not** `master`. + +# About **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 "Combined @@ -23,65 +101,20 @@ universe's beauty, but also his deep hatred of the Daleks. Rusty destroys the other Daleks and departs the ship, determined to track down and bring an end to the Dalek race.* -Significant portions of this code are ported from [Adam Langley's -Golang ed25519 library](https://github.com/agl/ed25519), which is in +`curve25519-dalek` is authored by Isis Agora Lovecruft and Henry de Valence. + +Portions of this library were originally a port of [Adam Langley's +Golang ed25519 library](https://github.com/agl/ed25519), which was in turn a port of the reference `ref10` implementation. -## Warning +The fast `u32` and `u64` scalar arithmetic was implemented by Andrew Moon, and +the addition chain for scalar inversion was provided by Brian Smith. -This code has **not** yet received sufficient peer review by other qualified -cryptographers to be considered in any way, shape, or form, safe. Further, -this library does **not** provide high-level routines such as encryption and -decryption or signing and verification. Instead, it is a low-level library, -intended for other cryptographers who would like to implement their own -primitives using this curve. (For an example of how one would implement a -signature scheme using this library, see -[ed25519-dalek](https://github.com/dalek-cryptography/ed25519-dalek).) +The `no_std` support was contributed by Tony Arcieri. -**USE AT YOUR OWN RISK** +Thanks also to Ashley Hauck, Lucas Salibian, and Manish Goregaokar for their +contributions. -## Documentation - -Extensive documentation is available [here](https://docs.rs/curve25519-dalek). - -# Installation - -To install, add the following to the dependencies section of your project's -`Cargo.toml`: - -```toml -curve25519-dalek = "^0.14" -``` - -Then, in your library or executable source, add: - - extern crate curve25519_dalek; - -## Features - -On nightly Rust, using the `nightly` feature enables a radix-51 field -arithmetic implementation using `u128`s, which is approximately twice as -fast. It will also enable additional developer documentation when -compiling via `make doc-internal`. - -By default, the benchmarks are not compiled without the `bench` -feature. To run the benchmarks, do: - -```sh -cargo bench --features="bench" -``` - -## TODO - -We intend to stabilise the following before curve25519-dalek-1.0.0: - -* Implement hashing to a point on the curve (Elligator). -* Finish Ristretto documentation. - -## Contributing - -Please see -[CONTRIBUTING.md](https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md). - -Patches and pull requests should be make against the `develop` -branch, **not** `master`. +[ed25519-dalek]: https://github.com/dalek-cryptography/ed25519-dalek +[x25519-dalek]: https://github.com/dalek-cryptography/x25519-dalek +[contributing]: https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md diff --git a/build.rs b/build.rs index 6b6e652..1c8be62 100644 --- a/build.rs +++ b/build.rs @@ -80,11 +80,13 @@ use edwards::EdwardsBasepointTable; use curve_models::window::LookupTable; use curve_models::AffineNielsPoint; -/// Table containing precomputed multiples of the basepoint `B = (x,4/5)`. -/// -/// The table is defined so `constants::base[i][j-1] = j*(16^2i)*B`, -/// for `0 ≤ i < 32`, `1 ≤ j < 9`. -pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = {:?}; + +/// Table containing precomputed multiples of the Ed25519 basepoint \\\\(B = (x, 4/5)\\\\). +pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = ED25519_BASEPOINT_TABLE_INNER_DOC_HIDDEN; + +/// Inner constant, used to avoid filling the docs with precomputed points. +#[doc(hidden)] +pub const ED25519_BASEPOINT_TABLE_INNER_DOC_HIDDEN: EdwardsBasepointTable = {:?}; \n\n", &table).as_bytes()).unwrap(); // Now generate AFFINE_ODD_MULTIPLES_OF_BASEPOINT diff --git a/rustdoc-include-katex-header.html b/rustdoc-include-katex-header.html index 695c735..455a58e 100644 --- a/rustdoc-include-katex-header.html +++ b/rustdoc-include-katex-header.html @@ -6,5 +6,5 @@ document.addEventListener("DOMContentLoaded", function() { renderMathInElement(d diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0e2ce3e..847b1fe 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -8,8 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! This module contains "backends" that contain different -//! implementations of common code for different architectures. +//! Pluggable implementations for different architectures. //! //! The naming of the `u32` and `u64` modules is somewhat unfortunate, //! since these are also the names of primitive types. Since types have diff --git a/src/backend/u32/constants.rs b/src/backend/u32/constants.rs index 6c8fb8b..94702d7 100644 --- a/src/backend/u32/constants.rs +++ b/src/backend/u32/constants.rs @@ -81,24 +81,34 @@ pub(crate) const RR: Scalar32 = Scalar32([ 0x0b5f9d12, 0x1e141b17, 0x158d7f3d, 0 0x1972d781, 0x042feb7c, 0x1ceec73d, 0x1e184d1e, 0x0005046d ]); +/// The Ed25519 basepoint, as an `EdwardsPoint`. +/// +/// This is called `_POINT` to distinguish it from +/// `ED25519_BASEPOINT_TABLE`, which should be used for scalar +/// multiplication (it's much faster). +pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ + X: FieldElement32([52811034, 25909283, 16144682, 17082669, 27570973, 30858332, 40966398, 8378388, 20764389, 8758491]), + Y: FieldElement32([40265304, 26843545, 13421772, 20132659, 26843545, 6710886, 53687091, 13421772, 40265318, 26843545]), + Z: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + T: FieldElement32([28827043, 27438313, 39759291, 244362, 8635006, 11264893, 19351346, 13413597, 16611511, 27139452]), +}; + +/// The 8-torsion subgroup \\(\mathcal E [8]\\). +/// +/// In the case of Curve25519, it is cyclic; the \\(i\\)-th element of +/// the array is \\([i]P\\), where \\(P\\) is a point of order \\(8\\) +/// generating \\(\mathcal E[8]\\). +/// +/// Thus \\(\mathcal E[8]\\) is the points indexed by `0,2,4,6`, and +/// \\(\mathcal E[2]\\) is the points indexed by `0,4`. /// The Ed25519 basepoint has y = 4/5. This is called `_POINT` to /// distinguish it from `_TABLE`, which should be used for scalar /// multiplication (it's much faster). -pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ - X: FieldElement32([52811034, 25909283, 16144682, 17082669, 27570973, 30858332, 40966398, 8378388, 20764389, 8758491]), - Y: FieldElement32([40265304, 26843545, 13421772, 20132659, 26843545, 6710886, 53687091, 13421772, 40265318, 26843545]), - Z: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - T: FieldElement32([28827043, 27438313, 39759291, 244362, 8635006, 11264893, 19351346, 13413597, 16611511, 27139452]), -}; +pub const EIGHT_TORSION: [EdwardsPoint; 8] = EIGHT_TORSION_INNER_DOC_HIDDEN; -/// The 8-torsion subgroup Ɛ[8]. -/// -/// In the case of Curve25519, it is cyclic; the `i`th element of the -/// array is `i*P`, where `P` is a point of order 8 generating Ɛ[8]. -/// -/// Thus Ɛ[4] is the points indexed by 0,2,4,6 and Ɛ[2] is the points -/// indexed by 0,4. -pub const EIGHT_TORSION: [EdwardsPoint; 8] = [ +/// Inner item used to hide limb constants from cargo doc output. +#[doc(hidden)] +pub const EIGHT_TORSION_INNER_DOC_HIDDEN: [EdwardsPoint; 8] = [ EdwardsPoint{ X: FieldElement32([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), Y: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 046e1dc..254c81c 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -54,8 +54,10 @@ pub(crate) const R: Scalar64 = Scalar64([ 0x000f48bd6721e6ed, 0x0003bab5ac67e45a /// `RR` = (R^2) % L where R = 2^260 pub(crate) const RR: Scalar64 = Scalar64([ 0x0009d265e952d13b, 0x000d63c715bea69f, 0x0005be65cb687604, 0x0003dceec73d217f, 0x000009411b7c309a ]); -/// The Ed25519 basepoint has y = 4/5. This is called `_POINT` to -/// distinguish it from `_TABLE`, which should be used for scalar +/// The Ed25519 basepoint, as an `EdwardsPoint`. +/// +/// This is called `_POINT` to distinguish it from +/// `ED25519_BASEPOINT_TABLE`, which should be used for scalar /// multiplication (it's much faster). pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ X: FieldElement64([1738742601995546, 1146398526822698, 2070867633025821, 562264141797630, 587772402128613]), @@ -64,14 +66,19 @@ pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ T: FieldElement64([1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039]), }; -/// The 8-torsion subgroup Ɛ[8]. +/// The 8-torsion subgroup \\(\mathcal E [8]\\). /// -/// In the case of Curve25519, it is cyclic; the `i`th element of the -/// array is `i*P`, where `P` is a point of order 8 generating Ɛ[8]. +/// In the case of Curve25519, it is cyclic; the \\(i\\)-th element of +/// the array is \\([i]P\\), where \\(P\\) is a point of order \\(8\\) +/// generating \\(\mathcal E[8]\\). /// -/// Thus Ɛ[4] is the points indexed by 0,2,4,6 and Ɛ[2] is the points -/// indexed by 0,4. -pub const EIGHT_TORSION: [EdwardsPoint; 8] = [ +/// Thus \\(\mathcal E[8]\\) is the points indexed by `0,2,4,6`, and +/// \\(\mathcal E[2]\\) is the points indexed by `0,4`. +pub const EIGHT_TORSION: [EdwardsPoint; 8] = EIGHT_TORSION_INNER_DOC_HIDDEN; + +/// Inner item used to hide limb constants from cargo doc output. +#[doc(hidden)] +pub const EIGHT_TORSION_INNER_DOC_HIDDEN: [EdwardsPoint; 8] = [ EdwardsPoint { X: FieldElement64([0, 0, 0, 0, 0]), Y: FieldElement64([1, 0, 0, 0, 0]), diff --git a/src/constants.rs b/src/constants.rs index b4c4646..16edb4f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -8,7 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! This module contains various constants, such as the Ristretto and Ed25519 basepoints. +//! 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 @@ -38,30 +38,36 @@ pub use backend::u64::constants::*; #[cfg(not(feature="radix_51"))] pub use backend::u32::constants::*; -/// Basepoint has y = 4/5. +/// The Ed25519 basepoint, in `CompressedEdwardsY` format. /// -/// Generated with Sage: these are the bytes of 4/5 in 𝔽_p. The -/// sign bit is 0 since the basepoint has x chosen to be positive. -pub const BASE_CMPRSSD: CompressedEdwardsY = +/// This is the little-endian byte encoding of \\( 4/5 \pmod p \\), +/// which is the \\(y\\)-coordinate of the Ed25519 basepoint. +/// +/// The sign bit is 0 since the basepoint has \\(x\\) chosen to be positive. +pub const ED25519_BASEPOINT_COMPRESSED: CompressedEdwardsY = CompressedEdwardsY([0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66]); -/// The X25519 basepoint, in compressed Montgomery form. -pub const BASE_COMPRESSED_MONTGOMERY: CompressedMontgomeryU = +/// The X25519 basepoint, in `CompressedMontgomeryU` format. +pub const X25519_BASEPOINT_COMPRESSED: CompressedMontgomeryU = CompressedMontgomeryU([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); -/// The Ed25519 basepoint, as a `RistrettoPoint`. This is called `_POINT` to distinguish it from -/// `_TABLE`, which provides fast scalar multiplication. +/// The Ristretto basepoint, as a `RistrettoPoint`. +/// +/// This is called `_POINT` to distinguish it from `_TABLE`, which +/// provides fast scalar multiplication. pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT); -/// `BASEPOINT_ORDER` is the order of base point, i.e. `l = 2^252 + -/// 27742317777372353535851937790883648493`, in little-endian bytes. +/// `BASEPOINT_ORDER` is the order of the Ristretto group and of the Ed25519 basepoint, i.e., +/// $$ +/// \ell = 2^\{252\} + 27742317777372353535851937790883648493. +/// $$ pub const BASEPOINT_ORDER: Scalar = Scalar{ bytes: [ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, @@ -71,26 +77,6 @@ pub const BASEPOINT_ORDER: Scalar = Scalar{ ], }; -/// `BASEPOINT_ORDER_MINUS_1` is the order of base point minus one, i.e. `l-1`, in little-endian bytes. -pub const BASEPOINT_ORDER_MINUS_1: Scalar = Scalar{ - bytes: [ - 0xec, 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, - ], -}; - -/// `BASEPOINT_ORDER_MINUS_2` is the order of base point minus two, i.e. `l-2`, in little-endian bytes. -pub const BASEPOINT_ORDER_MINUS_2: Scalar = Scalar{ - bytes: [ - 0xeb, 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, - ], -}; - // Precomputed basepoint table is generated into a file by build.rs #[cfg(feature="precomputed_tables")] @@ -98,7 +84,8 @@ include!(concat!(env!("OUT_DIR"), "/basepoint_table.rs")); #[cfg(feature="precomputed_tables")] use ristretto::RistrettoBasepointTable; -/// The Ed25519 basepoint, as a RistrettoPoint + +/// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication. #[cfg(feature="precomputed_tables")] pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable = RistrettoBasepointTable(ED25519_BASEPOINT_TABLE); diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index bc89d16..f9b29d8 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -8,8 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! This module contains internal curve representations which are not part -//! of the public API. +//! Internal curve representations which are not part of the public API. //! //! # Curve representations //! diff --git a/src/edwards.rs b/src/edwards.rs index 4b130dc..8f95323 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -9,6 +9,77 @@ // - Henry de Valence //! Group operations for Curve25519, in Edwards form. +//! +//! ## Encoding and Decoding +//! +//! Encoding is done by converting to and from a `CompressedEdwardsY` +//! struct, which is a typed wrapper around `[u8; 32]`. +//! +//! ## Equality Testing +//! +//! The `EdwardsPoint` struct implements the `subtle::Equal` trait for +//! constant-time equality checking, and the Rust `Eq` trait for +//! variable-time equality checking. +//! +//! ## Cofactor-related functions +//! +//! 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 small \\(\mathcal E[8]\\) part, not +//! the large prime-order \\(\mathcal E[\ell]\\) part. +//! +//! To test if a point is in \\( \mathcal E[8] \\), use +//! `EdwardsPoint::is_small_order()`. +//! +//! To test if a point is in \\( \mathcal E[\ell] \\), use +//! `EdwardsPoint::is_torsion_free()`. +//! +//! To multiply by the cofactor, use `EdwardsPoint::mult_by_cofactor()`. +//! +//! To avoid dealing with cofactors entirely, consider using Ristretto. +//! +//! ## Scalars +//! +//! Scalars are represented by the `Scalar` struct. To construct a scalar with a specific bit +//! pattern, see `Scalar::from_bits()`. +//! +//! ## Scalar Multiplication +//! +//! Scalar multiplication on Edwards points is provided by: +//! +//! * the `*` operator between a `Scalar` and a `EdwardsPoint`, which +//! performs constant-time variable-base scalar multiplication; +//! +//! * the `*` operator between a `Scalar` and a +//! `EdwardsBasepointTable`, which performs constant-time fixed-base +//! scalar multiplication; +//! +//! * the `edwards::multiscalar_mult` function, which performs +//! constant-time variable-base multiscalar multiplication; +//! +//! * the `edwards::vartime::multiscalar_mult` function, which +//! performs variable-time variable-base multiscalar multiplication. +//! +//! ## Implementation +//! +//! The Edwards arithmetic is implemented using the “extended twisted +//! coordinates” of Hisil, Wong, Carter, and Dawson, and the +//! corresponding complete formulas. For more details, +//! see the `curve_models` submodule of the internal documentation. +//! +//! ## Validity Checking +//! +//! There is no function for checking whether a point is valid. +//! Instead, the `EdwardsPoint` struct is guaranteed to hold a valid +//! point on the curve. +//! +//! We use the Rust type system to make invalid points +//! unrepresentable: `EdwardsPoint` objects can only be created via +//! successful decompression of a compressed point, or else by +//! operations on other (valid) `EdwardsPoint`s. // We allow non snake_case names because coordinates in projective space are // traditionally denoted by the capitalisation of their respective @@ -690,14 +761,14 @@ impl EdwardsBasepointTable { } impl EdwardsPoint { - /// Multiply by the cofactor: compute `8 * self`. + /// Multiply by the cofactor: return \\([8]P\\). pub fn mult_by_cofactor(&self) -> EdwardsPoint { self.mult_by_pow_2(3) } - /// Compute `2^k * self` by successive doublings. - /// Requires `k > 0`. + /// Compute \\([2\^k] P \\) by successive doublings. Requires \\( k > 0 \\). pub(crate) fn mult_by_pow_2(&self, k: u32) -> EdwardsPoint { + debug_assert!( k > 0 ); let mut r: CompletedPoint; let mut s = self.to_projective(); for _ in 0..(k-1) { @@ -709,22 +780,59 @@ impl EdwardsPoint { /// Determine if this point is of small order. /// - /// 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]\\). + /// # Return /// - /// For more information on cofactors and the group structure, see - /// the internal `curve25519-dalek` documentation on Ristretto. + /// * `true` if `self` is in the torsion subgroup \\( \mathcal E[8] \\); + /// * `false` if `self` is not in the torsion subgroup \\( \mathcal E[8] \\). + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::constants; + /// + /// // Generator of the prime-order subgroup + /// let P = constants::ED25519_BASEPOINT_POINT; + /// // Generator of the torsion subgroup + /// let Q = constants::EIGHT_TORSION[1]; + /// + /// // P has large order + /// assert_eq!(P.is_small_order(), false); + /// + /// // Q has small order + /// assert_eq!(Q.is_small_order(), true); + /// ``` + pub fn is_small_order(&self) -> bool { + self.mult_by_cofactor().is_identity() + } + + /// Determine if this point is “torsion-free”, i.e., is contained in + /// the prime-order subgroup. /// /// # Return /// - /// True if `self` is of small order; false otherwise. - pub fn is_small_order(&self) -> bool { - self.mult_by_cofactor().is_identity() + /// * `true` if `self` has zero torsion component and is in the + /// prime-order subgroup; + /// * `false` if `self` has a nonzero torsion component and is not + /// in the prime-order subgroup. + /// + /// # Example + /// + /// ``` + /// use curve25519_dalek::constants; + /// + /// // Generator of the prime-order subgroup + /// let P = constants::ED25519_BASEPOINT_POINT; + /// // Generator of the torsion subgroup + /// let Q = constants::EIGHT_TORSION[1]; + /// + /// // P is torsion-free + /// assert_eq!(P.is_torsion_free(), true); + /// + /// // P + Q is not torsion-free + /// assert_eq!((P+Q).is_torsion_free(), false); + /// ``` + pub fn is_torsion_free(&self) -> bool { + (self * &constants::BASEPOINT_ORDER).is_identity() } } @@ -989,18 +1097,18 @@ mod test { #[test] fn basepoint_decompression_compression() { let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES); - let bp = constants::BASE_CMPRSSD.decompress().unwrap(); + let bp = constants::ED25519_BASEPOINT_COMPRESSED.decompress().unwrap(); assert!(bp.is_valid()); // Check that decompression actually gives the correct X coordinate assert_eq!(base_X, bp.X); - assert_eq!(bp.compress(), constants::BASE_CMPRSSD); + assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED); } /// Test sign handling in decompression #[test] fn decompression_sign_handling() { // Manually set the high bit of the last byte to flip the sign - let mut minus_basepoint_bytes = constants::BASE_CMPRSSD.as_bytes().clone(); + let mut minus_basepoint_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes().clone(); minus_basepoint_bytes[31] |= 1 << 7; let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes) .decompress().unwrap(); @@ -1018,7 +1126,7 @@ mod test { fn basepoint_mult_one_vs_basepoint() { let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one(); let compressed = bp.compress(); - assert_eq!(compressed, constants::BASE_CMPRSSD); + assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED); } /// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint. @@ -1026,7 +1134,7 @@ mod test { #[cfg(feature="precomputed_tables")] fn basepoint_table_basepoint_function_correct() { let bp = constants::ED25519_BASEPOINT_TABLE.basepoint(); - assert_eq!(bp.compress(), constants::BASE_CMPRSSD); + assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED); } /// Test `impl Add for EdwardsPoint` @@ -1139,7 +1247,7 @@ mod test { fn basepoint_projective_extended_round_trip() { assert_eq!(constants::ED25519_BASEPOINT_POINT .to_projective().to_extended().compress(), - constants::BASE_CMPRSSD); + constants::ED25519_BASEPOINT_COMPRESSED); } /// Test computing 16*basepoint vs mult_by_pow_2(4) @@ -1262,7 +1370,7 @@ mod test { fn serde_cbor_basepoint_roundtrip() { let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap(); let parsed: EdwardsPoint = serde_cbor::from_slice(&output).unwrap(); - assert_eq!(parsed.compress(), constants::BASE_CMPRSSD); + assert_eq!(parsed.compress(), constants::ED25519_BASEPOINT_COMPRESSED); } #[test] @@ -1291,7 +1399,7 @@ mod bench { #[bench] fn edwards_decompress(b: &mut Bencher) { - let B = &constants::BASE_CMPRSSD; + let B = &constants::ED25519_BASEPOINT_COMPRESSED; b.iter(|| B.decompress().unwrap()); } diff --git a/src/lib.rs b/src/lib.rs index 0baf368..7df436c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,29 +9,22 @@ // - Henry de Valence #![cfg_attr(not(feature = "std"), no_std)] + #![cfg_attr(feature = "alloc", feature(alloc))] -#![cfg_attr(feature = "nightly", feature(i128_type))] -#![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(feature = "bench", feature(test))] -#![deny(missing_docs)] // refuse to compile if documentation is missing +#![cfg_attr(feature = "nightly", feature(i128_type))] +#![cfg_attr(feature = "nightly", feature(cfg_target_feature))] +#![cfg_attr(feature = "nightly", feature(external_doc))] -//! # curve25519-dalek -//! -//! **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 -//! "Combined Galactic Resistance" has discovered a broken Dalek that has -//! turned "good", desiring to kill all other Daleks. The Doctor, Clara and a -//! team of soldiers are miniaturized and enter the Dalek, which the Doctor -//! names Rusty. They repair the damage, but accidentally restore it to its -//! original nature, causing it to go on the rampage and alert the Dalek fleet -//! to the whereabouts of the rebel ship. However, the Doctor manages to -//! return Rusty to its previous state by linking his mind with the Dalek's: -//! Rusty shares the Doctor's view of the universe's beauty, but also his deep -//! hatred of the Daleks. Rusty destroys the other Daleks and departs the -//! ship, determined to track down and bring an end to the Dalek race. +// Refuse to compile if documentation is missing, but only on nightly. +// +// This means that missing docs will still fail CI, but means we can use +// README.md as the crate documentation. +#![cfg_attr(feature = "nightly", deny(missing_docs))] + +#![cfg_attr(feature = "nightly", doc(include = "../README.md"))] +#![doc(html_logo_url = "https://user-images.githubusercontent.com/797/34898472-83686016-f7f3-11e7-967b-24b2aadd623a.png")] //------------------------------------------------------------------------ // External dependencies: diff --git a/src/montgomery.rs b/src/montgomery.rs index a71e8a8..c7406ef 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -395,7 +395,7 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar { #[cfg(test)] mod test { - use constants::BASE_COMPRESSED_MONTGOMERY; + use constants::X25519_BASEPOINT_COMPRESSED; use traits::Identity; use super::*; @@ -405,14 +405,14 @@ mod test { #[test] fn basepoint_to_montgomery() { assert_eq!(constants::ED25519_BASEPOINT_POINT.to_montgomery().compress(), - BASE_COMPRESSED_MONTGOMERY); + X25519_BASEPOINT_COMPRESSED); } /// Test Montgomery conversion against the X25519 basepoint. #[test] fn basepoint_from_montgomery() { - assert_eq!(BASE_COMPRESSED_MONTGOMERY, - constants::BASE_CMPRSSD.decompress().unwrap().to_montgomery().compress()); + assert_eq!(X25519_BASEPOINT_COMPRESSED, + constants::ED25519_BASEPOINT_COMPRESSED.decompress().unwrap().to_montgomery().compress()); } /// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660. @@ -438,8 +438,8 @@ mod test { #[test] fn projective_to_affine_roundtrips() { - assert_eq!(BASE_COMPRESSED_MONTGOMERY.decompress().compress(), - BASE_COMPRESSED_MONTGOMERY); + assert_eq!(X25519_BASEPOINT_COMPRESSED.decompress().compress(), + X25519_BASEPOINT_COMPRESSED); } @@ -483,7 +483,7 @@ mod test { #[test] fn ladder_basepoint_times_two_matches_double() { let two: Scalar = Scalar::from_u64(2u64); - let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress() * &two; + let result: MontgomeryPoint = &X25519_BASEPOINT_COMPRESSED.decompress() * &two; let expected: EdwardsPoint = constants::ED25519_BASEPOINT_POINT.double(); assert_eq!(result.compress(), expected.to_montgomery().compress()); @@ -495,7 +495,7 @@ mod test { mod bench { use rand::OsRng; use constants::ED25519_BASEPOINT_TABLE; - use constants::BASE_COMPRESSED_MONTGOMERY; + use constants::X25519_BASEPOINT_COMPRESSED; use test::Bencher; use super::*; @@ -512,12 +512,12 @@ mod bench { #[bench] fn montgomery_decompress(b: &mut Bencher) { - b.iter(| | BASE_COMPRESSED_MONTGOMERY.decompress()); + b.iter(| | X25519_BASEPOINT_COMPRESSED.decompress()); } #[bench] fn montgomery_compress(b: &mut Bencher) { - let p: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress(); + let p: MontgomeryPoint = X25519_BASEPOINT_COMPRESSED.decompress(); b.iter(| | p.compress()); } diff --git a/src/ristretto.rs b/src/ristretto.rs index 67f183a..a7564be 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -8,37 +8,101 @@ // - Isis Agora Lovecruft // - Henry de Valence +// We allow non snake_case names because coordinates in projective space are +// traditionally denoted by the capitalisation of their respective +// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my +// affine and projective cakes and eat both of them too. +#![allow(non_snake_case)] + //! An implementation of Ristretto, which provides a prime-order group. //! -//! Ristretto is a modification of Mike Hamburg's [Decaf -//! cofactor-eliminating point-compression -//! scheme](https://eprint.iacr.org/2015/673.pdf) to work on top of the -//! Curve25519 group. +//! # The Ristretto Group //! -//! Below are some notes on Ristretto, which are *NOT* a full writeup and which may have errors. -//! -//! # Notes on Ristretto -//! -//! ## Decaf -//! -//! The introduction of the Decaf paper, [_Decaf: Eliminating cofactors -//! through point compression_](https://eprint.iacr.org/2015/673.pdf) -//! notes that while most cryptographic systems require a group of prime -//! order, most concrete implementations using elliptic curve groups -//! fall short -- they either provide a group of prime order, but with -//! incomplete or variable-time addition formulae (for instance, most -//! Weierstrass models), or else they provide a fast and safe -//! implementation of a group whose order is not quite a prime \\(q\\), -//! but \\(hq\\) for a small cofactor \\(h\\) (for instance, Edwards -//! curves, which have cofactor at least \\(4\\)). +//! Ristretto is a modification of Mike Hamburg's Decaf scheme to work +//! with Curve25519. The introduction of the Decaf paper, [_Decaf: +//! Eliminating cofactors through point +//! compression_](https://eprint.iacr.org/2015/673.pdf), notes that while +//! most cryptographic systems require a group of prime order, most +//! concrete implementations using elliptic curve groups fall short -- +//! they either provide a group of prime order, but with incomplete or +//! variable-time addition formulae (for instance, most Weierstrass +//! models), or else they provide a fast and safe implementation of a +//! group whose order is not quite a prime \\(q\\), but \\(hq\\) for a +//! small cofactor \\(h\\) (for instance, Edwards curves, which have +//! cofactor at least \\(4\\)). //! //! This abstraction mismatch requires ad-hoc protocol modifications to //! ensure security; these modifications require careful analysis and -//! are a recurring source of vulnerabilities. +//! are a recurring source of [vulnerabilities][cryptonote] and [design +//! complications][ed25519_hkd]. +//! +//! Instead, Ristretto uses a quotient group to implement a prime-order +//! group using a non-prime-order curve. More details are described in +//! the *Implementation* section below. Ristretto points are provided +//! in `curve25519-dalek` by the `RistrettoPoint` struct. +//! +//! ## Encoding and Decoding +//! +//! Encoding is done by converting to and from a `CompressedRistretto` +//! struct, which is a typed wrapper around `[u8; 32]`. +//! +//! The encoding is not batchable, but it is possible to +//! double-and-encode in a batch using +//! `RistrettoPoint::double_and_compress_batch`. +//! +//! ## Equality Testing +//! +//! Testing equality of points on an Edwards curve in projective +//! coordinates requires an expensive inversion. By contrast, equality +//! checking in the Ristretto group can be done in projective +//! coordinates without requiring an inversion, so it is much faster. +//! +//! The `RistrettoPoint` struct implements the `subtle::Equal` trait for +//! constant-time equality checking, and the Rust `Eq` trait for +//! variable-time equality checking. +//! +//! ## Scalars +//! +//! Scalars are represented by the `Scalar` struct. Each scalar has a +//! canonical representative mod the group order; see +//! `Scalar::from_canonical_bytes()` and `Scalar::is_canonical()`. +//! +//! ## Scalar Multiplication +//! +//! Scalar multiplication on Ristretto points is provided by: +//! +//! * the `*` operator between a `Scalar` and a `RistrettoPoint`, which +//! performs constant-time variable-base scalar multiplication; +//! +//! * the `*` operator between a `Scalar` and a +//! `RistrettoBasepointTable`, which performs constant-time fixed-base +//! scalar multiplication; +//! +//! * the `ristretto::multiscalar_mult` function, which performs +//! constant-time variable-base multiscalar multiplication; +//! +//! * the `ristretto::vartime::multiscalar_mult` function, which +//! performs variable-time variable-base multiscalar multiplication. +//! +//! ## Random Points and Hashing to Ristretto +//! +//! The Ristretto group comes equipped with an Elligator map. This is +//! used to implement +//! +//! * `RistrettoPoint::random()`, which generates random points from an +//! RNG; +//! +//! * `RistrettoPoint::from_hash()` and +//! `RistrettoPoint::hash_from_bytes()`, which perform hashing to the +//! group. +//! +//! The Elligator map itself is not currently exposed. +//! +//! ## Implementation //! //! The Decaf suggestion is to use a quotient group, such as \\(\mathcal //! E / \mathcal E[4]\\) or \\(2 \mathcal E / \mathcal E[2] \\), to -//! implement a prime-order group. +//! implement a prime-order group using a non-prime-order curve. //! //! This requires only changing //! @@ -61,8 +125,25 @@ //! explains the name: Decaf is named "after the procedure which divides //! the effect of coffee by \\(4\\)". However, Curve25519 has a //! cofactor of \\(8\\). To eliminate its cofactor, we tweak Decaf to -//! restrict further. This gives the -//! [Ristretto](https://en.wikipedia.org/wiki/Ristretto) encoding. +//! restrict further. This [additional restriction][ristretto_coffee] +//! gives the _Ristretto_ encoding. +//! +//! Notes on the details of the encoding can be found in the +//! `ristretto::notes` submodule of the internal `curve25519-dalek` +//! documentation. +//! +//! [cryptonote]: +//! https://moderncrypto.org/mail-archive/curves/2017/000898.html +//! [ed25519_hkd]: +//! https://moderncrypto.org/mail-archive/curves/2017/000858.html +//! [ristretto_coffee]: +//! https://en.wikipedia.org/wiki/Ristretto + +mod notes { + +//! Below are some notes on Ristretto, which are *NOT* a full writeup and which may have errors. +//! +//! # Notes on Ristretto //! //! ## The Jacobi Quartic //! @@ -379,11 +460,7 @@ //! //! ## ??? -// We allow non snake_case names because coordinates in projective space are -// traditionally denoted by the capitalisation of their respective -// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my -// affine and projective cakes and eat both of them too. -#![allow(non_snake_case)] +} use core::fmt::Debug; @@ -419,29 +496,31 @@ use traits::Identity; // Compressed points // ------------------------------------------------------------------------ -/// A point serialized using Mike Hamburg's Ristretto scheme. +/// A Ristretto point, in compressed wire format. /// -/// XXX think about how this API should work +/// The Ristretto encoding is canonical, so two points are equal if and +/// only if their encodings are equal. #[derive(Copy, Clone, Eq, PartialEq)] pub struct CompressedRistretto(pub [u8; 32]); -/// The result of compressing a `RistrettoPoint`. impl CompressedRistretto { - /// Convert this `CompressedRistretto` to its underlying array of bytes. + /// Copy the bytes of this `CompressedRistretto`. pub fn to_bytes(&self) -> [u8; 32] { self.0 } /// View this `CompressedRistretto` as an array of bytes. - pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { + pub fn as_bytes(&self) -> &[u8; 32] { &self.0 } /// Attempt to decompress to an `RistrettoPoint`. /// - /// This function executes in constant time for all valid inputs. - /// Inputs which do not decode to a RistrettoPoint may return - /// early. + /// # Return + /// + /// - `Some(RistrettoPoint)` if `self` was the canonical encoding of a point; + /// + /// - `None` if `self` was not the canonical encoding of a point. pub fn decompress(&self) -> Option { // Step 1. Check s for validity: // 1.a) s must be 32 bytes (we get this from the type system) @@ -562,75 +641,17 @@ impl<'de> Deserialize<'de> for RistrettoPoint { /// prime-order group as a quotient group of a subgroup of (the /// Edwards form of) Curve25519. /// -/// Internally, a `RistrettoPoint` is a wrapper type around -/// `EdwardsPoint`, with custom equality, compression, and -/// decompression routines to account for the quotient. +/// Internally, a `RistrettoPoint` is implemented as a wrapper type +/// around `EdwardsPoint`, with custom equality, compression, and +/// decompression routines to account for the quotient. This means that +/// operations on `RistrettoPoint`s are exactly as fast as operations on +/// `EdwardsPoint`s. +/// #[derive(Copy, Clone)] pub struct RistrettoPoint(pub(crate) EdwardsPoint); impl RistrettoPoint { - /// Compress in Ristretto format. - /// - /// # Implementation Notes - /// - /// The Ristretto encoding is as follows, on input in affine coordinates `(x,y)`: - /// - /// 1. If `xy` is negative or `x = 0`, "rotate" the point by - /// setting `(x,y) = (iy, ix)`. - /// 2. If `x` is negative, set `(x,y) = (-x, -y)`. - /// 3. Compute `s = +sqrt((1-y)/(1+y))`. - /// 4. Return the little-endian 32-byte encoding of `s`. - /// - /// However, our input is in extended twisted Edwards coordinates - /// `(X:Y:Z:T)` with `x = X/Z`, `y = Y/Z`, `xy = T/Z` (see the - /// module-level documentation on curve representations for more - /// details). Since inversions are expensive, we'd like to be - /// able to do this whole computation with only one inversion. - /// - /// Since `y = Y/Z`, in extended coordinates the formula for `s` becomes - /// - ///     s = sqrt((1 - Y/Z)/(1 + Y/Z)) = sqrt((Z-Y)/(Z+Y)). (1) - /// - /// We can compute this as - /// - ///     s = (Z - Y) / sqrt((Z-Y)(Z+Y)). (1) - /// - /// The denominator is - /// - ///     invsqrt((Z-Y)(Z+Y)) = invsqrt(Z² - Y²). (1) - /// - /// Write the input point as `(X₀:Y₀:Z₀:T₀)`. The rotation in - /// step 1 of the encoding procedure replaces `(X₀:Y₀:Z₀:T₀)` by - /// `(iY₀:iX₀:Z₀:-T₀)`. We therefore wish to relate the - /// computation of - /// - ///     invsqrt(Z² - Y²) = invsqrt(Z₀² - Y₀²) [non-rotated case] - /// - /// with the computation of - /// - ///     invsqrt(Z² - Y²) = invsqrt(Z₀² + X₀²). [rotated case] - /// - /// Recall the curve equation (in the 𝗣² model): - /// - ///     (-X² + Y²)Z² = Z⁴ + dX²Y². (1) - /// - /// This means that, for any point `(X:Y:Z:T)` in extended coordinates, we have - /// - ///     -dX²Y² = Z⁴ + Z²X² - Z²Y², (2) - /// - /// so that - /// - ///     (-1-d)X²Y² = Z⁴ + Z²X² - Z²Y² - X²Y², (3) - /// - /// and hence - /// - ///     (-1-d)X²Y² = (Z² - Y²)(Z² + X²). (4) - /// - /// Taking inverse square roots gives - /// - ///     invsqrt(Z² + X²) = invsqrt(-1-d) sqrt((Z² - Y²)/(X²Y²)). (4) - /// - /// + /// Compress this point using the Ristretto encoding. pub fn compress(&self) -> CompressedRistretto { let mut X = self.0.X; let mut Y = self.0.Y; @@ -666,7 +687,37 @@ impl RistrettoPoint { CompressedRistretto(s.to_bytes()) } - /// Double-and-compress a batch of points. + /// Double-and-compress a batch of points. The Ristretto encoding + /// is not batchable, since it requires an inverse square root. + /// + /// However, given input points \\( P\_1, \ldots, P\_n, \\) + /// it is possible to compute the encodings of their doubles \\( + /// \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; + /// extern crate rand; + /// use rand::OsRng; + /// + /// # // Need fn main() here in comment so the doctest compiles + /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests + /// # fn main() { + /// let mut rng = OsRng::new().unwrap(); + /// let points: Vec = + /// (0..32).map(|_| RistrettoPoint::random(&mut rng)).collect(); + /// + /// let compressed = RistrettoPoint::double_and_compress_batch(&points); + /// + /// for (P, P2_compressed) in points.iter().zip(compressed.iter()) { + /// assert_eq!(*P2_compressed, (P + P).compress()); + /// } + /// # } + /// ``` #[cfg(any(feature = "alloc", feature = "std"))] pub fn double_and_compress_batch<'a, I>(points: I) -> Vec where I: IntoIterator @@ -762,7 +813,7 @@ impl RistrettoPoint { /// /// This method is not public because it's just used for hashing /// to a point -- proper elligator support is deferred for now. - pub fn elligator_ristretto_flavour(r_0: &FieldElement) -> RistrettoPoint { + pub(crate) fn elligator_ristretto_flavour(r_0: &FieldElement) -> RistrettoPoint { let (i, d) = (&constants::SQRT_M1, &constants::EDWARDS_D); let one = FieldElement::one(); @@ -781,7 +832,7 @@ impl RistrettoPoint { // s = sqrt(N/D) if N/D is square s.conditional_assign(&maybe_s, N_over_D_is_square); - // XXX how do we reuse the computation of sqrt(N/D) to find sqrt(rN/D) ? + // XXX how exactly do we reuse the computation of sqrt(N/D) to find sqrt(rN/D) ? let (rN_over_D_is_square, mut maybe_s) = FieldElement::sqrt_ratio(&(&r*&N), &D); maybe_s.negate(); @@ -999,15 +1050,18 @@ define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint); -/// 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 /// -/// An iterable of `Scalar`s and a iterable of `DecafPoints`. It is an +/// An iterable of `Scalar`s and a iterable of `RistrettoPoints`. 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) -> RistrettoPoint @@ -1018,9 +1072,10 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points)) } -/// Precomputation +/// A precomputed table of multiples of a basepoint, used to accelerate +/// scalar multiplication. #[derive(Clone)] -pub struct RistrettoBasepointTable(pub EdwardsBasepointTable); +pub struct RistrettoBasepointTable(pub(crate) EdwardsBasepointTable); impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable { type Output = RistrettoPoint; @@ -1114,15 +1169,16 @@ pub mod vartime { //! Variable-time operations on ristretto points, useful for non-secret data. use super::*; - /// Given a vector of public scalars and a vector of (possibly secret) + /// Given an iterable of public scalars and an iterable of public /// points, compute - /// - /// c_1 P_1 + ... + c_n P_n. + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// $$ /// /// # Input /// - /// A vector of `Scalar`s and a vector of `RistrettoPoints`. It is an - /// error to call this function with two vectors of different lengths. + /// A iterable of `Scalar`s and a iterable of `RistrettoPoints`. 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) -> RistrettoPoint where I: IntoIterator, diff --git a/src/scalar.rs b/src/scalar.rs index 64a59bc..72b2b01 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -790,12 +790,6 @@ mod test { assert_eq!(should_be_two, two); } - #[test] - fn impl_sub() { - let should_be_one = &constants::BASEPOINT_ORDER - &constants::BASEPOINT_ORDER_MINUS_1; - assert_eq!(should_be_one, Scalar::one()); - } - #[allow(non_snake_case)] #[test] fn impl_mul() {