Merge remote-tracking branch 'hdevalence/feature/refactor-docs' into develop

This commit is contained in:
Isis Lovecruft 2018-01-31 02:11:46 +00:00
commit 5f1320bb3b
Failed to extract signature
13 changed files with 486 additions and 298 deletions

155
README.md
View file

@ -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) # 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)
<img <img
width="50%" width="33%"
align="right" align="right"
src="https://user-images.githubusercontent.com/797/34898472-83686016-f7f3-11e7-967b-24b2aadd623a.png"/> src="https://user-images.githubusercontent.com/797/34898472-83686016-f7f3-11e7-967b-24b2aadd623a.png"/>
**A low-level cryptographic library for point, group, field, and scalar **A pure-Rust implementation of group operations on Ristretto and Curve25519.**
operations on a curve isomorphic to the twisted Edwards curve defined by -x²+y²
= 1 - 121665/121666 x²y² over GF(2²⁵⁵ - 19).** `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 **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 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 other Daleks and departs the ship, determined to track down and bring an end
to the Dalek race.* to the Dalek race.*
Significant portions of this code are ported from [Adam Langley's `curve25519-dalek` is authored by Isis Agora Lovecruft and Henry de Valence.
Golang ed25519 library](https://github.com/agl/ed25519), which is in
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. 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 The `no_std` support was contributed by Tony Arcieri.
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).)
**USE AT YOUR OWN RISK** Thanks also to Ashley Hauck, Lucas Salibian, and Manish Goregaokar for their
contributions.
## Documentation [ed25519-dalek]: https://github.com/dalek-cryptography/ed25519-dalek
[x25519-dalek]: https://github.com/dalek-cryptography/x25519-dalek
Extensive documentation is available [here](https://docs.rs/curve25519-dalek). [contributing]: https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md
# 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`.

View file

@ -80,11 +80,13 @@ use edwards::EdwardsBasepointTable;
use curve_models::window::LookupTable; use curve_models::window::LookupTable;
use curve_models::AffineNielsPoint; use curve_models::AffineNielsPoint;
/// Table containing precomputed multiples of the basepoint `B = (x,4/5)`.
/// /// Table containing precomputed multiples of the Ed25519 basepoint \\\\(B = (x, 4/5)\\\\).
/// The table is defined so `constants::base[i][j-1] = j*(16^2i)*B`, pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = ED25519_BASEPOINT_TABLE_INNER_DOC_HIDDEN;
/// for `0 ≤ i < 32`, `1 ≤ j < 9`.
pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = {:?}; /// 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(); \n\n", &table).as_bytes()).unwrap();
// Now generate AFFINE_ODD_MULTIPLES_OF_BASEPOINT // Now generate AFFINE_ODD_MULTIPLES_OF_BASEPOINT

View file

@ -6,5 +6,5 @@ document.addEventListener("DOMContentLoaded", function() { renderMathInElement(d
</script> </script>
<style> <style>
.katex { font-size: 1em !important; } .katex { font-size: 1em !important; }
.docblock code, .dockblock-short code { font-size: 0.85em !important; } pre.rust, .docblock code, .docblock-short code { font-size: 0.85em !important; }
</style> </style>

View file

@ -8,8 +8,7 @@
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
//! This module contains "backends" that contain different //! Pluggable implementations for different architectures.
//! implementations of common code for different architectures.
//! //!
//! The naming of the `u32` and `u64` modules is somewhat unfortunate, //! The naming of the `u32` and `u64` modules is somewhat unfortunate,
//! since these are also the names of primitive types. Since types have //! since these are also the names of primitive types. Since types have

View file

@ -81,24 +81,34 @@ pub(crate) const RR: Scalar32 = Scalar32([ 0x0b5f9d12, 0x1e141b17, 0x158d7f3d, 0
0x1972d781, 0x042feb7c, 0x1ceec73d, 0x1e184d1e, 0x1972d781, 0x042feb7c, 0x1ceec73d, 0x1e184d1e,
0x0005046d ]); 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 /// The Ed25519 basepoint has y = 4/5. This is called `_POINT` to
/// distinguish it from `_TABLE`, which should be used for scalar /// distinguish it from `_TABLE`, which should be used for scalar
/// multiplication (it's much faster). /// multiplication (it's much faster).
pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ pub const EIGHT_TORSION: [EdwardsPoint; 8] = EIGHT_TORSION_INNER_DOC_HIDDEN;
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 Ɛ[8]. /// Inner item used to hide limb constants from cargo doc output.
/// #[doc(hidden)]
/// In the case of Curve25519, it is cyclic; the `i`th element of the pub const EIGHT_TORSION_INNER_DOC_HIDDEN: [EdwardsPoint; 8] = [
/// 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] = [
EdwardsPoint{ EdwardsPoint{
X: FieldElement32([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), X: FieldElement32([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
Y: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), Y: FieldElement32([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]),

View file

@ -54,8 +54,10 @@ pub(crate) const R: Scalar64 = Scalar64([ 0x000f48bd6721e6ed, 0x0003bab5ac67e45a
/// `RR` = (R^2) % L where R = 2^260 /// `RR` = (R^2) % L where R = 2^260
pub(crate) const RR: Scalar64 = Scalar64([ 0x0009d265e952d13b, 0x000d63c715bea69f, 0x0005be65cb687604, 0x0003dceec73d217f, 0x000009411b7c309a ]); pub(crate) const RR: Scalar64 = Scalar64([ 0x0009d265e952d13b, 0x000d63c715bea69f, 0x0005be65cb687604, 0x0003dceec73d217f, 0x000009411b7c309a ]);
/// The Ed25519 basepoint has y = 4/5. This is called `_POINT` to /// The Ed25519 basepoint, as an `EdwardsPoint`.
/// distinguish it from `_TABLE`, which should be used for scalar ///
/// This is called `_POINT` to distinguish it from
/// `ED25519_BASEPOINT_TABLE`, which should be used for scalar
/// multiplication (it's much faster). /// multiplication (it's much faster).
pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{ pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint{
X: FieldElement64([1738742601995546, 1146398526822698, 2070867633025821, 562264141797630, 587772402128613]), 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]), 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 /// In the case of Curve25519, it is cyclic; the \\(i\\)-th element of
/// array is `i*P`, where `P` is a point of order 8 generating Ɛ[8]. /// 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 /// Thus \\(\mathcal E[8]\\) is the points indexed by `0,2,4,6`, and
/// indexed by 0,4. /// \\(\mathcal E[2]\\) is the points indexed by `0,4`.
pub const EIGHT_TORSION: [EdwardsPoint; 8] = [ 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 { EdwardsPoint {
X: FieldElement64([0, 0, 0, 0, 0]), X: FieldElement64([0, 0, 0, 0, 0]),
Y: FieldElement64([1, 0, 0, 0, 0]), Y: FieldElement64([1, 0, 0, 0, 0]),

View file

@ -8,7 +8,7 @@
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
//! 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 //! Most of the constants are given with
//! `LONG_DESCRIPTIVE_UPPER_CASE_NAMES`, but they can be brought into //! `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"))] #[cfg(not(feature="radix_51"))]
pub use backend::u32::constants::*; 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 /// This is the little-endian byte encoding of \\( 4/5 \pmod p \\),
/// sign bit is 0 since the basepoint has x chosen to be positive. /// which is the \\(y\\)-coordinate of the Ed25519 basepoint.
pub const BASE_CMPRSSD: CompressedEdwardsY = ///
/// 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, 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, 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. /// The X25519 basepoint, in `CompressedMontgomeryU` format.
pub const BASE_COMPRESSED_MONTGOMERY: CompressedMontgomeryU = pub const X25519_BASEPOINT_COMPRESSED: CompressedMontgomeryU =
CompressedMontgomeryU([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 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 /// The Ristretto basepoint, as a `RistrettoPoint`.
/// `_TABLE`, which provides fast scalar multiplication. ///
/// This is called `_POINT` to distinguish it from `_TABLE`, which
/// provides fast scalar multiplication.
pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT); pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT);
/// `BASEPOINT_ORDER` is the order of base point, i.e. `l = 2^252 + /// `BASEPOINT_ORDER` is the order of the Ristretto group and of the Ed25519 basepoint, i.e.,
/// 27742317777372353535851937790883648493`, in little-endian bytes. /// $$
/// \ell = 2^\{252\} + 27742317777372353535851937790883648493.
/// $$
pub const BASEPOINT_ORDER: Scalar = Scalar{ pub const BASEPOINT_ORDER: Scalar = Scalar{
bytes: [ bytes: [
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 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 // Precomputed basepoint table is generated into a file by build.rs
#[cfg(feature="precomputed_tables")] #[cfg(feature="precomputed_tables")]
@ -98,7 +84,8 @@ include!(concat!(env!("OUT_DIR"), "/basepoint_table.rs"));
#[cfg(feature="precomputed_tables")] #[cfg(feature="precomputed_tables")]
use ristretto::RistrettoBasepointTable; use ristretto::RistrettoBasepointTable;
/// The Ed25519 basepoint, as a RistrettoPoint
/// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication.
#[cfg(feature="precomputed_tables")] #[cfg(feature="precomputed_tables")]
pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable
= RistrettoBasepointTable(ED25519_BASEPOINT_TABLE); = RistrettoBasepointTable(ED25519_BASEPOINT_TABLE);

View file

@ -8,8 +8,7 @@
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
//! This module contains internal curve representations which are not part //! Internal curve representations which are not part of the public API.
//! of the public API.
//! //!
//! # Curve representations //! # Curve representations
//! //!

View file

@ -9,6 +9,77 @@
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
//! Group operations for Curve25519, in Edwards form. //! 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 // We allow non snake_case names because coordinates in projective space are
// traditionally denoted by the capitalisation of their respective // traditionally denoted by the capitalisation of their respective
@ -690,14 +761,14 @@ impl EdwardsBasepointTable {
} }
impl EdwardsPoint { impl EdwardsPoint {
/// Multiply by the cofactor: compute `8 * self`. /// Multiply by the cofactor: return \\([8]P\\).
pub fn mult_by_cofactor(&self) -> EdwardsPoint { pub fn mult_by_cofactor(&self) -> EdwardsPoint {
self.mult_by_pow_2(3) self.mult_by_pow_2(3)
} }
/// Compute `2^k * self` by successive doublings. /// Compute \\([2\^k] P \\) by successive doublings. Requires \\( k > 0 \\).
/// Requires `k > 0`.
pub(crate) fn mult_by_pow_2(&self, k: u32) -> EdwardsPoint { pub(crate) fn mult_by_pow_2(&self, k: u32) -> EdwardsPoint {
debug_assert!( k > 0 );
let mut r: CompletedPoint; let mut r: CompletedPoint;
let mut s = self.to_projective(); let mut s = self.to_projective();
for _ in 0..(k-1) { for _ in 0..(k-1) {
@ -709,22 +780,59 @@ impl EdwardsPoint {
/// Determine if this point is of small order. /// Determine if this point is of small order.
/// ///
/// The order of the group of points on the curve \\(\mathcal E\\) /// # Return
/// 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 /// * `true` if `self` is in the torsion subgroup \\( \mathcal E[8] \\);
/// the internal `curve25519-dalek` documentation on Ristretto. /// * `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 /// # Return
/// ///
/// True if `self` is of small order; false otherwise. /// * `true` if `self` has zero torsion component and is in the
pub fn is_small_order(&self) -> bool { /// prime-order subgroup;
self.mult_by_cofactor().is_identity() /// * `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] #[test]
fn basepoint_decompression_compression() { fn basepoint_decompression_compression() {
let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES); 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()); assert!(bp.is_valid());
// Check that decompression actually gives the correct X coordinate // Check that decompression actually gives the correct X coordinate
assert_eq!(base_X, bp.X); 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 sign handling in decompression
#[test] #[test]
fn decompression_sign_handling() { fn decompression_sign_handling() {
// Manually set the high bit of the last byte to flip the sign // 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; minus_basepoint_bytes[31] |= 1 << 7;
let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes) let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes)
.decompress().unwrap(); .decompress().unwrap();
@ -1018,7 +1126,7 @@ mod test {
fn basepoint_mult_one_vs_basepoint() { fn basepoint_mult_one_vs_basepoint() {
let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one(); let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one();
let compressed = bp.compress(); 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. /// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint.
@ -1026,7 +1134,7 @@ mod test {
#[cfg(feature="precomputed_tables")] #[cfg(feature="precomputed_tables")]
fn basepoint_table_basepoint_function_correct() { fn basepoint_table_basepoint_function_correct() {
let bp = constants::ED25519_BASEPOINT_TABLE.basepoint(); 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<EdwardsPoint> for EdwardsPoint` /// Test `impl Add<EdwardsPoint> for EdwardsPoint`
@ -1139,7 +1247,7 @@ mod test {
fn basepoint_projective_extended_round_trip() { fn basepoint_projective_extended_round_trip() {
assert_eq!(constants::ED25519_BASEPOINT_POINT assert_eq!(constants::ED25519_BASEPOINT_POINT
.to_projective().to_extended().compress(), .to_projective().to_extended().compress(),
constants::BASE_CMPRSSD); constants::ED25519_BASEPOINT_COMPRESSED);
} }
/// Test computing 16*basepoint vs mult_by_pow_2(4) /// Test computing 16*basepoint vs mult_by_pow_2(4)
@ -1262,7 +1370,7 @@ mod test {
fn serde_cbor_basepoint_roundtrip() { fn serde_cbor_basepoint_roundtrip() {
let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap(); let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT_POINT).unwrap();
let parsed: EdwardsPoint = serde_cbor::from_slice(&output).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] #[test]
@ -1291,7 +1399,7 @@ mod bench {
#[bench] #[bench]
fn edwards_decompress(b: &mut Bencher) { fn edwards_decompress(b: &mut Bencher) {
let B = &constants::BASE_CMPRSSD; let B = &constants::ED25519_BASEPOINT_COMPRESSED;
b.iter(|| B.decompress().unwrap()); b.iter(|| B.decompress().unwrap());
} }

View file

@ -9,29 +9,22 @@
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "alloc", feature(alloc))] #![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))] #![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 // Refuse to compile if documentation is missing, but only on nightly.
//! //
//! **A high-performance, pure-Rust implementation of group operations for Ristretto and Curve25519.** // This means that missing docs will still fail CI, but means we can use
//! // README.md as the crate documentation.
//! **[SPOILER ALERT]** The Twelfth Doctor's first encounter with the Daleks is #![cfg_attr(feature = "nightly", deny(missing_docs))]
//! in his second full episode, "Into the Dalek". A beleaguered ship of the
//! "Combined Galactic Resistance" has discovered a broken Dalek that has #![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
//! turned "good", desiring to kill all other Daleks. The Doctor, Clara and a #![doc(html_logo_url = "https://user-images.githubusercontent.com/797/34898472-83686016-f7f3-11e7-967b-24b2aadd623a.png")]
//! 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.
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// External dependencies: // External dependencies:

View file

@ -395,7 +395,7 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use constants::BASE_COMPRESSED_MONTGOMERY; use constants::X25519_BASEPOINT_COMPRESSED;
use traits::Identity; use traits::Identity;
use super::*; use super::*;
@ -405,14 +405,14 @@ mod test {
#[test] #[test]
fn basepoint_to_montgomery() { fn basepoint_to_montgomery() {
assert_eq!(constants::ED25519_BASEPOINT_POINT.to_montgomery().compress(), assert_eq!(constants::ED25519_BASEPOINT_POINT.to_montgomery().compress(),
BASE_COMPRESSED_MONTGOMERY); X25519_BASEPOINT_COMPRESSED);
} }
/// Test Montgomery conversion against the X25519 basepoint. /// Test Montgomery conversion against the X25519 basepoint.
#[test] #[test]
fn basepoint_from_montgomery() { fn basepoint_from_montgomery() {
assert_eq!(BASE_COMPRESSED_MONTGOMERY, assert_eq!(X25519_BASEPOINT_COMPRESSED,
constants::BASE_CMPRSSD.decompress().unwrap().to_montgomery().compress()); constants::ED25519_BASEPOINT_COMPRESSED.decompress().unwrap().to_montgomery().compress());
} }
/// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660. /// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
@ -438,8 +438,8 @@ mod test {
#[test] #[test]
fn projective_to_affine_roundtrips() { fn projective_to_affine_roundtrips() {
assert_eq!(BASE_COMPRESSED_MONTGOMERY.decompress().compress(), assert_eq!(X25519_BASEPOINT_COMPRESSED.decompress().compress(),
BASE_COMPRESSED_MONTGOMERY); X25519_BASEPOINT_COMPRESSED);
} }
@ -483,7 +483,7 @@ mod test {
#[test] #[test]
fn ladder_basepoint_times_two_matches_double() { fn ladder_basepoint_times_two_matches_double() {
let two: Scalar = Scalar::from_u64(2u64); 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(); let expected: EdwardsPoint = constants::ED25519_BASEPOINT_POINT.double();
assert_eq!(result.compress(), expected.to_montgomery().compress()); assert_eq!(result.compress(), expected.to_montgomery().compress());
@ -495,7 +495,7 @@ mod test {
mod bench { mod bench {
use rand::OsRng; use rand::OsRng;
use constants::ED25519_BASEPOINT_TABLE; use constants::ED25519_BASEPOINT_TABLE;
use constants::BASE_COMPRESSED_MONTGOMERY; use constants::X25519_BASEPOINT_COMPRESSED;
use test::Bencher; use test::Bencher;
use super::*; use super::*;
@ -512,12 +512,12 @@ mod bench {
#[bench] #[bench]
fn montgomery_decompress(b: &mut Bencher) { fn montgomery_decompress(b: &mut Bencher) {
b.iter(| | BASE_COMPRESSED_MONTGOMERY.decompress()); b.iter(| | X25519_BASEPOINT_COMPRESSED.decompress());
} }
#[bench] #[bench]
fn montgomery_compress(b: &mut Bencher) { fn montgomery_compress(b: &mut Bencher) {
let p: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress(); let p: MontgomeryPoint = X25519_BASEPOINT_COMPRESSED.decompress();
b.iter(| | p.compress()); b.iter(| | p.compress());
} }

View file

@ -8,37 +8,101 @@
// - Isis Agora Lovecruft <isis@patternsinthevoid.net> // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
// 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. //! An implementation of Ristretto, which provides a prime-order group.
//! //!
//! Ristretto is a modification of Mike Hamburg's [Decaf //! # The Ristretto Group
//! cofactor-eliminating point-compression
//! scheme](https://eprint.iacr.org/2015/673.pdf) to work on top of the
//! Curve25519 group.
//! //!
//! Below are some notes on Ristretto, which are *NOT* a full writeup and which may have errors. //! Ristretto is a modification of Mike Hamburg's Decaf scheme to work
//! //! with Curve25519. The introduction of the Decaf paper, [_Decaf:
//! # Notes on Ristretto //! Eliminating cofactors through point
//! //! compression_](https://eprint.iacr.org/2015/673.pdf), notes that while
//! ## Decaf //! most cryptographic systems require a group of prime order, most
//! //! concrete implementations using elliptic curve groups fall short --
//! The introduction of the Decaf paper, [_Decaf: Eliminating cofactors //! they either provide a group of prime order, but with incomplete or
//! through point compression_](https://eprint.iacr.org/2015/673.pdf) //! variable-time addition formulae (for instance, most Weierstrass
//! notes that while most cryptographic systems require a group of prime //! models), or else they provide a fast and safe implementation of a
//! order, most concrete implementations using elliptic curve groups //! group whose order is not quite a prime \\(q\\), but \\(hq\\) for a
//! fall short -- they either provide a group of prime order, but with //! small cofactor \\(h\\) (for instance, Edwards curves, which have
//! incomplete or variable-time addition formulae (for instance, most //! cofactor at least \\(4\\)).
//! 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 //! This abstraction mismatch requires ad-hoc protocol modifications to
//! ensure security; these modifications require careful analysis and //! 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 //! The Decaf suggestion is to use a quotient group, such as \\(\mathcal
//! E / \mathcal E[4]\\) or \\(2 \mathcal E / \mathcal E[2] \\), to //! 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 //! This requires only changing
//! //!
@ -61,8 +125,25 @@
//! explains the name: Decaf is named "after the procedure which divides //! explains the name: Decaf is named "after the procedure which divides
//! the effect of coffee by \\(4\\)". However, Curve25519 has a //! the effect of coffee by \\(4\\)". However, Curve25519 has a
//! cofactor of \\(8\\). To eliminate its cofactor, we tweak Decaf to //! cofactor of \\(8\\). To eliminate its cofactor, we tweak Decaf to
//! restrict further. This gives the //! restrict further. This [additional restriction][ristretto_coffee]
//! [Ristretto](https://en.wikipedia.org/wiki/Ristretto) encoding. //! 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 //! ## 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; use core::fmt::Debug;
@ -419,29 +496,31 @@ use traits::Identity;
// Compressed points // 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)] #[derive(Copy, Clone, Eq, PartialEq)]
pub struct CompressedRistretto(pub [u8; 32]); pub struct CompressedRistretto(pub [u8; 32]);
/// The result of compressing a `RistrettoPoint`.
impl CompressedRistretto { impl CompressedRistretto {
/// Convert this `CompressedRistretto` to its underlying array of bytes. /// Copy the bytes of this `CompressedRistretto`.
pub fn to_bytes(&self) -> [u8; 32] { pub fn to_bytes(&self) -> [u8; 32] {
self.0 self.0
} }
/// View this `CompressedRistretto` as an array of bytes. /// 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 &self.0
} }
/// Attempt to decompress to an `RistrettoPoint`. /// Attempt to decompress to an `RistrettoPoint`.
/// ///
/// This function executes in constant time for all valid inputs. /// # Return
/// Inputs which do not decode to a RistrettoPoint may return ///
/// early. /// - `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<RistrettoPoint> { pub fn decompress(&self) -> Option<RistrettoPoint> {
// Step 1. Check s for validity: // Step 1. Check s for validity:
// 1.a) s must be 32 bytes (we get this from the type system) // 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 /// prime-order group as a quotient group of a subgroup of (the
/// Edwards form of) Curve25519. /// Edwards form of) Curve25519.
/// ///
/// Internally, a `RistrettoPoint` is a wrapper type around /// Internally, a `RistrettoPoint` is implemented as a wrapper type
/// `EdwardsPoint`, with custom equality, compression, and /// around `EdwardsPoint`, with custom equality, compression, and
/// decompression routines to account for the quotient. /// 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)] #[derive(Copy, Clone)]
pub struct RistrettoPoint(pub(crate) EdwardsPoint); pub struct RistrettoPoint(pub(crate) EdwardsPoint);
impl RistrettoPoint { impl RistrettoPoint {
/// Compress in Ristretto format. /// Compress this point using the Ristretto encoding.
///
/// # 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)). <span style="float: right">(1)</span>
///
/// We can compute this as
///
/// s = (Z - Y) / sqrt((Z-Y)(Z+Y)). <span style="float: right">(1)</span>
///
/// The denominator is
///
/// invsqrt((Z-Y)(Z+Y)) = invsqrt(Z² - Y²). <span style="float: right">(1)</span>
///
/// 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². <span style="float: right">(1)</span>
///
/// This means that, for any point `(X:Y:Z:T)` in extended coordinates, we have
///
/// -dX²Y² = Z⁴ + Z²X² - Z²Y², <span style="float: right">(2)</span>
///
/// so that
///
/// (-1-d)X²Y² = Z⁴ + Z²X² - Z²Y² - X²Y², <span style="float: right">(3)</span>
///
/// and hence
///
/// (-1-d)X²Y² = (Z² - Y²)(Z² + X²). <span style="float: right">(4)</span>
///
/// Taking inverse square roots gives
///
/// invsqrt(Z² + X²) = invsqrt(-1-d) sqrt((Z² - Y²)/(X²Y²)). <span style="float: right">(4)</span>
///
///
pub fn compress(&self) -> CompressedRistretto { pub fn compress(&self) -> CompressedRistretto {
let mut X = self.0.X; let mut X = self.0.X;
let mut Y = self.0.Y; let mut Y = self.0.Y;
@ -666,7 +687,37 @@ impl RistrettoPoint {
CompressedRistretto(s.to_bytes()) 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<RistrettoPoint> =
/// (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"))] #[cfg(any(feature = "alloc", feature = "std"))]
pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto> pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto>
where I: IntoIterator<Item = &'a RistrettoPoint> where I: IntoIterator<Item = &'a RistrettoPoint>
@ -762,7 +813,7 @@ impl RistrettoPoint {
/// ///
/// This method is not public because it's just used for hashing /// This method is not public because it's just used for hashing
/// to a point -- proper elligator support is deferred for now. /// 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 (i, d) = (&constants::SQRT_M1, &constants::EDWARDS_D);
let one = FieldElement::one(); let one = FieldElement::one();
@ -781,7 +832,7 @@ impl RistrettoPoint {
// s = sqrt(N/D) if N/D is square // s = sqrt(N/D) if N/D is square
s.conditional_assign(&maybe_s, N_over_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); let (rN_over_D_is_square, mut maybe_s) = FieldElement::sqrt_ratio(&(&r*&N), &D);
maybe_s.negate(); 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); define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
/// Given a vector of (possibly secret) scalars and a vector of /// Given an iterator of (possibly secret) scalars and an iterator of
/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. /// (possibly secret) points, compute
/// $$
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
/// ///
/// This function has the same behaviour as /// This function has the same behaviour as
/// `vartime::multiscalar_mult` but is constant-time. /// `vartime::multiscalar_mult` but is constant-time.
/// ///
/// # Input /// # 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. /// error to call this function with two iterators of different lengths.
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint 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)) RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points))
} }
/// Precomputation /// A precomputed table of multiples of a basepoint, used to accelerate
/// scalar multiplication.
#[derive(Clone)] #[derive(Clone)]
pub struct RistrettoBasepointTable(pub EdwardsBasepointTable); pub struct RistrettoBasepointTable(pub(crate) EdwardsBasepointTable);
impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable { impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable {
type Output = RistrettoPoint; type Output = RistrettoPoint;
@ -1114,15 +1169,16 @@ pub mod vartime {
//! Variable-time operations on ristretto points, useful for non-secret data. //! Variable-time operations on ristretto points, useful for non-secret data.
use super::*; 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 /// points, compute
/// /// $$
/// c_1 P_1 + ... + c_n P_n. /// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
/// $$
/// ///
/// # Input /// # Input
/// ///
/// A vector of `Scalar`s and a vector of `RistrettoPoints`. It is an /// A iterable of `Scalar`s and a iterable of `RistrettoPoints`. It is an
/// error to call this function with two vectors of different lengths. /// error to call this function with two iterators of different lengths.
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint
where I: IntoIterator<Item = &'a Scalar>, where I: IntoIterator<Item = &'a Scalar>,

View file

@ -790,12 +790,6 @@ mod test {
assert_eq!(should_be_two, two); 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)] #[allow(non_snake_case)]
#[test] #[test]
fn impl_mul() { fn impl_mul() {