From a81b1378d671d5ac9fdc835c66c07cfb7c5b0a84 Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Thu, 29 Dec 2022 13:22:09 -0800 Subject: [PATCH] Release a fork for temporary use before dalek 4.0 (#3) * edit * update curve25519 * fmt Co-authored-by: onewayfunc --- .travis.yml | 41 - CHANGELOG.md | 197 ---- CODE_OF_CONDUCT.md | 8 - CONTRIBUTING.md | 19 - Cargo.toml | 19 +- Makefile | 8 - README.md | 228 +---- benches/dalek_benchmarks.rs | 24 +- docs/assets/dalek-logo-clear.png | Bin 113085 -> 0 bytes docs/assets/dalek-logo.png | Bin 109731 -> 0 bytes docs/assets/dalek-logo.svg | 1 - docs/assets/rustdoc-include-katex-header.html | 10 - docs/avx2-notes.md | 140 --- docs/ifma-notes.md | 580 ------------ docs/parallel-formulas.md | 333 ------- src/constants.rs | 4 +- src/edwards.rs | 4 +- src/field.rs | 19 +- src/lib.rs | 223 +---- src/ristretto.rs | 23 +- src/scalar.rs | 40 +- src/traits.rs | 24 +- vendor/ristretto.sage | 857 ------------------ 23 files changed, 79 insertions(+), 2723 deletions(-) delete mode 100644 .travis.yml delete mode 100644 CHANGELOG.md delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 Makefile delete mode 100644 docs/assets/dalek-logo-clear.png delete mode 100644 docs/assets/dalek-logo.png delete mode 100644 docs/assets/dalek-logo.svg delete mode 100644 docs/assets/rustdoc-include-katex-header.html delete mode 100644 docs/avx2-notes.md delete mode 100644 docs/ifma-notes.md delete mode 100644 docs/parallel-formulas.md delete mode 100644 vendor/ristretto.sage diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f2411e00..00000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -language: rust - -rust: - - stable - - nightly - -env: - # Tests the u32 backend - - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u32_backend' - # Tests the u64 backend - - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' - # Tests the fiat_u32 backend - - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std fiat_u32_backend' - # Tests the fiat_u64 backend - - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std fiat_u64_backend' - # Tests the simd backend - - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std simd_backend' - # Tests serde support and default feature selection - - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' - # Tests building without std. We have to select a backend, so we select the one - # most likely to be useful in an embedded environment. - - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend' - # Tests no_std+alloc usage using the most embedded-friendly backend - - TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' - -matrix: - exclude: - # Test the simd backend only on nightly - - rust: stable - env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std simd_backend' - # Test no_std+alloc only on nightly - - rust: stable - env: TEST_COMMAND=test EXTRA_FLAGS='--lib --no-default-features' FEATURES='alloc u32_backend' - -script: - - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS - -notifications: - slack: - rooms: - - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 000b9654..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,197 +0,0 @@ -# Changelog - -Entries are listed in reverse chronological order per undeprecated -major series. - -## 3.x series - -### 3.2.0 - -* Add support for getting the identity element for the Montgomery - form of curve25519, which is useful in certain protocols for - checking contributory behaviour in derivation of shared secrets. - -### 3.1.2 - -* Revert a commit which mistakenly removed support for `zeroize` traits - for some point types, as well as elligator2 support for Edwards points. - -### 3.1.1 - -* Fix documentation builds on nightly due to syntax changes to - `#![cfg_attr(feature = "nightly", doc = include_str!("../README.md"))]`. - -### 3.1.0 - -* Add support for the Elligator2 encoding for Edwards points. -* Add two optional formally-verified field arithmetic backends which - use the Fiat Crypto project's Rust code, which is generated from - proofs of functional correctness checked by the Coq theorem proving - system. -* Add support for additional sizes of precomputed tables for basepoint - scalar multiplication. -* Fix an unused import. -* Add support for using the `zeroize` traits with all point types. - Note that points are not automatically zeroized on Drop, but that - consumers of `curve25519-dalek` should call these methods manually - when needed. - -### 3.0.3 - -* Fix documentation builds on nightly due to syntax changes to - `#![cfg_attr(feature = "nightly", doc = include_str!("../README.md"))]`. - -### 3.0.2 - -* Multiple documentation typo fixes. -* Fixes to make using `alloc`+`no_std` possible for stable Rust. - -### 3.0.1 - -* Update the optional `packed-simd` dependency to rely on a newer, - maintained version of the `packed-simd-2` crate. - -### 3.0.0 - -* Update the `digest` dependency to `0.9`. This requires a major version - because the `digest` traits are part of the public API, but there are - otherwise no changes to the API. - -## 2.x series - -### 2.1.3 - -* Fix documentation builds on nightly due to syntax changes to - `#![fg_attr(feature = "nightly", doc = include_str!("../README.md"))]`. - -### 2.1.2 - -* Multiple documenation typo fixes. -* Fix `alloc` feature working with stable rust. - -### 2.1.1 - -* Update the optional `packed-simd` dependency to rely on a newer, - maintained version of the `packed-simd-2` crate. - -### 2.1.0 - -* Make `Scalar::from_bits` a `const fn`, allowing its use in `const` contexts. - -### 2.0.0 - -* Fix a data modeling error in the `serde` feature pointed out by Trevor Perrin - which caused points and scalars to be serialized with length fields rather - than as fixed-size 32-byte arrays. This is a breaking change, but it fixes - compatibility with `serde-json` and ensures that the `serde-bincode` encoding - matches the conventional encoding for X/Ed25519. -* Update `rand_core` to `0.5`, allowing use with new `rand` versions. -* Switch from `clear_on_drop` to `zeroize` (by Tony Arcieri). -* Require `subtle = ^2.2.1` and remove the note advising nightly Rust, which is - no longer required as of that version of `subtle`. See the `subtle` - changelog for more details. -* Update `README.md` for `2.x` series. -* Remove the `build.rs` hack which loaded the entire crate into its own - `build.rs` to generate constants, and keep the constants in the source code. - -The only significant change is the data model change to the `serde` feature; -besides the `rand_core` version bump, there are no other user-visible changes. - -## 1.x series - -### 1.2.6 - -* Fixes to make using alloc+no_std possible for stable Rust. - -### 1.2.5 - -* Update the optional `packed-simd` dependency to rely on a newer, - maintained version of the `packed-simd-2` crate. - -### 1.2.4 - -* Specify a semver bound for `clear_on_drop` rather than an exact version, - addressing an issue where changes to inline assembly in rustc prevented - `clear_on_drop` from working without an update. - -### 1.2.3 - -* Fix an issue identified by a Quarkslab audit (and Jack Grigg), where manually - constructing unreduced `Scalar` values, as needed for X/Ed25519, and then - performing scalar/scalar arithmetic could compute incorrect results. -* Switch to upstream Rust intrinsics for the IFMA backend now that they exist in - Rust and don't need to be defined locally. -* Ensure that the NAF computation works correctly, even for parameters never - used elsewhere in the codebase. -* Minor refactoring to EdwardsPoint decompression. -* Fix broken links in documentation. -* Fix compilation on nightly broken due to changes to the `#[doc(include)]` path - root (not quite correctly done in 1.2.2). - -### 1.2.2 - -* Fix a typo in an internal doc-comment. -* Add the "crypto" tag to crate metadata. -* Fix compilation on nightly broken due to changes to the `#[doc(include)]` path - root. - -### 1.2.1 - -* Fix a bug in bucket index calculations in the Pippenger multiscalar algorithm - for very large input sizes. -* Add a more extensive randomized multiscalar multiplication consistency check - to the test suite to prevent regressions. -* Ensure that that multiscalar and NAF computations work correctly on extremal - `Scalar` values constructed via `from_bits`. - -### 1.2.0 - -* New multiscalar multiplication algorithm with better performance for - large problem sizes. The backend algorithm is selected - transparently using the size hints of the input iterators, so no - changes are required for client crates to start using it. -* Equality of Edwards points is now checked in projective coordinates. -* Serde can now be used with `no_std`. - -### 1.1.4 - -* Fix typos in documentation comments. -* Remove unnecessary `Default` bound on `Scalar::from_hash`. - -### 1.1.3 - -* Reverts the change in 1.1.0 to allow owned and borrowed RNGs, which caused a breakage due to a subtle interaction with ownership rules. (The `RngCore` change is retained). - -### 1.1.2 - -* Disabled KaTeX on `docs.rs` pending proper [support upstream](https://github.com/rust-lang/docs.rs/issues/302). - -## 1.1.1 - -* Fixed an issue related to `#[cfg(rustdoc)]` which prevented documenting multiple backends. - -### 1.1.0 - -* Adds support for precomputation for multiscalar multiplication. -* Restructures the internal source tree into `serial` and `vector` backends (no change to external API). -* Adds a new IFMA backend which sets speed records. -* The `avx2_backend` feature is now an alias for the `simd_backend` feature, which autoselects an appropriate vector backend (currently AVX2 or IFMA). -* Replaces the `rand` dependency with `rand_core`. -* Generalizes trait bounds on `RistrettoPoint::random()` and `Scalar::random()` to allow owned and borrowed RNGs and to allow `RngCore` instead of `Rng`. - -### 1.0.3 - -* Adds `ConstantTimeEq` implementation for compressed points. - -### 1.0.2 - -* Fixes a typo in the naming of variables in Ristretto formulas (no change to functionality). - -### 1.0.1 - -* Depends on the stable `2.0` version of `subtle` instead of `2.0.0-pre.0`. - -### 1.0.0 - -Initial stable release. Yanked due to a dependency mistake (see above). - diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index a802fde5..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,8 +0,0 @@ -# Code of Conduct - -We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html), -with the following additional clauses: - -* We respect the rights to privacy and anonymity for contributors and people in - the community. If someone wishes to contribute under a pseudonym different to - their primary identity, that wish is to be respected by all contributors. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index d4e0ff8e..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,19 +0,0 @@ -# Contributing to curve25519-dalek - -If you have questions or comments, please feel free to email the -authors. - -For feature requests, suggestions, and bug reports, please open an issue on -[our Github](https://github.com/dalek-cryptography/curve25519-dalek). (Or, send us -an email if you're opposed to using Github for whatever reason.) - -Patches are welcomed as pull requests on -[our Github](https://github.com/dalek-cryptography/curve25519-dalek), as well as by -email (preferably sent to all of the authors listed in `Cargo.toml`). - -All issues on curve25519-dalek are mentored, if you want help with a bug just -ask @isislovecruft or @hdevalence. - -Some issues are easier than others. The `easy` label can be used to find the -easy issues. If you want to work on an issue, please leave a comment so that we -can assign it to you! diff --git a/Cargo.toml b/Cargo.toml index 3de9143d..c65bbc02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,17 @@ [package] -name = "curve25519-dalek" -# Before incrementing: -# - update CHANGELOG -# - update html_root_url -# - update README if required by semver -# - if README was updated, also update module documentation in src/lib.rs -version = "3.2.0" +name = "noah-curve25519-dalek" +version = "4.0.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" license = "BSD-3-Clause" -repository = "https://github.com/dalek-cryptography/curve25519-dalek" -homepage = "https://dalek.rs/curve25519-dalek" -documentation = "https://docs.rs/curve25519-dalek" +repository = "https://github.com/FindoraNetwork/curve25519-dalek" categories = ["cryptography", "no-std"] keywords = ["cryptography", "crypto", "ristretto", "curve25519", "ristretto255"] description = "A pure-Rust implementation of group operations on ristretto255 and Curve25519" exclude = [ "**/.gitignore", - ".gitignore", - ".travis.yml", + ".gitignore" ] [package.metadata.docs.rs] @@ -27,9 +19,6 @@ exclude = [ # rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] features = ["nightly", "simd_backend"] -[badges] -travis-ci = { repository = "dalek-cryptography/curve25519-dalek", branch = "master"} - [dev-dependencies] sha2 = { version = "0.10", default-features = false } bincode = "1" diff --git a/Makefile b/Makefile deleted file mode 100644 index 7d870571..00000000 --- a/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -FEATURES := nightly simd_backend - -doc: - cargo rustdoc --features "$(FEATURES)" -- --html-in-header docs/assets/rustdoc-include-katex-header.html - -doc-internal: - cargo rustdoc --features "$(FEATURES)" -- --html-in-header docs/assets/rustdoc-include-katex-header.html --document-private-items - diff --git a/README.md b/README.md index 2600cce1..b1e96733 100644 --- a/README.md +++ b/README.md @@ -1,226 +1,6 @@ +# A fork of curve25519-dalek in Noah -# curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://img.shields.io/badge/dynamic/json.svg?label=docs&uri=https%3A%2F%2Fcrates.io%2Fapi%2Fv1%2Fcrates%2Fcurve25519-dalek%2Fversions&query=%24.versions%5B0%5D.num&colorB=4F74A6)](https://doc.dalek.rs) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) +This crate is a fork of curve25519-dalek with some dependencies updates. - - -**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. - -# 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 = "3" -``` - -The sole breaking change in the `3.x` series was an update to the `digest` -version, and in terms of non-breaking changes it includes: - -* support for using `alloc` instead of `std` on stable Rust, -* the Elligator2 encoding for Edwards points, -* a fix to use `packed_simd2`, -* various documentation fixes and improvements, -* support for configurably-sized, precomputed lookup tables for basepoint scalar - multiplication, -* two new formally-verified field arithmetic backends which use the Fiat Crypto - Rust code, which is generated from proofs of functional correctness checked by - the Coq theorem proving system, and -* support for explicitly calling the `zeroize` traits for all point types. - -The `2.x` series has API almost entirely unchanged from the `1.x` series, -except that: - -* an error in the data modeling for the (optional) `serde` feature was - corrected, so that when the `2.x`-series `serde` implementation is used - with `serde-bincode`, the derived serialization matches the usual X/Ed25519 - formats; -* the `rand` version was updated. - -See `CHANGELOG.md` for more details. - -# Backends and Features - -The `nightly` feature enables features available only when using a Rust nightly -compiler. In particular, it is required for rendering documentation and for -the SIMD backends. - -Curve arithmetic is implemented using one of the following backends: - -* a `u32` backend using serial formulas and `u64` products; -* a `u64` backend using serial formulas and `u128` products; -* an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records); -* an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records); - -By default the `u64` backend is selected. To select a specific backend, use: -```sh -cargo build --no-default-features --features "std u32_backend" -cargo build --no-default-features --features "std u64_backend" -# Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2 -cargo build --no-default-features --features "std simd_backend" -# Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma -cargo build --no-default-features --features "std simd_backend" -``` -Crates using `curve25519-dalek` can either select a backend on behalf of their -users, or expose feature flags that control the `curve25519-dalek` backend. - -The `std` feature is enabled by default, but it can be disabled for no-`std` -builds using `--no-default-features`. Note that this requires explicitly -selecting an arithmetic backend using one of the `_backend` features. -If no backend is selected, compilation will fail. - -# Safety - -The `curve25519-dalek` types are designed to make illegal states -unrepresentable. For example, any instance of an `EdwardsPoint` is -guaranteed to hold a point on the Edwards curve, and any instance of a -`RistrettoPoint` is guaranteed to hold a valid point in the Ristretto -group. - -All operations are implemented using constant-time logic (no -secret-dependent branches, no secret-dependent memory accesses), -unless specifically marked as being variable-time code. -We believe that our constant-time logic is lowered to constant-time -assembly, at least on `x86_64` targets. - -As an additional guard against possible future compiler optimizations, -the `subtle` crate places an optimization barrier before every -conditional move or assignment. More details can be found in [the -documentation for the `subtle` crate][subtle_doc]. - -Some functionality (e.g., multiscalar multiplication or batch -inversion) requires heap allocation for temporary buffers. All -heap-allocated buffers of potentially secret data are explicitly -zeroed before release. - -However, we do not attempt to zero stack data, for two reasons. -First, it's not possible to do so correctly: we don't have control -over stack allocations, so there's no way to know how much data to -wipe. Second, because `curve25519-dalek` provides a mid-level API, -the correct place to start zeroing stack data is likely not at the -entrypoints of `curve25519-dalek` functions, but at the entrypoints of -functions in other crates. - -The implementation is memory-safe, and contains no significant -`unsafe` code. The SIMD backend uses `unsafe` internally to call SIMD -intrinsics. These are marked `unsafe` only because invoking them on an -inappropriate CPU would cause `SIGILL`, but the entire backend is only -compiled with appropriate `target_feature`s, so this cannot occur. - -# Performance - -Benchmarks are run using [`criterion.rs`][criterion]: - -```sh -cargo bench --no-default-features --features "std u32_backend" -cargo bench --no-default-features --features "std u64_backend" -# Uses avx2 or ifma only if compiled for an appropriate target. -export RUSTFLAGS="-C target_cpu=native" -cargo bench --no-default-features --features "std simd_backend" -``` - -Performance is a secondary goal behind correctness, safety, and -clarity, but we aim to be competitive with other implementations. - -# FFI - -Unfortunately, we have no plans to add FFI to `curve25519-dalek` directly. The -reason is that we use Rust features to provide an API that maintains safety -invariants, which are not possible to maintain across an FFI boundary. For -instance, as described in the _Safety_ section above, invalid points are -impossible to construct, and this would not be the case if we exposed point -operations over FFI. - -However, `curve25519-dalek` is designed as a *mid-level* API, aimed at -implementing other, higher-level primitives. Instead of providing FFI at the -mid-level, our suggestion is to implement the higher-level primitive (a -signature, PAKE, ZKP, etc) in Rust, using `curve25519-dalek` as a dependency, -and have that crate provide a minimal, byte-buffer-oriented FFI specific to -that primitive. - -# 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 -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.* - -`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. Most of this code, -including the 32-bit field arithmetic, has since been rewritten. - -The fast `u32` and `u64` scalar arithmetic was implemented by Andrew Moon, and -the addition chain for scalar inversion was provided by Brian Smith. The -optimised batch inversion was contributed by Sean Bowe and Daira Hopwood. - -The `no_std` and `zeroize` support was contributed by Tony Arcieri. - -The formally verified backends, `fiat_u32_backend` and `fiat_u64_backend`, which -integrate with the Rust generated by the -[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) were contributed -by François Garillot. - -Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg, -Pratyush Mishra, Michael Rosenberg, and countless others for their -contributions. - -[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 -[docs-external]: https://doc.dalek.rs/curve25519_dalek/ -[docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/ -[criterion]: https://github.com/japaric/criterion.rs -[parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/vector/avx2/index.html -[subtle_doc]: https://doc.dalek.rs/subtle/ +Please refer to the [original repository](https://github.com/dalek-cryptography/curve25519-dalek) for more information. +We plan to switch to curve25519-dalek 4.0 when it is ready. \ No newline at end of file diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 41339ac4..256c07e5 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -12,10 +12,10 @@ use criterion::BatchSize; use criterion::Criterion; use criterion::{BenchmarkGroup, BenchmarkId}; -extern crate curve25519_dalek; +extern crate noah_curve25519_dalek; -use curve25519_dalek::constants; -use curve25519_dalek::scalar::Scalar; +use noah_curve25519_dalek::constants; +use noah_curve25519_dalek::scalar::Scalar; static BATCH_SIZES: [usize; 5] = [1, 2, 4, 8, 16]; static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024]; @@ -23,7 +23,7 @@ static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, mod edwards_benches { use super::*; - use curve25519_dalek::edwards::EdwardsPoint; + use noah_curve25519_dalek::edwards::EdwardsPoint; fn compress(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_POINT; @@ -80,11 +80,11 @@ mod edwards_benches { mod multiscalar_benches { use super::*; - use curve25519_dalek::edwards::EdwardsPoint; - use curve25519_dalek::edwards::VartimeEdwardsPrecomputation; - use curve25519_dalek::traits::MultiscalarMul; - use curve25519_dalek::traits::VartimeMultiscalarMul; - use curve25519_dalek::traits::VartimePrecomputedMultiscalarMul; + use noah_curve25519_dalek::edwards::EdwardsPoint; + use noah_curve25519_dalek::edwards::VartimeEdwardsPrecomputation; + use noah_curve25519_dalek::traits::MultiscalarMul; + use noah_curve25519_dalek::traits::VartimeMultiscalarMul; + use noah_curve25519_dalek::traits::VartimePrecomputedMultiscalarMul; fn construct_scalars(n: usize) -> Vec { let mut rng = thread_rng(); @@ -98,10 +98,6 @@ mod multiscalar_benches { .collect() } - fn construct(n: usize) -> (Vec, Vec) { - (construct_scalars(n), construct_points(n)) - } - fn consttime_multiscalar_mul(c: &mut BenchmarkGroup) { for multiscalar_size in &MULTISCALAR_SIZES { c.bench_with_input( @@ -249,7 +245,7 @@ mod multiscalar_benches { mod ristretto_benches { use super::*; - use curve25519_dalek::ristretto::RistrettoPoint; + use noah_curve25519_dalek::ristretto::RistrettoPoint; fn compress(c: &mut Criterion) { c.bench_function("RistrettoPoint compression", |b| { diff --git a/docs/assets/dalek-logo-clear.png b/docs/assets/dalek-logo-clear.png deleted file mode 100644 index d3170d80b21501ba3325a66e2dd7186446cb4944..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113085 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 SJpPLO-62g~jnsW7uKj=Qh`hQ0 diff --git a/docs/assets/dalek-logo.png b/docs/assets/dalek-logo.png deleted file mode 100644 index 83d6a0c52776e03c7c56efcffdbaa8b0584d881f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109731 zcmeFZcRbc_{6Bh2DG`xMcA_Oan+S=FqU^2gmAx|hl$9+RS&_Z7H<_XAy@jldvO+ko z>(=-8JLiw{*E#3E)1%MhllSd?jo0;>&)4(1eILo)KXaPmGzNn?BPA)WfWaJ}LI0h? zgC}1*FI7z`aoO8o8<$C#gEF1Ed!v4_8uPo2$)`cz~; z^5AN}Gy%@J&SK%09KIEU{RH@A56hl}su+J#C+zC+GdJhWkSOqgFxe?a=EoK~e0b9^RLhNDKZ}x^0kT4#Jasq6euNQeHF^7Fkhb;AaqGi*|B)X$y8oVM49A6p$kWHtd^yp; zt$6gX_U1j9qguXO#DPi|2^Ie6#BxYcYzA4Lre?xDdLl{8Nc!@BKE0^SKzd`Ic;+`X z^NIiYctt)$vSHcw{L#-M@2^uarq?dqJo?eYmVNPO#L>SoYy|&npex7tjvm(D@jPz* z?{@=E!8v39P?CH(+AM~fKKS24%*5$V9(_9KBSLiauRN0fH6pF>|N3YA+5h#=|2>by z|2xzC{{te)d;C8-s{e0fc&op!Z`KwcEsykzQ7QpA1jSgJc0TKhG3zpo&?HZSiw{B{ zNK3zqj$YEyN52+>G|J-y1tn$Q&`^4Nd;81ys^6_lp$xeL(pX(fVlU1*hS7PsJW?W{ zc?{n0IDQYk(juA=3~$AXshY1!G|7C6u|23}+>GTnoN^GEv& z#UKU0svC*s_b@t(#{BQm^`!9d3#o1Bjo{MIuHt-oP@v(5jFoa*~{<@6M76S+iG`g)ot$IMowX$n3$T{ zl%v-$Qm?_ZJ$Pezt?Jf>@ z=yVvw&`PLyg^_#kkU#qL>?z*=%cn<9h8(*kDlOMd#eXO947;cL5zCt8e~TkTi(`Ld zSg|->yXkQXBw8$6HUBFIpscVF!?BBe(sx7xdhNyzBD+-LH00T2@Z| zn+vNS?7I_(``Z83^BOzc-Rop;cE+chgQ8WCYI{7xZjtb>Tg^XxQNS*NE{+K6P!|HQ za~2jA{f|hn`y`yldeO5?==}B0c3iR^8ymZNbpAtF*}hWNcIEovvbVe2+tsD3#|3j? zLh-QzaNJuinJzyJ;2CF2Cq>U&ypaEWb?l|bzcuKgHIS28(hUp@7|I=;H8yDAHF9>| z)k{Na8oYw`!VBb*HWSU!LAc{4EP^@FeD=H~zuEHBp z0yXp4z?$A&%jzA2QB$@-qX8l3<}QVgwMd@9N?j@|X!%8JF+Y$O0rxRiEpV_z-XV*Xd>^?_9kz|IWMNC<+>j3O;{Q&gXWp8 zj#o)YV

wBujqx>C=yLF}x(3nWiC2 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 U`|&OIdalek \ No newline at end of file diff --git a/docs/assets/rustdoc-include-katex-header.html b/docs/assets/rustdoc-include-katex-header.html deleted file mode 100644 index bc4e3d8a..00000000 --- a/docs/assets/rustdoc-include-katex-header.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/docs/avx2-notes.md b/docs/avx2-notes.md deleted file mode 100644 index ccb50223..00000000 --- a/docs/avx2-notes.md +++ /dev/null @@ -1,140 +0,0 @@ -An AVX2 implementation of the vectorized point operation strategy. - -# Field element representation - -Our strategy is to implement 4-wide multiplication and squaring by -wordslicing, using one 64-bit AVX2 lane for each field element. Field -elements are represented in the usual way as 10 `u32` limbs in radix -\\(25.5\\) (i.e., alternating between \\(2\^{26}\\) for even limbs and -\\(2\^{25}\\) for odd limbs). This has the effect that passing between -the parallel 32-bit AVX2 representation and the serial 64-bit -representation (which uses radix \\(2^{51}\\)) amounts to regrouping -digits. - -The field element representation is oriented around the AVX2 -`vpmuludq` instruction, which multiplies the low 32 bits of each -64-bit lane of each operand to produce a 64-bit result. - -```text,no_run -(a1 ?? b1 ?? c1 ?? d1 ??) -(a2 ?? b2 ?? c2 ?? d2 ??) - -(a1*a2 b1*b2 c1*c2 d1*d2) -``` - -To unpack 32-bit values into 64-bit lanes for use in multiplication -it would be convenient to use the `vpunpck[lh]dq` instructions, -which unpack and interleave the low and high 32-bit lanes of two -source vectors. -However, the AVX2 versions of these instructions are designed to -operate only within 128-bit lanes of the 256-bit vectors, so that -interleaving the low lanes of `(a0 b0 c0 d0 a1 b1 c1 d1)` with zero -gives `(a0 00 b0 00 a1 00 b1 00)`. Instead, we pre-shuffle the data -layout as `(a0 b0 a1 b1 c0 d0 c1 d1)` so that we can unpack the -"low" and "high" parts as - -```text,no_run -(a0 00 b0 00 c0 00 d0 00) -(a1 00 b1 00 c1 00 d1 00) -``` - -The data layout for a vector of four field elements \\( (a,b,c,d) -\\) with limbs \\( a_0, a_1, \ldots, a_9 \\) is as `[u32x8; 5]` in -the form - -```text,no_run -(a0 b0 a1 b1 c0 d0 c1 d1) -(a2 b2 a3 b3 c2 d2 c3 d3) -(a4 b4 a5 b5 c4 d4 c5 d5) -(a6 b6 a7 b7 c6 d6 c7 d7) -(a8 b8 a9 b9 c8 d8 c9 d9) -``` - -Since this breaks cleanly into two 128-bit lanes, it may be possible -to adapt it to 128-bit vector instructions such as NEON without too -much difficulty. - -# Avoiding Overflow in Doubling - -To analyze the size of the field element coefficients during the -computations, we can parameterize the bounds on the limbs of each -field element by \\( b \in \mathbb R \\) representing the excess bits -above that limb's radix, so that each limb is bounded by either -\\(2\^{25+b} \\) or \\( 2\^{26+b} \\), as appropriate. - -The multiplication routine requires that its inputs are bounded with -\\( b < 1.75 \\), in order to fit a multiplication by \\( 19 \\) -into 32 bits. Since \\( \lg 19 < 4.25 \\), \\( 19x < 2\^{32} \\) -when \\( x < 2\^{27.75} = 2\^{26 + 1.75} \\). However, this is only -required for one of the inputs; the other can grow up to \\( b < 2.5 -\\). - -In addition, the multiplication and squaring routines do not -canonically reduce their outputs, but can leave some small uncarried -excesses, so that their reduced outputs are bounded with -\\( b < 0.007 \\). - -The non-parallel portion of the doubling formulas is -$$ -\begin{aligned} -(S\_5 &&,&& S\_6 &&,&& S\_8 &&,&& S\_9 ) -&\gets -(S\_1 + S\_2 &&,&& S\_1 - S\_2 &&,&& S\_1 + 2S\_3 - S\_2 &&,&& S\_1 + S\_2 - S\_4) -\end{aligned} -$$ - -Computing \\( (S\_5, S\_6, S\_8, S\_9 ) \\) as -$$ -\begin{matrix} - & S\_1 & S\_1 & S\_1 & S\_1 \\\\ -+& S\_2 & & & S\_2 \\\\ -+& & & S\_3 & \\\\ -+& & & S\_3 & \\\\ -+& & 2p & 2p & 2p \\\\ --& & S\_2 & S\_2 & \\\\ --& & & & S\_4 \\\\ -=& S\_5 & S\_6 & S\_8 & S\_9 -\end{matrix} -$$ -results in bit-excesses \\( < (1.01, 1.60, 2.33, 2.01)\\) for -\\( (S\_5, S\_6, S\_8, S\_9 ) \\). The products we want to compute -are then -$$ -\begin{aligned} -X\_3 &\gets S\_8 S\_9 \leftrightarrow (2.33, 2.01) \\\\ -Y\_3 &\gets S\_5 S\_6 \leftrightarrow (1.01, 1.60) \\\\ -Z\_3 &\gets S\_8 S\_6 \leftrightarrow (2.33, 1.60) \\\\ -T\_3 &\gets S\_5 S\_9 \leftrightarrow (1.01, 2.01) -\end{aligned} -$$ -which are too large: it's not possible to arrange the multiplicands so -that one vector has \\(b < 2.5\\) and the other has \\( b < 1.75 \\). -However, if we flip the sign of \\( S\_4 = S\_0\^2 \\) during -squaring, so that we output \\(S\_4' = -S\_4 \pmod p\\), then we can -compute -$$ -\begin{matrix} - & S\_1 & S\_1 & S\_1 & S\_1 \\\\ -+& S\_2 & & & S\_2 \\\\ -+& & & S\_3 & \\\\ -+& & & S\_3 & \\\\ -+& & & & S\_4' \\\\ -+& & 2p & 2p & \\\\ --& & S\_2 & S\_2 & \\\\ -=& S\_5 & S\_6 & S\_8 & S\_9 -\end{matrix} -$$ -resulting in bit-excesses \\( < (1.01, 1.60, 2.33, 1.60)\\) for -\\( (S\_5, S\_6, S\_8, S\_9 ) \\). The products we want to compute -are then -$$ -\begin{aligned} -X\_3 &\gets S\_8 S\_9 \leftrightarrow (2.33, 1.60) \\\\ -Y\_3 &\gets S\_5 S\_6 \leftrightarrow (1.01, 1.60) \\\\ -Z\_3 &\gets S\_8 S\_6 \leftrightarrow (2.33, 1.60) \\\\ -T\_3 &\gets S\_5 S\_9 \leftrightarrow (1.01, 1.60) -\end{aligned} -$$ -whose right-hand sides are all bounded with \\( b < 1.75 \\) and -whose left-hand sides are all bounded with \\( b < 2.5 \\), -so that we can avoid any intermediate reductions. diff --git a/docs/ifma-notes.md b/docs/ifma-notes.md deleted file mode 100644 index c6fd3b3a..00000000 --- a/docs/ifma-notes.md +++ /dev/null @@ -1,580 +0,0 @@ -An AVX512-IFMA implementation of the vectorized point operation -strategy. - -# IFMA instructions - -AVX512-IFMA is an extension to AVX-512 consisting of two instructions: - -* `vpmadd52luq`: packed multiply of unsigned 52-bit integers and add - the low 52 product bits to 64-bit accumulators; -* `vpmadd52huq`: packed multiply of unsigned 52-bit integers and add - the high 52 product bits to 64-bit accumulators; - -These operate on 64-bit lanes of their source vectors, taking the low -52 bits of each lane of each source vector, computing the 104-bit -products of each pair, and then adding either the high or low 52 bits -of the 104-bit products to the 64-bit lanes of the destination vector. -The multiplication is performed internally by reusing circuitry for -floating-point arithmetic. Although these instructions are part of -AVX512, the AVX512VL (vector length) extension (present whenever IFMA -is) allows using them with 512, 256, or 128-bit operands. - -This provides a major advantage to vectorized integer operations: -previously, vector operations could only use a \\(32 \times 32 -\rightarrow 64\\)-bit multiplier, while serial code could use a -\\(64\times 64 \rightarrow 128\\)-bit multiplier. - -## IFMA for big-integer multiplications - -A detailed example of the intended use of the IFMA instructions can be -found in a 2016 paper by Gueron and Krasnov, [_Accelerating Big -Integer Arithmetic Using Intel IFMA Extensions_][2016_gueron_krasnov]. -The basic idea is that multiplication of large integers (such as 1024, -2048, or more bits) can be performed as follows. - -First, convert a “packed” 64-bit representation -\\[ -\begin{aligned} -x &= x'_0 + x'_1 2^{64} + x'_2 2^{128} + \cdots \\\\ -y &= y'_0 + y'_1 2^{64} + y'_2 2^{128} + \cdots -\end{aligned} -\\] -into a “redundant” 52-bit representation -\\[ -\begin{aligned} -x &= x_0 + x_1 2^{52} + x_2 2^{104} + \cdots \\\\ -y &= y_0 + y_1 2^{52} + y_2 2^{104} + \cdots -\end{aligned} -\\] -with each \\(x_i, y_j\\) in a 64-bit lane. - -Writing the product as \\(z = z_0 + z_1 2^{52} + z_2 2^{104} + \cdots\\), -the “schoolbook” multiplication strategy gives -\\[ -\begin{aligned} -&z_0 &&=& x_0 & y_0 & & & & & & & & \\\\ -&z_1 &&=& x_1 & y_0 &+ x_0 & y_1 & & & & & & \\\\ -&z_2 &&=& x_2 & y_0 &+ x_1 & y_1 &+ x_0 & y_2 & & & & \\\\ -&z_3 &&=& x_3 & y_0 &+ x_2 & y_1 &+ x_1 & y_2 &+ x_0 & y_3 & & \\\\ -&z_4 &&=& \vdots\\;&\\;\vdots &+ x_3 & y_1 &+ x_2 & y_2 &+ x_1 & y_3 &+ \cdots& \\\\ -&z_5 &&=& & & \vdots\\;&\\;\vdots &+ x_3 & y_2 &+ x_2 & y_3 &+ \cdots& \\\\ -&z_6 &&=& & & & & \vdots\\;&\\;\vdots &+ x_3 & y_3 &+ \cdots& \\\\ -&z_7 &&=& & & & & & & \vdots\\;&\\;\vdots &+ \cdots& \\\\ -&\vdots&&=& & & & & & & & & \ddots& \\\\ -\end{aligned} -\\] -Notice that the product coefficient \\(z_k\\), representing the value -\\(z_k 2^{52k}\\), is the sum of all product terms -\\( -(x_i 2^{52 i}) (y_j 2^{52 j}) -\\) -with \\(k = i + j\\). -Write the IFMA operators \\(\mathrm{lo}(a,b)\\), denoting the low -\\(52\\) bits of \\(ab\\), and -\\(\mathrm{hi}(a,b)\\), denoting the high \\(52\\) bits of -\\(ab\\). -Now we can rewrite the product terms as -\\[ -\begin{aligned} -(x_i 2^{52 i}) (y_j 2^{52 j}) -&= -2^{52 (i+j)}( -\mathrm{lo}(x_i, y_j) + -\mathrm{hi}(x_i, y_j) 2^{52} -) -\\\\ -&= -\mathrm{lo}(x_i, y_j) 2^{52 (i+j)} + -\mathrm{hi}(x_i, y_j) 2^{52 (i+j+1)}. -\end{aligned} -\\] -This means that the low half of \\(x_i y_j\\) can be accumulated onto -the product limb \\(z_{i+j}\\) and the high half can be directly -accumulated onto the next-higher product limb \\(z_{i+j+1}\\) with no -additional operations. This allows rewriting the schoolbook -multiplication into the form -\\[ -\begin{aligned} -&z_0 &&=& \mathrm{lo}(x_0,&y_0) & & & & & & & & & & \\\\ -&z_1 &&=& \mathrm{lo}(x_1,&y_0) &+\mathrm{hi}(x_0,&y_0) &+\mathrm{lo}(x_0,&y_1) & & & & & & \\\\ -&z_2 &&=& \mathrm{lo}(x_2,&y_0) &+\mathrm{hi}(x_1,&y_0) &+\mathrm{lo}(x_1,&y_1) &+\mathrm{hi}(x_0,&y_1) &+\mathrm{lo}(x_0,&y_2) & & \\\\ -&z_3 &&=& \mathrm{lo}(x_3,&y_0) &+\mathrm{hi}(x_2,&y_0) &+\mathrm{lo}(x_2,&y_1) &+\mathrm{hi}(x_1,&y_1) &+\mathrm{lo}(x_1,&y_2) &+ \cdots& \\\\ -&z_4 &&=& \vdots\\;&\\;\vdots &+\mathrm{hi}(x_3,&y_0) &+\mathrm{lo}(x_3,&y_1) &+\mathrm{hi}(x_2,&y_1) &+\mathrm{lo}(x_2,&y_2) &+ \cdots& \\\\ -&z_5 &&=& & & \vdots\\;&\\;\vdots & \vdots\\;&\\;\vdots &+\mathrm{hi}(x_3,&y_1) &+\mathrm{lo}(x_3,&y_2) &+ \cdots& \\\\ -&z_6 &&=& & & & & & & \vdots\\;&\\;\vdots & \vdots\\;&\\;\vdots &+ \cdots& \\\\ -&\vdots&&=& & & & & & & & & & & \ddots& \\\\ -\end{aligned} -\\] -Gueron and Krasnov implement multiplication by constructing vectors -out of the columns of this diagram, so that the source operands for -the IFMA instructions are of the form \\((x_0, x_1, x_2, \ldots)\\) -and \\((y_i, y_i, y_i, \ldots)\\). -After performing the multiplication, -the product terms \\(z_i\\) are then repacked into a 64-bit representation. - -## An alternative strategy - -The strategy described above is aimed at big-integer multiplications, -such as 1024, 2048, or 4096 bits, which would be used for applications -like RSA. However, elliptic curve cryptography uses much smaller field -sizes, such as 256 or 384 bits, so a different strategy is needed. - -The parallel Edwards formulas provide parallelism at the level of the -formulas for curve operations. This means that instead of scanning -through the terms of the source operands and parallelizing *within* a -field element (as described above), we can arrange the computation in -product-scanning form and parallelize *across* field elements (as -described below). - -The parallel Edwards -formulas provide 4-way parallelism, so they can be implemented using -256-bit vectors using a single 64-bit lane for each element, or using -512-bit vectors using two 64-bit lanes. -The only available CPU supporting IFMA (the -i3-8121U) executes 512-bit IFMA instructions at half rate compared to -256-bit instructions, so for now there's no throughput advantage to -using 512-bit IFMA instructions, and this implementation uses 256-bit -vectors. - -To extend this to 512-bit vectors, it's only only necessary to achieve -2-way parallelism, and it's possible (with a small amount of overhead) -to create a hybrid strategy that operates entirely within 128-bit -lanes. This means that cross-lane operations can use the faster -`vpshufd` (1c latency) instead of a general shuffle instruction (3c -latency). - -# Choice of radix - -The inputs to IFMA instructions are 52 bits wide, so the radix \\(r\\) -used to represent a multiprecision integer must be \\( r \leq 52 \\). -The obvious choice is the "native" radix \\(r = 52\\). - -As described above, this choice -has the advantage that for \\(x_i, y_j \in [0,2^{52})\\), the product term -\\[ -\begin{aligned} -(x_i 2^{52 i}) (y_j 2^{52 j}) -&= -2^{52 (i+j)}( -\mathrm{lo}(x_i, y_j) + -\mathrm{hi}(x_i, y_j) 2^{52} -) -\\\\ -&= -\mathrm{lo}(x_i, y_j) 2^{52 (i+j)} + -\mathrm{hi}(x_i, y_j) 2^{52 (i+j+1)}, -\end{aligned} -\\] -so that the low and high halves of the product can be directly accumulated -onto the product limbs. -In contrast, when using a smaller radix \\(r = 52 - k\\), -the product term has the form -\\[ -\begin{aligned} -(x_i 2^{r i}) (y_j 2^{r j}) -&= -2^{r (i+j)}( -\mathrm{lo}(x_i, y_j) + -\mathrm{hi}(x_i, y_j) 2^{52} -) -\\\\ -&= -\mathrm{lo}(x_i, y_j) 2^{r (i+j)} + -( -\mathrm{hi}(x_i, y_j) 2^k -) -2^{r (i+j+1)}. -\end{aligned} -\\] -What's happening is that the product \\(x_i y_j\\) of size \\(2r\\) -bits is split not at \\(r\\) but at \\(52\\), so \\(k\\) product bits -are placed into the low half instead of the high half. This means -that the high half of the product cannot be directly accumulated onto -\\(z_{i+j+1}\\), but must first be multiplied by \\(2^k\\) (i.e., left -shifted by \\(k\\)). In addition, the low half of the product is -\\(52\\) bits large instead of \\(r\\) bits. - -## Handling offset product terms - -[Drucker and Gueron][2018_drucker_gueron] analyze the choice of radix -in the context of big-integer squaring, outlining three ways to handle -the offset product terms, before concluding that all of them are -suboptimal: - -1. Shift the results after accumulation; -2. Shift the input operands before multiplication; -3. Split the MAC operation, accumulating into a zeroed register, - shifting the result, and then adding. - -The first option is rejected because it could double-shift some -previously accumulated terms, the second doesn't work because the -inputs could become larger than \\(52\\) bits, and the third requires -additional instructions to handle the shifting and adding. - -Based on an analysis of total number of instructions, they suggest an -addition to the instruction set, which they call `FMSA` (fused -multiply-shift-add). This would shift the result according to an 8-bit -immediate value before accumulating it into the destination register. - -However, this change to the instruction set doesn't seem to be -necessary. Instead, the product terms can be grouped according to -their coefficients, accumulated together, then shifted once before -adding them to the final sum. This uses an extra register, shift, and -add, but only once per product term (accumulation target), not once -per source term (as in the Drucker-Gueron paper). - -Moreover, because IFMA instructions execute only on two ports -(presumably 0 and 1), while adds and shifts can execute on three ports -(0, 1, and 5), the adds and shifts can execute independently of the -IFMA operations, as long as there is not too much pressure on port 5. -This means that, although the total number of instructions increases, -the shifts and adds do not necessarily increase the execution time, as -long as throughput is limited by IFMA operations. - -Finally, because IFMA instructions have 4 cycle latency and 0.5/1 -cycle throughput (for 256/512 bit vectors), maximizing IFMA throughput -requires either 8 (for 256) or 4 (for 512) independent operations. So -accumulating groups of terms independently before adding them at the -end may be necessary anyways, in order to prevent long chains of -dependent instructions. - -## Advantages of a smaller radix - -Using a smaller radix has other advantages. Although radix \\(52\\) -is an unsaturated representation from the point of view of the -\\(64\\)-bit accumulators (because up to 4096 product terms can be -accumulated without carries), it's a saturated representation from the -point of view of the multiplier (since \\(52\\)-bit values are the -maximum input size). - -Because the inputs to a multiplication must have all of their limbs -bounded by \\(2^{52}\\), limbs in excess of \\(2^{52}\\) must be -reduced before they can be used as an input. The -[Gueron-Krasnov][2016_gueron_krasnov] paper suggests normalizing -values using a standard, sequential carry chain: for each limb, add -the carryin from reducing the previous limb, compute the carryout and -reduce the current limb, then move to the next limb. - -However, when using a smaller radix, such as \\(51\\), each limb can -store a carry bit and still be used as the input to a multiplication. -This means that the inputs do not need to be normalized, and instead -of using a sequential carry chain, we can compute all carryouts in -parallel, reduce all limbs in parallel, and then add the carryins in -parallel (possibly growing the limb values by one bit). - -Because the output of this partial reduction is an acceptable -multiplication input, we can "close the loop" using partial reductions -and never have to normalize to a canonical representation through the -entire computation, in contrast to the Gueron-Krasnov approach, which -converts back to a packed representation after every operation. (This -idea seems to trace back to at least as early as [this 1999 -paper][1999_walter]). - -Using \\(r = 51\\) is enough to keep a carry bit in each limb and -avoid normalizations. What about an even smaller radix? One reason -to choose a smaller radix would be to align the limb boundaries with -an inline reduction (for instance, choosing \\(r = 43\\) for the -Mersenne field \\(p = 2^{127} - 1\\)), but for \\(p = 2^{255 - 19}\\), -\\(r = 51 = 255/5\\) is the natural choice. - -# Multiplication - -The inputs to a multiplication are two field elements -\\[ -\begin{aligned} -x &= x_0 + x_1 2^{51} + x_2 2^{102} + x_3 2^{153} + x_4 2^{204} \\\\ -y &= y_0 + y_1 2^{51} + y_2 2^{102} + y_3 2^{153} + y_4 2^{204}, -\end{aligned} -\\] -with limbs in range \\([0,2^{52})\\). - -Writing the product terms as -\\[ -\begin{aligned} -z &= z_0 + z_1 2^{51} + z_2 2^{102} + z_3 2^{153} + z_4 2^{204} \\\\ - &+ z_5 2^{255} + z_6 2^{306} + z_7 2^{357} + z_8 2^{408} + z_9 2^{459}, -\end{aligned} -\\] -a schoolbook multiplication in product scanning form takes the form -\\[ -\begin{aligned} -z_0 &= x_0 y_0 \\\\ -z_1 &= x_1 y_0 + x_0 y_1 \\\\ -z_2 &= x_2 y_0 + x_1 y_1 + x_0 y_2 \\\\ -z_3 &= x_3 y_0 + x_2 y_1 + x_1 y_2 + x_0 y_3 \\\\ -z_4 &= x_4 y_0 + x_3 y_1 + x_2 y_2 + x_1 y_3 + x_0 y_4 \\\\ -z_5 &= x_4 y_1 + x_3 y_2 + x_2 y_3 + x_1 y_4 \\\\ -z_6 &= x_4 y_2 + x_3 y_3 + x_2 y_4 \\\\ -z_7 &= x_4 y_3 + x_3 y_4 \\\\ -z_8 &= x_4 y_4 \\\\ -z_9 &= 0 \\\\ -\end{aligned} -\\] -Each term \\(x_i y_j\\) can be written in terms of IFMA operations as -\\[ -x_i y_j = \mathrm{lo}(x_i,y_j) + 2\mathrm{hi}(x_i,y_j)2^{51}. -\\] -Substituting this equation into the schoolbook multiplication, then -moving terms to eliminate the \\(2^{51}\\) factors gives -\\[ -\begin{aligned} -z_0 &= \mathrm{lo}(x_0, y_0) \\\\ - &+ \qquad 0 \\\\ -z_1 &= \mathrm{lo}(x_1, y_0) + \mathrm{lo}(x_0, y_1) \\\\ - &+ \qquad 2( \mathrm{hi}(x_0, y_0) )\\\\ -z_2 &= \mathrm{lo}(x_2, y_0) + \mathrm{lo}(x_1, y_1) + \mathrm{lo}(x_0, y_2) \\\\ - &+ \qquad 2( \mathrm{hi}(x_1, y_0) + \mathrm{hi}(x_0, y_1) )\\\\ -z_3 &= \mathrm{lo}(x_3, y_0) + \mathrm{lo}(x_2, y_1) + \mathrm{lo}(x_1, y_2) + \mathrm{lo}(x_0, y_3) \\\\ - &+ \qquad 2( \mathrm{hi}(x_2, y_0) + \mathrm{hi}(x_1, y_1) + \mathrm{hi}(x_0, y_2) )\\\\ -z_4 &= \mathrm{lo}(x_4, y_0) + \mathrm{lo}(x_3, y_1) + \mathrm{lo}(x_2, y_2) + \mathrm{lo}(x_1, y_3) + \mathrm{lo}(x_0, y_4) \\\\ - &+ \qquad 2( \mathrm{hi}(x_3, y_0) + \mathrm{hi}(x_2, y_1) + \mathrm{hi}(x_1, y_2) + \mathrm{hi}(x_0, y_3) )\\\\ -z_5 &= \mathrm{lo}(x_4, y_1) + \mathrm{lo}(x_3, y_2) + \mathrm{lo}(x_2, y_3) + \mathrm{lo}(x_1, y_4) \\\\ - &+ \qquad 2( \mathrm{hi}(x_4, y_0) + \mathrm{hi}(x_3, y_1) + \mathrm{hi}(x_2, y_2) + \mathrm{hi}(x_1, y_3) + \mathrm{hi}(x_0, y_4) )\\\\ -z_6 &= \mathrm{lo}(x_4, y_2) + \mathrm{lo}(x_3, y_3) + \mathrm{lo}(x_2, y_4) \\\\ - &+ \qquad 2( \mathrm{hi}(x_4, y_1) + \mathrm{hi}(x_3, y_2) + \mathrm{hi}(x_2, y_3) + \mathrm{hi}(x_1, y_4) )\\\\ -z_7 &= \mathrm{lo}(x_4, y_3) + \mathrm{lo}(x_3, y_4) \\\\ - &+ \qquad 2( \mathrm{hi}(x_4, y_2) + \mathrm{hi}(x_3, y_3) + \mathrm{hi}(x_2, y_4) )\\\\ -z_8 &= \mathrm{lo}(x_4, y_4) \\\\ - &+ \qquad 2( \mathrm{hi}(x_4, y_3) + \mathrm{hi}(x_3, y_4) )\\\\ -z_9 &= 0 \\\\ - &+ \qquad 2( \mathrm{hi}(x_4, y_4) )\\\\ -\end{aligned} -\\] -As noted above, our strategy will be to multiply and accumulate the -terms with coefficient \\(2\\) separately from those with coefficient -\\(1\\), before combining them at the end. This can alternately be -thought of as accumulating product terms into a *doubly-redundant* -representation, with two limbs for each digit, before collapsing -the doubly-redundant representation by shifts and adds. - -This computation requires 25 `vpmadd52luq` and 25 `vpmadd52huq` -operations. For 256-bit vectors, IFMA operations execute on an -i3-8121U with latency 4 cycles, throughput 0.5 cycles, so executing 50 -instructions requires 25 cycles' worth of throughput. Accumulating -terms with coefficient \\(1\\) and \\(2\\) seperately means that the -longest dependency chain has length 5, so the critical path has length -20 cycles and the bottleneck is throughput. - -# Reduction modulo \\(p\\) - -The next question is how to handle the reduction modulo \\(p\\). -Because \\(p = 2^{255} - 19\\), \\(2^{255} = 19 \pmod p\\), so we can -alternately write -\\[ -\begin{aligned} -z &= z_0 + z_1 2^{51} + z_2 2^{102} + z_3 2^{153} + z_4 2^{204} \\\\ - &+ z_5 2^{255} + z_6 2^{306} + z_7 2^{357} + z_8 2^{408} + z_9 2^{459} -\end{aligned} -\\] -as -\\[ -\begin{aligned} -z &= (z_0 + 19z_5) + (z_1 + 19z_6) 2^{51} + (z_2 + 19z_7) 2^{102} + (z_3 + 19z_8) 2^{153} + (z_4 + 19z_9) 2^{204}. -\end{aligned} -\\] -When using a \\(64 \times 64 \rightarrow 128\\)-bit multiplier, this -can be handled (as in [Ed25519][ed25519_paper]) by premultiplying -source terms by \\(19\\). Since \\(\lg(19) < 4.25\\), this increases -their size by less than \\(4.25\\) bits, and the rest of the -multiplication can be shown to work out. - -Here, we have at most \\(1\\) bit of headroom. In order to allow -premultiplication, we would need to use radix \\(2^{47}\\), which -would require six limbs instead of five. Instead, we compute the high -terms \\(z_5, \ldots, z_9\\), each using two chains of IFMA -operations, then multiply by \\(19\\) and combine with the lower terms -\\(z_0, \ldots, z_4\\). There are two ways to perform the -multiplication by \\(19\\): using more IFMA operations, or using the -`vpmullq` instruction, which computes the low \\(64\\) bits of a \\(64 -\times 64\\)-bit product. However, `vpmullq` has 15c/1.5c -latency/throughput, in contrast to the 4c/0.5c latency/throughput of -IFMA operations, so it seems like a worse choice. - -The high terms \\(z_5, \ldots, z_9\\) are sums of \\(52\\)-bit terms, -so they are larger than \\(52\\) bits. Write these terms in radix \\(52\\) as -\\[ -z_{5+i} = z_{5+i}' + z_{5+i}'' 2^{52}, \qquad z_{5+i}' < 2^{52}. -\\] -Then the contribution of \\(z_{5+i}\\), taken modulo \\(p\\), is -\\[ -\begin{aligned} -z_{5+i} 2^{255} 2^{51 i} -&= -19 (z_{5+i}' + z_{5+i}'' 2^{52}) 2^{51 i} -\\\\ -&= -19 z_{5+i}' 2^{51 i} + 2 \cdot 19 z_{5+i}'' 2^{51 (i+1)} -\\\\ -\end{aligned} -\\] -The products \\(19 z_{5+i}', 19 z_{5+i}''\\) can be written in terms of IFMA operations as -\\[ -\begin{aligned} -19 z_{5+i}' &= \mathrm{lo}(19, z_{5+i}') + 2 \mathrm{hi}(19, z_{5+i}') 2^{51}, \\\\ -19 z_{5+i}'' &= \mathrm{lo}(19, z_{5+i}'') + 2 \mathrm{hi}(19, z_{5+i}'') 2^{51}. \\\\ -\end{aligned} -\\] -Because \\(z_{5+i} < 2^{64}\\), \\(z_{5+i}'' < 2^{12} \\), so \\(19 -z_{5+i}'' < 2^{17} < 2^{52} \\) and \\(\mathrm{hi}(19, z_{5+i}'') = 0\\). -Because IFMA operations ignore the high bits of their source -operands, we do not need to compute \\(z\_{5+i}'\\) explicitly: -the high bits will be ignored. -Combining these observations, we can write -\\[ -\begin{aligned} -z_{5+i} 2^{255} 2^{51 i} -&= -19 z_{5+i}' 2^{51 i} + 2 \cdot 19 z_{5+i}'' 2^{51 (i+1)} -\\\\ -&= -\mathrm{lo}(19, z_{5+i}) 2^{51 i} -\+ 2 \mathrm{hi}(19, z_{5+i}) 2^{51 (i+1)} -\+ 2 \mathrm{lo}(19, z_{5+i}/2^{52}) 2^{51 (i+1)}. -\end{aligned} -\\] - -For \\(i = 0,1,2,3\\), this allows reducing \\(z_{5+i}\\) onto -\\(z_{i}, z_{i+1}\\), and if the low terms are computed using a -doubly-redundant representation, no additional shifts are needed to -handle the \\(2\\) coefficients. For \\(i = 4\\), there's a -complication: the contribution becomes -\\[ -\begin{aligned} -z_{9} 2^{255} 2^{204} -&= -\mathrm{lo}(19, z_{9}) 2^{204} -\+ 2 \mathrm{hi}(19, z_{9}) 2^{255} -\+ 2 \mathrm{lo}(19, z_{9}/2^{52}) 2^{255} -\\\\ -&= -\mathrm{lo}(19, z_{9}) 2^{204} -\+ 2 \mathrm{hi}(19, z_{9}) 19 -\+ 2 \mathrm{lo}(19, z_{9}/2^{52}) 19 -\\\\ -&= -\mathrm{lo}(19, z_{9}) 2^{204} -\+ 2 -\mathrm{lo}(19, \mathrm{hi}(19, z_{9}) + \mathrm{lo}(19, z_{9}/2^{52})). -\\\\ -\end{aligned} -\\] - -It would be possible to cut the number of multiplications from 3 to 2 -by carrying the high part of each \\(z_i\\) onto \\(z_{i+1}\\). This -would eliminate 5 multiplications, clearing 2.5 cycles of port -pressure, at the cost of 5 additions, adding 1.66 cycles of port -pressure. But doing this would create a dependency between terms -(e.g., \\(z_{5}\\) must be computed before the reduction of -\\(z_{6}\\) can begin), whereas with the approach above, all -contributions to all terms are computed independently, to maximize ILP -and flexibility for the processor to schedule instructions. - -This strategy performs 16 IFMA operations, adding two IFMA operations -to each of the \\(2\\)-coefficient terms and one to each of the -\\(1\\)-coefficient terms. Considering the multiplication and -reduction together, we use 66 IFMA operations, requiring 33 cycles' -throughput, while the longest chain of IFMA operations is in the -reduction of \\(z_5\\) onto \\(z_1\\), of length 7 (so 28 cycles, plus -2 cycles to combine the two parts of \\(z_5\\), and the bottleneck is -again throughput. - -Once this is done, we have computed the product terms -\\[ -z = z_0 + z_1 2^{51} + z_2 2^{102} + z_3 2^{153} + z_4 2^{204}, -\\] -without reducing the \\(z_i\\) to fit in \\(52\\) bits. Because the -overall flow of operations alternates multiplications and additions or -subtractions, we would have to perform a reduction after an addition -but before the next multiplication anyways, so there's no benefit to -fully reducing the limbs at the end of a multiplication. Instead, we -leave them unreduced, and track the reduction state using the type -system to ensure that unreduced limbs are not accidentally used as an -input to a multiplication. - -# Squaring - -Squaring operates similarly to multiplication, but with the -possibility to combine identical terms. -As before, we write the input as -\\[ -\begin{aligned} -x &= x_0 + x_1 2^{51} + x_2 2^{102} + x_3 2^{153} + x_4 2^{204} -\end{aligned} -\\] -with limbs in range \\([0,2^{52})\\). -Writing the product terms as -\\[ -\begin{aligned} -z &= z_0 + z_1 2^{51} + z_2 2^{102} + z_3 2^{153} + z_4 2^{204} \\\\ - &+ z_5 2^{255} + z_6 2^{306} + z_7 2^{357} + z_8 2^{408} + z_9 2^{459}, -\end{aligned} -\\] -a schoolbook squaring in product scanning form takes the form -\\[ -\begin{aligned} -z_0 &= x_0 x_0 \\\\ -z_1 &= 2 x_1 x_0 \\\\ -z_2 &= 2 x_2 x_0 + x_1 x_1 \\\\ -z_3 &= 2 x_3 x_0 + 2 x_2 x_1 \\\\ -z_4 &= 2 x_4 x_0 + 2 x_3 x_1 + x_2 x_2 \\\\ -z_5 &= 2 x_4 x_1 + 2 x_3 x_2 \\\\ -z_6 &= 2 x_4 x_2 + x_3 x_3 \\\\ -z_7 &= 2 x_4 x_3 \\\\ -z_8 &= x_4 x_4 \\\\ -z_9 &= 0 \\\\ -\end{aligned} -\\] -As before, we write \\(x_i x_j\\) as -\\[ -x_i x_j = \mathrm{lo}(x_i,x_j) + 2\mathrm{hi}(x_i,x_j)2^{51}, -\\] -and substitute to obtain -\\[ -\begin{aligned} -z_0 &= \mathrm{lo}(x_0, x_0) + 0 \\\\ -z_1 &= 2 \mathrm{lo}(x_1, x_0) + 2 \mathrm{hi}(x_0, x_0) \\\\ -z_2 &= 2 \mathrm{lo}(x_2, x_0) + \mathrm{lo}(x_1, x_1) + 4 \mathrm{hi}(x_1, x_0) \\\\ -z_3 &= 2 \mathrm{lo}(x_3, x_0) + 2 \mathrm{lo}(x_2, x_1) + 4 \mathrm{hi}(x_2, x_0) + 2 \mathrm{hi}(x_1, x_1) \\\\ -z_4 &= 2 \mathrm{lo}(x_4, x_0) + 2 \mathrm{lo}(x_3, x_1) + \mathrm{lo}(x_2, x_2) + 4 \mathrm{hi}(x_3, x_0) + 4 \mathrm{hi}(x_2, x_1) \\\\ -z_5 &= 2 \mathrm{lo}(x_4, x_1) + 2 \mathrm{lo}(x_3, x_2) + 4 \mathrm{hi}(x_4, x_0) + 4 \mathrm{hi}(x_3, x_1) + 2 \mathrm{hi}(x_2, x_2) \\\\ -z_6 &= 2 \mathrm{lo}(x_4, x_2) + \mathrm{lo}(x_3, x_3) + 4 \mathrm{hi}(x_4, x_1) + 4 \mathrm{hi}(x_3, x_2) \\\\ -z_7 &= 2 \mathrm{lo}(x_4, x_3) + 4 \mathrm{hi}(x_4, x_2) + 2 \mathrm{hi}(x_3, x_3) \\\\ -z_8 &= \mathrm{lo}(x_4, x_4) + 4 \mathrm{hi}(x_4, x_3) \\\\ -z_9 &= 0 + 2 \mathrm{hi}(x_4, x_4) \\\\ -\end{aligned} -\\] -To implement these, we group terms by their coefficient, computing -those with coefficient \\(2\\) on set of IFMA chains, and on another -set of chains, we begin with coefficient-\\(4\\) terms, then shift -left before continuing with the coefficient-\\(1\\) terms. -The reduction strategy is the same as for multiplication. - -# Future improvements - -LLVM won't use blend operations on [256-bit vectors yet][llvm_blend], -so there's a bunch of blend instructions that could be omitted. - -Although the multiplications and squarings are much faster, there's no -speedup to the additions and subtractions, so there are diminishing -returns. In fact, the complications in the doubling formulas mean -that doubling is actually slower than readdition. This also suggests -that moving to 512-bit vectors won't be much help for a strategy aimed -at parallelism within a group operation, so to extract performance -gains from 512-bit vectors it will probably be necessary to create a -parallel-friendly multiscalar multiplication algorithm. This could -also help with reducing shuffle pressure. - -The squaring implementation could probably be optimized, but without -`perf` support on Cannonlake it's difficult to make actual -measurements. - -Another improvement would be to implement vectorized square root -computations, which would allow creating an iterator adaptor for point -decompression that bunched decompression operations and executed them -in parallel. This would accelerate batch verification. - -[2016_gueron_krasnov]: https://ieeexplore.ieee.org/document/7563269 -[2018_drucker_gueron]: https://eprint.iacr.org/2018/335 -[1999_walter]: https://pdfs.semanticscholar.org/0e6a/3e8f30b63b556679f5dff2cbfdfe9523f4fa.pdf -[ed25519_paper]: https://ed25519.cr.yp.to/ed25519-20110926.pdf -[llvm_blend]: https://bugs.llvm.org/show_bug.cgi?id=38343 diff --git a/docs/parallel-formulas.md b/docs/parallel-formulas.md deleted file mode 100644 index f84d1ccd..00000000 --- a/docs/parallel-formulas.md +++ /dev/null @@ -1,333 +0,0 @@ -Vectorized implementations of field and point operations, using a -modification of the 4-way parallel formulas of Hisil, Wong, Carter, -and Dawson. - -These notes explain the parallel formulas and our strategy for using -them with SIMD operations. There are two backend implementations: one -using AVX2, and the other using AVX512-IFMA. - -# Overview - -The 2008 paper [_Twisted Edwards Curves Revisited_][hwcd08] by Hisil, -Wong, Carter, and Dawson (HWCD) introduced the “extended coordinates” -and mixed-model representations which are used by most Edwards curve -implementations. - -However, they also describe 4-way parallel formulas for point addition -and doubling: a unified addition algorithm taking an effective -\\(2\mathbf M + 1\mathbf D\\), a doubling algorithm taking an -effective \\(1\mathbf M + 1\mathbf S\\), and a dedicated (i.e., for -distinct points) addition algorithm taking an effective \\(2 \mathbf M -\\). They compare these formulas with a 2-way parallel variant of the -Montgomery ladder. - -Unlike their serial formulas, which are used widely, their parallel -formulas do not seem to have been implemented in software before. The -2-way parallel Montgomery ladder was used in 2015 by Tung Chou's -`sandy2x` implementation. Curiously, however, although the [`sandy2x` -paper][sandy2x] also implements Edwards arithmetic, and cites HWCD08, -it doesn't mention their parallel Edwards formulas. -A 2015 paper by Hernández and López describes an AVX2 implementation -of X25519. Neither the paper nor the code are publicly available, but -it apparently gives only a [slight speedup][avx2trac], suggesting that -it uses a 4-way parallel Montgomery ladder rather than parallel -Edwards formulas. - -The reason may be that HWCD08 describe their formulas as operating on -four independent processors, which would make a software -implementation impractical: all of the operations are too low-latency -to effectively synchronize. But a closer inspection reveals that the -(more expensive) multiplication and squaring steps are uniform, while -the instruction divergence occurs in the (much cheaper) addition and -subtraction steps. This means that a SIMD implementation can perform -the expensive steps uniformly, and handle divergence in the -inexpensive steps using masking. - -These notes describe modifications to the original parallel formulas -to allow a SIMD implementation, and this module contains -implementations of the modified formulas targeting either AVX2 or -AVX512-IFMA. - -# Parallel formulas in HWCD'08 - -The doubling formula is presented in the HWCD paper as follows: - -| Cost | Processor 1 | Processor 2 | Processor 3 | Processor 4 | -|------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------| -| | idle | idle | idle | \\( R\_1 \gets X\_1 + Y\_1 \\) | -| \\(1\mathbf S\\) | \\( R\_2 \gets X\_1\^2 \\) | \\( R\_3 \gets Y\_1\^2 \\) | \\( R\_4 \gets Z\_1\^2 \\) | \\( R\_5 \gets R\_1\^2 \\) | -| | \\( R\_6 \gets R\_2 + R\_3 \\) | \\( R\_7 \gets R\_2 - R\_3 \\) | \\( R\_4 \gets 2 R\_4 \\) | idle | -| | idle | \\( R\_1 \gets R\_4 + R\_7 \\) | idle | \\( R\_2 \gets R\_6 - R\_5 \\) | -| \\(1\mathbf M\\) | \\( X\_3 \gets R\_1 R\_2 \\) | \\( Y\_3 \gets R\_6 R\_7 \\) | \\( T\_3 \gets R\_2 R\_6 \\) | \\( Z\_3 \gets R\_1 R\_7 \\) | - -and the unified addition algorithm is presented as follows: - -| Cost | Processor 1 | Processor 2 | Processor 3 | Processor 4 | -|------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------| -| | \\( R\_1 \gets Y\_1 - X\_1 \\) | \\( R\_2 \gets Y\_2 - X\_2 \\) | \\( R\_3 \gets Y\_1 + X\_1 \\) | \\( R\_4 \gets Y\_2 + X\_2 \\) | -| \\(1\mathbf M\\) | \\( R\_5 \gets R\_1 R\_2 \\) | \\( R\_6 \gets R\_3 R\_4 \\) | \\( R\_7 \gets T\_1 T\_2 \\) | \\( R\_8 \gets Z\_1 Z\_2 \\) | -| \\(1\mathbf D\\) | idle | idle | \\( R\_7 \gets k R\_7 \\) | \\( R\_8 \gets 2 R\_8 \\) | -| | \\( R\_1 \gets R\_6 - R\_5 \\) | \\( R\_2 \gets R\_8 - R\_7 \\) | \\( R\_3 \gets R\_8 + R\_7 \\) | \\( R\_4 \gets R\_6 + R\_5 \\) | -| \\(1\mathbf M\\) | \\( X\_3 \gets R\_1 R\_2 \\) | \\( Y\_3 \gets R\_3 R\_4 \\) | \\( T\_3 \gets R\_1 R\_4 \\) | \\( Z\_3 \gets R\_2 R\_3 \\) | - -Here \\(\mathbf M\\) and \\(\mathbf S\\) represent the cost of -multiplication and squaring of generic field elements, \\(\mathbf D\\) -represents the cost of multiplication by a curve constant (in this -case \\( k = 2d \\)). - -Notice that the \\(1\mathbf M\\) and \\(1\mathbf S\\) steps are -uniform. The non-uniform steps are all inexpensive additions or -subtractions, with the exception of the multiplication by the curve -constant \\(k = 2d\\): -$$ -R\_7 \gets 2 d R\_7. -$$ - -HWCD suggest parallelising this step by breaking \\(k = 2d\\) into four -parts as \\(k = k_0 + 2\^n k_1 + 2\^{2n} k_2 + 2\^{3n} k_3 \\) and -computing \\(k_i R_7 \\) in parallel. This is quite awkward, but if -the curve constant is a ratio \\( d = d\_1/d\_2 \\), then projective -coordinates allow us to instead compute -$$ -(R\_5, R\_6, R\_7, R\_8) \gets (d\_2 R\_5, d\_2 R\_6, 2d\_1 R\_7, d\_2 R\_8). -$$ -This can be performed as a uniform multiplication by a vector of -constants, and if \\(d\_1, d\_2\\) are small, it is relatively -inexpensive. (This trick was suggested by Mike Hamburg). -In the Curve25519 case, we have -$$ -d = \frac{d\_1}{d\_2} = \frac{-121665}{121666}; -$$ -Since \\(2 \cdot 121666 < 2\^{18}\\), all the constants above fit (up -to sign) in 32 bits, so this can be done in parallel as four -multiplications by small constants \\( (121666, 121666, 2\cdot 121665, -2\cdot 121666) \\), followed by a negation to compute \\( - 2\cdot 121665\\). - -# Modified parallel formulas - -Using the modifications sketched above, we can write SIMD-friendly -versions of the parallel formulas as follows. To avoid confusion with -the original formulas, temporary variables are named \\(S\\) instead -of \\(R\\) and are in static single-assignment form. - -## Addition - -To add points -\\(P_1 = (X_1 : Y_1 : Z_1 : T_1) \\) -and -\\(P_2 = (X_2 : Y_2 : Z_2 : T_2 ) \\), -we compute -$$ -\begin{aligned} -(S\_0 &&,&& S\_1 &&,&& S\_2 &&,&& S\_3 ) -&\gets -(Y\_1 - X\_1&&,&& Y\_1 + X\_1&&,&& Y\_2 - X\_2&&,&& Y\_2 + X\_2) -\\\\ -(S\_4 &&,&& S\_5 &&,&& S\_6 &&,&& S\_7 ) -&\gets -(S\_0 \cdot S\_2&&,&& S\_1 \cdot S\_3&&,&& Z\_1 \cdot Z\_2&&,&& T\_1 \cdot T\_2) -\\\\ -(S\_8 &&,&& S\_9 &&,&& S\_{10} &&,&& S\_{11} ) -&\gets -(d\_2 \cdot S\_4 &&,&& d\_2 \cdot S\_5 &&,&& 2 d\_2 \cdot S\_6 &&,&& 2 d\_1 \cdot S\_7 ) -\\\\ -(S\_{12} &&,&& S\_{13} &&,&& S\_{14} &&,&& S\_{15}) -&\gets -(S\_9 - S\_8&&,&& S\_9 + S\_8&&,&& S\_{10} - S\_{11}&&,&& S\_{10} + S\_{11}) -\\\\ -(X\_3&&,&& Y\_3&&,&& Z\_3&&,&& T\_3) -&\gets -(S\_{12} \cdot S\_{14}&&,&& S\_{15} \cdot S\_{13}&&,&& S\_{15} \cdot S\_{14}&&,&& S\_{12} \cdot S\_{13}) -\end{aligned} -$$ -to obtain \\( P\_3 = (X\_3 : Y\_3 : Z\_3 : T\_3) = P\_1 + P\_2 \\). -This costs \\( 2\mathbf M + 1 \mathbf D\\). - -## Readdition - -If the point \\( P_2 = (X\_2 : Y\_2 : Z\_2 : T\_2) \\) is fixed, we -can cache the multiplication of the curve constants by computing -$$ -\begin{aligned} -(S\_2' &&,&& S\_3' &&,&& Z\_2' &&,&& T\_2' ) -&\gets -(d\_2 \cdot (Y\_2 - X\_2)&&,&& d\_2 \cdot (Y\_1 + X\_1)&&,&& 2d\_2 \cdot Z\_2 &&,&& 2d\_1 \cdot T\_2). -\end{aligned} -$$ -This costs \\( 1\mathbf D\\); with \\( (S\_2', S\_3', Z\_2', T\_2')\\) -in hand, the addition formulas above become -$$ -\begin{aligned} -(S\_0 &&,&& S\_1 &&,&& Z\_1 &&,&& T\_1 ) -&\gets -(Y\_1 - X\_1&&,&& Y\_1 + X\_1&&,&& Z\_1 &&,&& T\_1) -\\\\ -(S\_8 &&,&& S\_9 &&,&& S\_{10} &&,&& S\_{11} ) -&\gets -(S\_0 \cdot S\_2' &&,&& S\_1 \cdot S\_3'&&,&& Z\_1 \cdot Z\_2' &&,&& T\_1 \cdot T\_2') -\\\\ -(S\_{12} &&,&& S\_{13} &&,&& S\_{14} &&,&& S\_{15}) -&\gets -(S\_9 - S\_8&&,&& S\_9 + S\_8&&,&& S\_{10} - S\_{11}&&,&& S\_{10} + S\_{11}) -\\\\ -(X\_3&&,&& Y\_3&&,&& Z\_3&&,&& T\_3) -&\gets -(S\_{12} \cdot S\_{14}&&,&& S\_{15} \cdot S\_{13}&&,&& S\_{15} \cdot S\_{14}&&,&& S\_{12} \cdot S\_{13}) -\end{aligned} -$$ -which costs only \\( 2\mathbf M \\). This precomputation is -essentially similar to the precomputation that HWCD suggest for their -serial formulas. Because the cost of precomputation and then -readdition is the same as addition, it's sufficient to only -implement caching and readdition. - -## Doubling - -The non-uniform portions of the (re)addition formulas have a fairly -regular structure. Unfortunately, this is not the case for the -doubling formulas, which are much less nice. - -To double a point \\( P = (X\_1 : Y\_1 : Z\_1 : T\_1) \\), we compute -$$ -\begin{aligned} -(X\_1 &&,&& Y\_1 &&,&& Z\_1 &&,&& S\_0) -&\gets -(X\_1 &&,&& Y\_1 &&,&& Z\_1 &&,&& X\_1 + Y\_1) -\\\\ -(S\_1 &&,&& S\_2 &&,&& S\_3 &&,&& S\_4 ) -&\gets -(X\_1\^2 &&,&& Y\_1\^2&&,&& Z\_1\^2 &&,&& S\_0\^2) -\\\\ -(S\_5 &&,&& S\_6 &&,&& S\_8 &&,&& S\_9 ) -&\gets -(S\_1 + S\_2 &&,&& S\_1 - S\_2 &&,&& S\_1 + 2S\_3 - S\_2 &&,&& S\_1 + S\_2 - S\_4) -\\\\ -(X\_3 &&,&& Y\_3 &&,&& Z\_3 &&,&& T\_3 ) -&\gets -(S\_8 \cdot S\_9 &&,&& S\_5 \cdot S\_6 &&,&& S\_8 \cdot S\_6 &&,&& S\_5 \cdot S\_9) -\end{aligned} -$$ -to obtain \\( P\_3 = (X\_3 : Y\_3 : Z\_3 : T\_3) = [2]P\_1 \\). - -The intermediate step between the squaring and multiplication requires -a long chain of additions. For the IFMA-based implementation, this is not a problem; for the AVX2-based implementation, it is, but with some care and finesse, it's possible to arrange the computation without requiring an intermediate reduction. - -# Implementation - -These formulas aren't specific to a particular representation of field -element vectors, whose optimum choice is determined by the details of -the instruction set. However, it's not possible to perfectly separate -the implementation of the field element vectors from the -implementation of the point operations. Instead, the [`avx2`] and -[`ifma`] backends provide `ExtendedPoint` and `CachedPoint` types, and -the [`scalar_mul`] code uses one of the backend types by a type alias. - -# Comparison to non-vectorized formulas - -In theory, the parallel Edwards formulas seem to allow a \\(4\\)-way -speedup from parallelism. However, an actual vectorized -implementation has several slowdowns that cut into this speedup. - -First, the parallel formulas can only use the available vector -multiplier. For AVX2, this is a \\( 32 \times 32 \rightarrow 64 -\\)-bit integer multiplier, so the speedup from vectorization must -overcome the disadvantage of losing the \\( 64 \times 64 \rightarrow -128\\)-bit (serial) integer multiplier. The effect of this slowdown -is microarchitecture-dependent, since it requires accounting for the -total number of multiplications and additions and their relative -costs. IFMA allows using a \\( 52 \times 52 \rightarrow 104 \\)-bit -multiplier, but the high and low halves need to be computed -separately, and the reduction requires extra work because it's not -possible to pre-multiply by \\(19\\). - -Second, the parallel doubling formulas incur both a theoretical and -practical slowdown. The parallel formulas described above work on the -\\( \mathbb P\^3 \\) “extended” coordinates. The \\( \mathbb P\^2 \\) -model introduced earlier by [Bernstein, Birkner, Joye, Lange, and -Peters][bbjlp08] allows slightly faster doublings, so HWCD suggest -mixing coordinate systems while performing scalar multiplication -(attributing the idea to [a 1998 paper][cmo98] by Cohen, Miyagi, and -Ono). The \\( T \\) coordinate is not required for doublings, so when -doublings are followed by doublings, its computation can be skipped. -More details on this approach and the different coordinate systems can -be found in the [`curve_models` module documentation][curve_models]. - -Unfortunately, this optimization is not compatible with the parallel -formulas, which cannot save time by skipping a single variable, so the -parallel doubling formulas do slightly more work when counting the -total number of field multiplications and squarings. - -In addition, the parallel doubling formulas have a less regular -pattern of additions and subtractions than the parallel addition -formulas, so the vectorization overhead is proportionately greater. -Both the parallel addition and parallel doubling formulas also require -some shuffling to rearrange data within the vectors, which places more -pressure on the shuffle unit than is desirable. - -This means that the speedup from using a vectorized implementation of -parallel Edwards formulas is likely to be greatest in applications -that do fewer doublings and more additions (like a large multiscalar -multiplication) rather than applications that do fewer additions and -more doublings (like a double-base scalar multiplication). - -Third, Amdahl's law says that the speedup is limited to the portion -which can be parallelized. Normally, the field multiplications -dominate the cost of point operations, but with the IFMA backend, the -multiplications are so fast that the non-parallel additions end up as -a significant portion of the total time. - -Fourth, current Intel CPUs perform thermal throttling when using wide -vector instructions. A detailed description can be found in §15.26 of -[the Intel Optimization Manual][intel], but using wide vector -instructions prevents the core from operating at higher frequencies. -The core can return to the higher-frequency state after 2 -milliseconds, but this timer is reset every time high-power -instructions are used. - -Any speedup from vectorization therefore has to be weighed against a -slowdown for the next few million instructions. For a mixed workload, -where point operations are interspersed with other tasks, this can -reduce overall performance. This implementation is therefore probably -not suitable for basic applications, like signatures, but is -worthwhile for complex applications, like zero-knowledge proofs, which -do sustained work. - -# Future work - -There are several directions for future improvement: - -* Using the vectorized field arithmetic code to parallelize across - point operations rather than within a single point operation. This - is less flexible, but would give a speedup both from allowing use of - the faster mixed-model arithmetic and from reducing shuffle - pressure. One approach in this direction would be to implement - batched scalar-point operations using vectors of points (AoSoA - layout). This less generally useful but would give a speedup for - Bulletproofs. - -* Extending the IFMA implementation to use the full width of AVX512, - either handling the extra parallelism internally to a single point - operation (by using a 2-way parallel implementation of field - arithmetic instead of a wordsliced one), or externally, - parallelizing across point operations. Internal parallelism would - be preferable but might require too much shuffle pressure. For now, - the only available CPU which runs IFMA operations executes them at - 256-bits wide anyways, so this isn't yet important. - -* Generalizing the implementation to NEON instructions. The current - point arithmetic code is written in terms of field element vectors, - which are in turn implemented using platform SIMD vectors. It - should be possible to write an alternate implementation of the - `FieldElement2625x4` using NEON without changing the point - arithmetic. NEON has 128-bit vectors rather than 256-bit vectors, - but this may still be worthwhile compared to a serial - implementation. - - -[sandy2x]: https://eprint.iacr.org/2015/943.pdf -[avx2trac]: https://trac.torproject.org/projects/tor/ticket/8897#comment:28 -[hwcd08]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf -[curve_models]: https://doc-internal.dalek.rs/curve25519_dalek/backend/serial/curve_models/index.html -[bbjlp08]: https://eprint.iacr.org/2008/013 -[cmo98]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf -[intel]: https://software.intel.com/sites/default/files/managed/9e/bc/64-ia-32-architectures-optimization-manual.pdf diff --git a/src/constants.rs b/src/constants.rs index 90bf38ac..0d932a3f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -16,8 +16,8 @@ //! scope using a `let` binding: //! //! ``` -//! use curve25519_dalek::constants; -//! use curve25519_dalek::traits::IsIdentity; +//! use noah_curve25519_dalek::constants; +//! use noah_curve25519_dalek::traits::IsIdentity; //! //! let B = &constants::RISTRETTO_BASEPOINT_TABLE; //! let l = &constants::BASEPOINT_ORDER; diff --git a/src/edwards.rs b/src/edwards.rs index 8583ea05..c5176987 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1161,7 +1161,7 @@ impl EdwardsPoint { /// # Example /// /// ``` - /// use curve25519_dalek::constants; + /// use noah_curve25519_dalek::constants; /// /// // Generator of the prime-order subgroup /// let P = constants::ED25519_BASEPOINT_POINT; @@ -1191,7 +1191,7 @@ impl EdwardsPoint { /// # Example /// /// ``` - /// use curve25519_dalek::constants; + /// use noah_curve25519_dalek::constants; /// /// // Generator of the prime-order subgroup /// let P = constants::ED25519_BASEPOINT_POINT; diff --git a/src/field.rs b/src/field.rs index 95ea3ae1..385f899f 100644 --- a/src/field.rs +++ b/src/field.rs @@ -150,7 +150,7 @@ impl FieldElement { /// Given a slice of public `FieldElements`, replace each with its inverse. /// - /// All input `FieldElements` **MUST** be nonzero. + /// When an input `FieldElement` is zero, its value is unchanged. #[cfg(feature = "alloc")] pub fn batch_invert(inputs: &mut [FieldElement]) { // Montgomery’s Trick and Fast Implementation of Masked AES @@ -167,10 +167,11 @@ impl FieldElement { // products in the scratch space for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) { *scratch = acc; - acc = &acc * input; + // acc <- acc * input, but skipping zeros (constant-time) + acc.conditional_assign(&(&acc * input), !input.is_zero()); } - // acc is nonzero iff all inputs are nonzero + // acc is nonzero because we skipped zeros in inputs assert_eq!(acc.is_zero().unwrap_u8(), 0); // Compute the inverse of all products @@ -180,8 +181,11 @@ impl FieldElement { // in place for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) { let tmp = &acc * input; - *input = &acc * &scratch; - acc = tmp; + // input <- acc * scratch, then acc <- tmp + // Again, we skip zeros in a constant-time way + let nz = !input.is_zero(); + input.conditional_assign(&(&acc * &scratch), nz); + acc.conditional_assign(&tmp, nz); } } @@ -362,11 +366,12 @@ mod test { let ap58 = FieldElement::from_bytes(&AP58_BYTES); let asq = FieldElement::from_bytes(&ASQ_BYTES); let ainv = FieldElement::from_bytes(&AINV_BYTES); + let a0 = &a - &a; let a2 = &a + &a; - let a_list = vec![a, ap58, asq, ainv, a2]; + let a_list = vec![a, ap58, asq, ainv, a0, a2]; let mut ainv_list = a_list.clone(); FieldElement::batch_invert(&mut ainv_list[..]); - for i in 0..5 { + for i in 0..6 { assert_eq!(a_list[i].invert(), ainv_list[i]); } } diff --git a/src/lib.rs b/src/lib.rs index 5fa30c18..dd7ad5cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,234 +15,13 @@ #![cfg_attr(feature = "simd_backend", feature(stdsimd))] // Refuse to compile if documentation is missing. #![deny(missing_docs)] -#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] -#![doc(html_root_url = "https://docs.rs/curve25519-dalek/3.2.0")] -//! # curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://img.shields.io/badge/dynamic/json.svg?label=docs&uri=https%3A%2F%2Fcrates.io%2Fapi%2Fv1%2Fcrates%2Fcurve25519-dalek%2Fversions&query=%24.versions%5B0%5D.num&colorB=4F74A6)](https://doc.dalek.rs) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) -//! -//! +//! # curve25519-dalek //! //! **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. -//! -//! # 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 = "3" -//! ``` -//! -//! The sole breaking change in the `3.x` series was an update to the `digest` -//! version, and in terms of non-breaking changes it includes: -//! -//! * support for using `alloc` instead of `std` on stable Rust, -//! * the Elligator2 encoding for Edwards points, -//! * a fix to use `packed_simd2`, -//! * various documentation fixes and improvements, -//! * support for configurably-sized, precomputed lookup tables for basepoint scalar -//! multiplication, -//! * two new formally-verified field arithmetic backends which use the Fiat Crypto -//! Rust code, which is generated from proofs of functional correctness checked by -//! the Coq theorem proving system, and -//! * support for explicitly calling the `zeroize` traits for all point types. -//! -//! The `2.x` series has API almost entirely unchanged from the `1.x` series, -//! except that: -//! -//! * an error in the data modeling for the (optional) `serde` feature was -//! corrected, so that when the `2.x`-series `serde` implementation is used -//! with `serde-bincode`, the derived serialization matches the usual X/Ed25519 -//! formats; -//! * the `rand` version was updated. -//! -//! See `CHANGELOG.md` for more details. -//! -//! # Backends and Features -//! -//! The `nightly` feature enables features available only when using a Rust nightly -//! compiler. In particular, it is required for rendering documentation and for -//! the SIMD backends. -//! -//! Curve arithmetic is implemented using one of the following backends: -//! -//! * a `u32` backend using serial formulas and `u64` products; -//! * a `u64` backend using serial formulas and `u128` products; -//! * an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records); -//! * an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records); -//! -//! By default the `u64` backend is selected. To select a specific backend, use: -//! ```sh -//! cargo build --no-default-features --features "std u32_backend" -//! cargo build --no-default-features --features "std u64_backend" -//! # Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2 -//! cargo build --no-default-features --features "std simd_backend" -//! # Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma -//! cargo build --no-default-features --features "std simd_backend" -//! ``` -//! Crates using `curve25519-dalek` can either select a backend on behalf of their -//! users, or expose feature flags that control the `curve25519-dalek` backend. -//! -//! The `std` feature is enabled by default, but it can be disabled for no-`std` -//! builds using `--no-default-features`. Note that this requires explicitly -//! selecting an arithmetic backend using one of the `_backend` features. -//! If no backend is selected, compilation will fail. -//! -//! # Safety -//! -//! The `curve25519-dalek` types are designed to make illegal states -//! unrepresentable. For example, any instance of an `EdwardsPoint` is -//! guaranteed to hold a point on the Edwards curve, and any instance of a -//! `RistrettoPoint` is guaranteed to hold a valid point in the Ristretto -//! group. -//! -//! All operations are implemented using constant-time logic (no -//! secret-dependent branches, no secret-dependent memory accesses), -//! unless specifically marked as being variable-time code. -//! We believe that our constant-time logic is lowered to constant-time -//! assembly, at least on `x86_64` targets. -//! -//! As an additional guard against possible future compiler optimizations, -//! the `subtle` crate places an optimization barrier before every -//! conditional move or assignment. More details can be found in [the -//! documentation for the `subtle` crate][subtle_doc]. -//! -//! Some functionality (e.g., multiscalar multiplication or batch -//! inversion) requires heap allocation for temporary buffers. All -//! heap-allocated buffers of potentially secret data are explicitly -//! zeroed before release. -//! -//! However, we do not attempt to zero stack data, for two reasons. -//! First, it's not possible to do so correctly: we don't have control -//! over stack allocations, so there's no way to know how much data to -//! wipe. Second, because `curve25519-dalek` provides a mid-level API, -//! the correct place to start zeroing stack data is likely not at the -//! entrypoints of `curve25519-dalek` functions, but at the entrypoints of -//! functions in other crates. -//! -//! The implementation is memory-safe, and contains no significant -//! `unsafe` code. The SIMD backend uses `unsafe` internally to call SIMD -//! intrinsics. These are marked `unsafe` only because invoking them on an -//! inappropriate CPU would cause `SIGILL`, but the entire backend is only -//! compiled with appropriate `target_feature`s, so this cannot occur. -//! -//! # Performance -//! -//! Benchmarks are run using [`criterion.rs`][criterion]: -//! -//! ```sh -//! cargo bench --no-default-features --features "std u32_backend" -//! cargo bench --no-default-features --features "std u64_backend" -//! # Uses avx2 or ifma only if compiled for an appropriate target. -//! export RUSTFLAGS="-C target_cpu=native" -//! cargo bench --no-default-features --features "std simd_backend" -//! ``` -//! -//! Performance is a secondary goal behind correctness, safety, and -//! clarity, but we aim to be competitive with other implementations. -//! -//! # FFI -//! -//! Unfortunately, we have no plans to add FFI to `curve25519-dalek` directly. The -//! reason is that we use Rust features to provide an API that maintains safety -//! invariants, which are not possible to maintain across an FFI boundary. For -//! instance, as described in the _Safety_ section above, invalid points are -//! impossible to construct, and this would not be the case if we exposed point -//! operations over FFI. -//! -//! However, `curve25519-dalek` is designed as a *mid-level* API, aimed at -//! implementing other, higher-level primitives. Instead of providing FFI at the -//! mid-level, our suggestion is to implement the higher-level primitive (a -//! signature, PAKE, ZKP, etc) in Rust, using `curve25519-dalek` as a dependency, -//! and have that crate provide a minimal, byte-buffer-oriented FFI specific to -//! that primitive. -//! -//! # 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 -//! 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.* -//! -//! `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. Most of this code, -//! including the 32-bit field arithmetic, has since been rewritten. -//! -//! The fast `u32` and `u64` scalar arithmetic was implemented by Andrew Moon, and -//! the addition chain for scalar inversion was provided by Brian Smith. The -//! optimised batch inversion was contributed by Sean Bowe and Daira Hopwood. -//! -//! The `no_std` and `zeroize` support was contributed by Tony Arcieri. -//! -//! The formally verified backends, `fiat_u32_backend` and `fiat_u64_backend`, which -//! integrate with the Rust generated by the -//! [Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) were contributed -//! by François Garillot. -//! -//! Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg, -//! Pratyush Mishra, Michael Rosenberg, and countless others for their -//! contributions. -//! -//! [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 -//! [docs-external]: https://doc.dalek.rs/curve25519_dalek/ -//! [docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/ -//! [criterion]: https://github.com/japaric/criterion.rs -//! [parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/vector/avx2/index.html -//! [subtle_doc]: https://doc.dalek.rs/subtle/ //------------------------------------------------------------------------ // External dependencies: diff --git a/src/ristretto.rs b/src/ristretto.rs index 4f2b48fc..405dc676 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -503,8 +503,8 @@ impl RistrettoPoint { /// in a batch. /// /// ``` - /// # extern crate curve25519_dalek; - /// # use curve25519_dalek::ristretto::RistrettoPoint; + /// # extern crate noah_curve25519_dalek; + /// # use noah_curve25519_dalek::ristretto::RistrettoPoint; /// extern crate rand_core; /// use rand_core::OsRng; /// @@ -701,8 +701,8 @@ impl RistrettoPoint { /// # Example /// /// ``` - /// # extern crate curve25519_dalek; - /// # use curve25519_dalek::ristretto::RistrettoPoint; + /// # extern crate noah_curve25519_dalek; + /// # use noah_curve25519_dalek::ristretto::RistrettoPoint; /// extern crate sha2; /// use sha2::Sha512; /// @@ -1019,8 +1019,8 @@ impl RistrettoPoint { /// A precomputed table of multiples of the Ristretto basepoint is /// available in the `constants` module: /// ``` -/// use curve25519_dalek::constants; -/// use curve25519_dalek::scalar::Scalar; +/// use noah_curve25519_dalek::constants; +/// use noah_curve25519_dalek::scalar::Scalar; /// /// let a = Scalar::from(87329482u64); /// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE; @@ -1067,14 +1067,14 @@ impl ConditionallySelectable for RistrettoPoint { /// /// ``` /// # extern crate subtle; - /// # extern crate curve25519_dalek; + /// # extern crate noah_curve25519_dalek; /// # /// use subtle::ConditionallySelectable; /// use subtle::Choice; /// # - /// # use curve25519_dalek::traits::Identity; - /// # use curve25519_dalek::ristretto::RistrettoPoint; - /// # use curve25519_dalek::constants; + /// # use noah_curve25519_dalek::traits::Identity; + /// # use noah_curve25519_dalek::ristretto::RistrettoPoint; + /// # use noah_curve25519_dalek::constants; /// # fn main() { /// /// let A = RistrettoPoint::identity(); @@ -1510,9 +1510,10 @@ mod test { fn double_and_compress_1024_random_points() { let mut rng = OsRng; - let points: Vec = (0..1024) + let mut points: Vec = (0..1024) .map(|_| RistrettoPoint::random(&mut rng)) .collect(); + points[500] = RistrettoPoint::identity(); let compressed = RistrettoPoint::double_and_compress_batch(&points); diff --git a/src/scalar.rs b/src/scalar.rs index a09b6f6f..3983d8f1 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -34,7 +34,7 @@ //! `Some(Scalar)` in return: //! //! ``` -//! use curve25519_dalek::scalar::Scalar; +//! use noah_curve25519_dalek::scalar::Scalar; //! //! let one_as_bytes: [u8; 32] = Scalar::one().to_bytes(); //! let a: Option = Scalar::from_canonical_bytes(one_as_bytes); @@ -46,7 +46,7 @@ //! (in this case, \\( \ell + 2 \\)), we'll get `None` back: //! //! ``` -//! use curve25519_dalek::scalar::Scalar; +//! use noah_curve25519_dalek::scalar::Scalar; //! //! let l_plus_two_bytes: [u8; 32] = [ //! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, @@ -66,7 +66,7 @@ //! resultant scalar \\( \mod \ell \\), producing \\( 2 \\): //! //! ``` -//! use curve25519_dalek::scalar::Scalar; +//! use noah_curve25519_dalek::scalar::Scalar; //! //! let l_plus_two_bytes: [u8; 32] = [ //! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, @@ -91,12 +91,12 @@ //! which allows an IUF API. //! //! ``` -//! # extern crate curve25519_dalek; +//! # extern crate noah_curve25519_dalek; //! # extern crate sha2; //! # //! # fn main() { //! use sha2::{Digest, Sha512}; -//! use curve25519_dalek::scalar::Scalar; +//! use noah_curve25519_dalek::scalar::Scalar; //! //! // Hashing a single byte slice //! let a = Scalar::hash_from_bytes::(b"Abolish ICE"); @@ -119,7 +119,7 @@ //! assurances as to reduction modulo the group order: //! //! ``` -//! use curve25519_dalek::scalar::Scalar; +//! use noah_curve25519_dalek::scalar::Scalar; //! //! let l_plus_two_bytes: [u8; 32] = [ //! 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, @@ -514,7 +514,7 @@ impl From for Scalar { /// # Example /// /// ``` - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// let fourtytwo = Scalar::from(42u64); /// let six = Scalar::from(6u64); @@ -560,10 +560,10 @@ impl Scalar { /// /// ``` /// extern crate rand_core; - /// # extern crate curve25519_dalek; + /// # extern crate noah_curve25519_dalek; /// # /// # fn main() { - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// use rand_core::OsRng; /// @@ -586,8 +586,8 @@ impl Scalar { /// # Example /// /// ``` - /// # extern crate curve25519_dalek; - /// # use curve25519_dalek::scalar::Scalar; + /// # extern crate noah_curve25519_dalek; + /// # use noah_curve25519_dalek::scalar::Scalar; /// extern crate sha2; /// /// use sha2::Sha512; @@ -617,8 +617,8 @@ impl Scalar { /// # Example /// /// ``` - /// # extern crate curve25519_dalek; - /// # use curve25519_dalek::scalar::Scalar; + /// # extern crate noah_curve25519_dalek; + /// # use noah_curve25519_dalek::scalar::Scalar; /// extern crate sha2; /// extern crate digest; /// @@ -658,7 +658,7 @@ impl Scalar { /// # Example /// /// ``` - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// let s: Scalar = Scalar::zero(); /// @@ -673,7 +673,7 @@ impl Scalar { /// # Example /// /// ``` - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// let s: Scalar = Scalar::zero(); /// @@ -713,7 +713,7 @@ impl Scalar { /// # Example /// /// ``` - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// // x = 2238329342913194256032495932344128051776374960164957527413114840482143558222 /// let X: Scalar = Scalar::from_bytes_mod_order([ @@ -757,8 +757,8 @@ impl Scalar { /// # Example /// /// ``` - /// # extern crate curve25519_dalek; - /// # use curve25519_dalek::scalar::Scalar; + /// # extern crate noah_curve25519_dalek; + /// # use noah_curve25519_dalek::scalar::Scalar; /// # fn main() { /// let mut scalars = [ /// Scalar::from(3u64), @@ -1127,9 +1127,9 @@ impl Scalar { /// This is intended for uses like input validation, where variable-time code is acceptable. /// /// ``` - /// # extern crate curve25519_dalek; + /// # extern crate noah_curve25519_dalek; /// # extern crate subtle; - /// # use curve25519_dalek::scalar::Scalar; + /// # use noah_curve25519_dalek::scalar::Scalar; /// # use subtle::ConditionallySelectable; /// # fn main() { /// // 2^255 - 1, since `from_bits` clears the high bit diff --git a/src/traits.rs b/src/traits.rs index d127b3ed..9c43abb4 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -84,10 +84,10 @@ pub trait MultiscalarMul { /// iterators returning either `Scalar`s or `&Scalar`s. /// /// ``` - /// use curve25519_dalek::constants; - /// use curve25519_dalek::traits::MultiscalarMul; - /// use curve25519_dalek::ristretto::RistrettoPoint; - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::constants; + /// use noah_curve25519_dalek::traits::MultiscalarMul; + /// use noah_curve25519_dalek::ristretto::RistrettoPoint; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// // Some scalars /// let a = Scalar::from(87329482u64); @@ -136,10 +136,10 @@ pub trait VartimeMultiscalarMul { /// inlining point decompression into the multiscalar call, /// avoiding the need for temporary buffers. /// ``` - /// use curve25519_dalek::constants; - /// use curve25519_dalek::traits::VartimeMultiscalarMul; - /// use curve25519_dalek::ristretto::RistrettoPoint; - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::constants; + /// use noah_curve25519_dalek::traits::VartimeMultiscalarMul; + /// use noah_curve25519_dalek::ristretto::RistrettoPoint; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// // Some scalars /// let a = Scalar::from(87329482u64); @@ -199,10 +199,10 @@ pub trait VartimeMultiscalarMul { /// iterators returning either `Scalar`s or `&Scalar`s. /// /// ``` - /// use curve25519_dalek::constants; - /// use curve25519_dalek::traits::VartimeMultiscalarMul; - /// use curve25519_dalek::ristretto::RistrettoPoint; - /// use curve25519_dalek::scalar::Scalar; + /// use noah_curve25519_dalek::constants; + /// use noah_curve25519_dalek::traits::VartimeMultiscalarMul; + /// use noah_curve25519_dalek::ristretto::RistrettoPoint; + /// use noah_curve25519_dalek::scalar::Scalar; /// /// // Some scalars /// let a = Scalar::from(87329482u64); diff --git a/vendor/ristretto.sage b/vendor/ristretto.sage deleted file mode 100644 index 04cf4f92..00000000 --- a/vendor/ristretto.sage +++ /dev/null @@ -1,857 +0,0 @@ -import binascii -class InvalidEncodingException(Exception): pass -class NotOnCurveException(Exception): pass -class SpecException(Exception): pass - -def lobit(x): return int(x) & 1 -def hibit(x): return lobit(2*x) -def negative(x): return lobit(x) -def enc_le(x,n): return bytearray([int(x)>>(8*i) & 0xFF for i in xrange(n)]) -def dec_le(x): return sum(b<<(8*i) for i,b in enumerate(x)) -def randombytes(n): return bytearray([randint(0,255) for _ in range(n)]) - -def optimized_version_of(spec): - """Decorator: This function is an optimized version of some specification""" - def decorator(f): - def wrapper(self,*args,**kwargs): - def pr(x): - if isinstance(x,bytearray): return binascii.hexlify(x) - else: return str(x) - try: spec_ans = getattr(self,spec,spec)(*args,**kwargs),None - except Exception as e: spec_ans = None,e - try: opt_ans = f(self,*args,**kwargs),None - except Exception as e: opt_ans = None,e - if spec_ans[1] is None and opt_ans[1] is not None: - raise - #raise SpecException("Mismatch in %s: spec returned %s but opt threw %s" - # % (f.__name__,str(spec_ans[0]),str(opt_ans[1]))) - if spec_ans[1] is not None and opt_ans[1] is None: - raise - #raise SpecException("Mismatch in %s: spec threw %s but opt returned %s" - # % (f.__name__,str(spec_ans[1]),str(opt_ans[0]))) - if spec_ans[0] != opt_ans[0]: - raise SpecException("Mismatch in %s: %s != %s" - % (f.__name__,pr(spec_ans[0]),pr(opt_ans[0]))) - if opt_ans[1] is not None: raise - else: return opt_ans[0] - wrapper.__name__ = f.__name__ - return wrapper - return decorator - -def xsqrt(x,exn=InvalidEncodingException("Not on curve")): - """Return sqrt(x)""" - if not is_square(x): raise exn - s = sqrt(x) - if negative(s): s=-s - return s - -def isqrt(x,exn=InvalidEncodingException("Not on curve")): - """Return 1/sqrt(x)""" - if x==0: return 0 - if not is_square(x): raise exn - s = sqrt(x) - #if negative(s): s=-s - return 1/s - -def inv0(x): return 1/x if x != 0 else 0 - -def isqrt_i(x): - """Return 1/sqrt(x) or 1/sqrt(zeta * x)""" - if x==0: return True,0 - gen = x.parent(-1) - while is_square(gen): gen = sqrt(gen) - if is_square(x): return True,1/sqrt(x) - else: return False,1/sqrt(x*gen) - -class QuotientEdwardsPoint(object): - """Abstract class for point an a quotiented Edwards curve; needs F,a,d,cofactor to work""" - def __init__(self,x=0,y=1): - x = self.x = self.F(x) - y = self.y = self.F(y) - if y^2 + self.a*x^2 != 1 + self.d*x^2*y^2: - raise NotOnCurveException(str(self)) - - def __repr__(self): - return "%s(0x%x,0x%x)" % (self.__class__.__name__, self.x, self.y) - - def __iter__(self): - yield self.x - yield self.y - - def __add__(self,other): - x,y = self - X,Y = other - a,d = self.a,self.d - return self.__class__( - (x*Y+y*X)/(1+d*x*y*X*Y), - (y*Y-a*x*X)/(1-d*x*y*X*Y) - ) - - def __neg__(self): return self.__class__(-self.x,self.y) - def __sub__(self,other): return self + (-other) - def __rmul__(self,other): return self*other - def __eq__(self,other): - """NB: this is the only method that is different from the usual one""" - x,y = self - X,Y = other - return x*Y == X*y or (self.cofactor==8 and -self.a*x*X == y*Y) - def __ne__(self,other): return not (self==other) - - def __mul__(self,exp): - exp = int(exp) - if exp < 0: exp,self = -exp,-self - total = self.__class__() - work = self - while exp != 0: - if exp & 1: total += work - work += work - exp >>= 1 - return total - - def xyzt(self): - x,y = self - z = self.F.random_element() - return x*z,y*z,z,x*y*z - - def torque(self): - """Apply cofactor group, except keeping the point even""" - if self.cofactor == 8: - if self.a == -1: return self.__class__(self.y*self.i, self.x*self.i) - if self.a == 1: return self.__class__(-self.y, self.x) - else: - return self.__class__(-self.x, -self.y) - - def doubleAndEncodeSpec(self): - return (self+self).encode() - - # Utility functions - @classmethod - def bytesToGf(cls,bytes,mustBeProper=True,mustBePositive=False,maskHiBits=False): - """Convert little-endian bytes to field element, sanity check length""" - if len(bytes) != cls.encLen: - raise InvalidEncodingException("wrong length %d" % len(bytes)) - s = dec_le(bytes) - if mustBeProper and s >= cls.F.order(): - raise InvalidEncodingException("%d out of range!" % s) - bitlen = int(ceil(log(cls.F.order())/log(2))) - if maskHiBits: s &= 2^bitlen-1 - s = cls.F(s) - if mustBePositive and negative(s): - raise InvalidEncodingException("%d is negative!" % s) - return s - - @classmethod - def gfToBytes(cls,x,mustBePositive=False): - """Convert little-endian bytes to field element, sanity check length""" - if negative(x) and mustBePositive: x = -x - return enc_le(x,cls.encLen) - -class RistrettoPoint(QuotientEdwardsPoint): - """The new Ristretto group""" - def encodeSpec(self): - """Unoptimized specification for encoding""" - x,y = self - if self.cofactor==8 and (negative(x*y) or y==0): (x,y) = self.torque() - if y == -1: y = 1 # Avoid divide by 0; doesn't affect impl - - if negative(x): x,y = -x,-y - s = xsqrt(self.mneg*(1-y)/(1+y),exn=Exception("Unimplemented: point is odd: " + str(self))) - return self.gfToBytes(s) - - @classmethod - def decodeSpec(cls,s): - """Unoptimized specification for decoding""" - s = cls.bytesToGf(s,mustBePositive=True) - - a,d = cls.a,cls.d - x = xsqrt(4*s^2 / (a*d*(1+a*s^2)^2 - (1-a*s^2)^2)) - y = (1+a*s^2) / (1-a*s^2) - - if cls.cofactor==8 and (negative(x*y) or y==0): - raise InvalidEncodingException("x*y has high bit") - - return cls(x,y) - - @optimized_version_of("encodeSpec") - def encode(self): - """Encode, optimized version""" - a,d,mneg = self.a,self.d,self.mneg - x,y,z,t = self.xyzt() - - if self.cofactor==8: - u1 = mneg*(z+y)*(z-y) - u2 = x*y # = t*z - isr = isqrt(u1*u2^2) - i1 = isr*u1 # sqrt(mneg*(z+y)*(z-y))/(x*y) - i2 = isr*u2 # 1/sqrt(a*(y+z)*(y-z)) - z_inv = i1*i2*t # 1/z - - if negative(t*z_inv): - if a==-1: - x,y = y*self.i,x*self.i - den_inv = self.magic * i1 - else: - x,y = -y,x - den_inv = self.i * self.magic * i1 - - else: - den_inv = i2 - - if negative(x*z_inv): y = -y - s = (z-y) * den_inv - else: - num = mneg*(z+y)*(z-y) - isr = isqrt(num*y^2) - if negative(isr^2*num*y*t): y = -y - s = isr*y*(z-y) - - return self.gfToBytes(s,mustBePositive=True) - - @optimized_version_of("doubleAndEncodeSpec") - def doubleAndEncode(self): - X,Y,Z,T = self.xyzt() - a,d,mneg = self.a,self.d,self.mneg - - if self.cofactor==8: - e = 2*X*Y - f = Z^2+d*T^2 - g = Y^2-a*X^2 - h = Z^2-d*T^2 - - inv1 = 1/(e*f*g*h) - z_inv = inv1*e*g # 1 / (f*h) - t_inv = inv1*f*h - - if negative(e*g*z_inv): - if a==-1: sqrta = self.i - else: sqrta = -1 - e,f,g,h = g,h,-e,f*sqrta - factor = self.i - else: - factor = self.magic - - if negative(h*e*z_inv): g=-g - s = (h-g)*factor*g*t_inv - - else: - foo = Y^2+a*X^2 - bar = X*Y - den = 1/(foo*bar) - if negative(2*bar^2*den): tmp = a*X^2 - else: tmp = Y^2 - s = self.magic*(Z^2-tmp)*foo*den - - return self.gfToBytes(s,mustBePositive=True) - - @classmethod - @optimized_version_of("decodeSpec") - def decode(cls,s): - """Decode, optimized version""" - s = cls.bytesToGf(s,mustBePositive=True) - - a,d = cls.a,cls.d - yden = 1-a*s^2 - ynum = 1+a*s^2 - yden_sqr = yden^2 - xden_sqr = a*d*ynum^2 - yden_sqr - - isr = isqrt(xden_sqr * yden_sqr) - - xden_inv = isr * yden - yden_inv = xden_inv * isr * xden_sqr - - x = 2*s*xden_inv - if negative(x): x = -x - y = ynum * yden_inv - - if cls.cofactor==8 and (negative(x*y) or y==0): - raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y)) - - return cls(x,y) - - @classmethod - def fromJacobiQuartic(cls,s,t,sgn=1): - """Convert point from its Jacobi Quartic representation""" - a,d = cls.a,cls.d - assert s^4 - 2*cls.a*(1-2*d/(d-a))*s^2 + 1 == t^2 - x = 2*s*cls.magic / t - y = (1+a*s^2) / (1-a*s^2) - return cls(sgn*x,y) - - @classmethod - def elligatorSpec(cls,r0): - a,d = cls.a,cls.d - r = cls.qnr * cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True)^2 - den = (d*r-a)*(a*r-d) - if den == 0: return cls() - n1 = cls.a*(r+1)*(a+d)*(d-a)/den - n2 = r*n1 - if is_square(n1): - sgn,s,t = 1, xsqrt(n1), -(r-1)*(a+d)^2 / den - 1 - else: - sgn,s,t = -1,-xsqrt(n2), r*(r-1)*(a+d)^2 / den - 1 - - return cls.fromJacobiQuartic(s,t) - - @classmethod - @optimized_version_of("elligatorSpec") - def elligator(cls,r0): - a,d = cls.a,cls.d - r0 = cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True) - r = cls.qnr * r0^2 - den = (d*r-a)*(a*r-d) - num = cls.a*(r+1)*(a+d)*(d-a) - - iss,isri = isqrt_i(num*den) - if iss: sgn,twiddle = 1,1 - else: sgn,twiddle = -1,r0*cls.qnr - isri *= twiddle - s = isri*num - t = -sgn*isri*s*(r-1)*(d+a)^2 - 1 - if negative(s) == iss: s = -s - return cls.fromJacobiQuartic(s,t) - - -class Decaf_1_1_Point(QuotientEdwardsPoint): - """Like current decaf but tweaked for simplicity""" - def encodeSpec(self): - """Unoptimized specification for encoding""" - a,d = self.a,self.d - x,y = self - if x==0 or y==0: return(self.gfToBytes(0)) - - if self.cofactor==8 and negative(x*y*self.isoMagic): - x,y = self.torque() - - sr = xsqrt(1-a*x^2) - altx = x*y*self.isoMagic / sr - if negative(altx): s = (1+sr)/x - else: s = (1-sr)/x - - return self.gfToBytes(s,mustBePositive=True) - - @classmethod - def decodeSpec(cls,s): - """Unoptimized specification for decoding""" - a,d = cls.a,cls.d - s = cls.bytesToGf(s,mustBePositive=True) - - if s==0: return cls() - t = xsqrt(s^4 + 2*(a-2*d)*s^2 + 1) - altx = 2*s*cls.isoMagic/t - if negative(altx): t = -t - x = 2*s / (1+a*s^2) - y = (1-a*s^2) / t - - if cls.cofactor==8 and (negative(x*y*cls.isoMagic) or y==0): - raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y)) - - return cls(x,y) - - def toJacobiQuartic(self,toggle_rotation=False,toggle_altx=False,toggle_s=False): - "Return s,t on jacobi curve" - a,d = self.a,self.d - x,y,z,t = self.xyzt() - - if self.cofactor == 8: - # Cofactor 8 version - # Simulate IMAGINE_TWIST because that's how libdecaf does it - x = self.i*x - t = self.i*t - a = -a - d = -d - - # OK, the actual libdecaf code should be here - num = (z+y)*(z-y) - den = x*y - isr = isqrt(num*(a-d)*den^2) - - iden = isr * den * self.isoMagic # 1/sqrt((z+y)(z-y)) = 1/sqrt(1-Y^2) / z - inum = isr * num # sqrt(1-Y^2) * z / xysqrt(a-d) ~ 1/sqrt(1-ax^2)/z - - if negative(iden*inum*self.i*t^2*(d-a)) != toggle_rotation: - iden,inum = inum,iden - fac = x*sqrt(a) - toggle=(a==-1) - else: - fac = y - toggle=False - - imi = self.isoMagic * self.i - altx = inum*t*imi - neg_altx = negative(altx) != toggle_altx - if neg_altx != toggle: inum =- inum - - tmp = fac*(inum*z + 1) - s = iden*tmp*imi - - negm1 = (negative(s) != toggle_s) != neg_altx - if negm1: m1 = a*fac + z - else: m1 = a*fac - z - - swap = toggle_s - - else: - # Much simpler cofactor 4 version - num = (x+t)*(x-t) - isr = isqrt(num*(a-d)*x^2) - ratio = isr*num - altx = ratio*self.isoMagic - - neg_altx = negative(altx) != toggle_altx - if neg_altx: ratio =- ratio - - tmp = ratio*z - t - s = (a-d)*isr*x*tmp - - negx = (negative(s) != toggle_s) != neg_altx - if negx: m1 = -a*t + x - else: m1 = -a*t - x - - swap = toggle_s - - if negative(s): s = -s - - return s,m1,a*tmp,swap - - def invertElligator(self,toggle_r=False,*args,**kwargs): - "Produce preimage of self under elligator, or None" - a,d = self.a,self.d - - rets = [] - - tr = [False,True] if self.cofactor == 8 else [False] - for toggle_rotation in tr: - for toggle_altx in [False,True]: - for toggle_s in [False,True]: - for toggle_r in [False,True]: - s,m1,m12,swap = self.toJacobiQuartic(toggle_rotation,toggle_altx,toggle_s) - - #print - #print toggle_rotation,toggle_altx,toggle_s - #print m1 - #print m12 - - - if self == self.__class__(): - if self.cofactor == 4: - # Hacks for identity! - if toggle_altx: m12 = 1 - elif toggle_s: m1 = 1 - elif toggle_r: continue - ## BOTH??? - - else: - m12 = 1 - imi = self.isoMagic * self.i - if toggle_rotation: - if toggle_altx: m1 = -imi - else: m1 = +imi - else: - if toggle_altx: m1 = 0 - else: m1 = a-d - - rnum = (d*a*m12-m1) - rden = ((d*a-1)*m12+m1) - if swap: rnum,rden = rden,rnum - - ok,sr = isqrt_i(rnum*rden*self.qnr) - if not ok: continue - sr *= rnum - #print "Works! %d %x" % (swap,sr) - - if negative(sr) != toggle_r: sr = -sr - ret = self.gfToBytes(sr) - if self.elligator(ret) != self and self.elligator(ret) != -self: - print "WRONG!",[toggle_rotation,toggle_altx,toggle_s] - if self.elligator(ret) == -self and self != -self: print "Negated!",[toggle_rotation,toggle_altx,toggle_s] - rets.append(bytes(ret)) - return rets - - @optimized_version_of("encodeSpec") - def encode(self): - """Encode, optimized version""" - return self.gfToBytes(self.toJacobiQuartic()[0]) - - @classmethod - @optimized_version_of("decodeSpec") - def decode(cls,s): - """Decode, optimized version""" - a,d = cls.a,cls.d - s = cls.bytesToGf(s,mustBePositive=True) - - #if s==0: return cls() - s2 = s^2 - den = 1+a*s2 - num = den^2 - 4*d*s2 - isr = isqrt(num*den^2) - altx = 2*s*isr*den*cls.isoMagic - if negative(altx): isr = -isr - x = 2*s *isr^2*den*num - y = (1-a*s^2) * isr*den - - if cls.cofactor==8 and (negative(x*y*cls.isoMagic) or y==0): - raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y)) - - return cls(x,y) - - @classmethod - def fromJacobiQuartic(cls,s,t,sgn=1): - """Convert point from its Jacobi Quartic representation""" - a,d = cls.a,cls.d - if s==0: return cls() - x = 2*s / (1+a*s^2) - y = (1-a*s^2) / t - return cls(x,sgn*y) - - @optimized_version_of("doubleAndEncodeSpec") - def doubleAndEncode(self): - X,Y,Z,T = self.xyzt() - a,d = self.a,self.d - - if self.cofactor == 8: - # Cofactor 8 version - # Simulate IMAGINE_TWIST because that's how libdecaf does it - X = self.i*X - T = self.i*T - a = -a - d = -d - # TODO: This is only being called for a=-1, so could - # be wrong for a=1 - - e = 2*X*Y - f = Y^2+a*X^2 - g = Y^2-a*X^2 - h = Z^2-d*T^2 - - eim = e*self.isoMagic - inv = 1/(eim*g*f*h) - fh_inv = eim*g*inv*self.i - - if negative(eim*g*fh_inv): - idf = g*self.isoMagic*self.i - bar = f - foo = g - test = eim*f - else: - idf = eim - bar = h - foo = -eim - test = g*h - - if negative(test*fh_inv): bar =- bar - s = idf*(foo+bar)*inv*f*h - - else: - xy = X*Y - h = Z^2-d*T^2 - inv = 1/(xy*h) - if negative(inv*2*xy^2*self.isoMagic): tmp = Y - else: tmp = X - s = tmp^2*h*inv # = X/Y or Y/X, interestingly - - return self.gfToBytes(s,mustBePositive=True) - - @classmethod - def elligatorSpec(cls,r0,fromR=False): - a,d = cls.a,cls.d - if fromR: r = r0 - else: r = cls.qnr * cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True)^2 - - den = (d*r-(d-a))*((d-a)*r-d) - if den == 0: return cls() - n1 = (r+1)*(a-2*d)/den - n2 = r*n1 - if is_square(n1): - sgn,s,t = 1, xsqrt(n1), -(r-1)*(a-2*d)^2 / den - 1 - else: - sgn,s,t = -1, -xsqrt(n2), r*(r-1)*(a-2*d)^2 / den - 1 - - return cls.fromJacobiQuartic(s,t) - - @classmethod - @optimized_version_of("elligatorSpec") - def elligator(cls,r0): - a,d = cls.a,cls.d - r0 = cls.bytesToGf(r0,mustBeProper=False,maskHiBits=True) - r = cls.qnr * r0^2 - den = (d*r-(d-a))*((d-a)*r-d) - num = (r+1)*(a-2*d) - - iss,isri = isqrt_i(num*den) - if iss: sgn,twiddle = 1,1 - else: sgn,twiddle = -1,r0*cls.qnr - isri *= twiddle - s = isri*num - t = -sgn*isri*s*(r-1)*(a-2*d)^2 - 1 - if negative(s) == iss: s = -s - return cls.fromJacobiQuartic(s,t) - - def elligatorInverseBruteForce(self): - """Invert Elligator using SAGE's polynomial solver""" - a,d = self.a,self.d - R. = self.F[] - r = self.qnr * r0^2 - den = (d*r-(d-a))*((d-a)*r-d) - n1 = (r+1)*(a-2*d)/den - n2 = r*n1 - ret = set() - for s2,t in [(n1, -(r-1)*(a-2*d)^2 / den - 1), - (n2,r*(r-1)*(a-2*d)^2 / den - 1)]: - x2 = 4*s2/(1+a*s2)^2 - y = (1-a*s2) / t - - selfT = self - for i in xrange(self.cofactor/2): - xT,yT = selfT - polyX = xT^2-x2 - polyY = yT-y - sx = set(r for r,_ in polyX.numerator().roots()) - sy = set(r for r,_ in polyY.numerator().roots()) - ret = ret.union(sx.intersection(sy)) - - selfT = selfT.torque() - - ret = [self.gfToBytes(r) for r in ret] - - for r in ret: - assert self.elligator(r) in [self,-self] - - ret = [r for r in ret if self.elligator(r) == self] - - return ret - -class Ed25519Point(RistrettoPoint): - F = GF(2^255-19) - d = F(-121665/121666) - a = F(-1) - i = sqrt(F(-1)) - mneg = F(1) - qnr = i - magic = isqrt(a*d-1) - cofactor = 8 - encLen = 32 - - @classmethod - def base(cls): - return cls( 15112221349535400772501151409588531511454012693041857206046113283949847762202, 46316835694926478169428394003475163141307993866256225615783033603165251855960 - ) - -class NegEd25519Point(RistrettoPoint): - F = GF(2^255-19) - d = F(121665/121666) - a = F(1) - i = sqrt(F(-1)) - mneg = F(-1) # TODO checkme vs 1-ad or whatever - qnr = i - magic = isqrt(a*d-1) - cofactor = 8 - encLen = 32 - - @classmethod - def base(cls): - y = cls.F(4/5) - x = sqrt((y^2-1)/(cls.d*y^2-cls.a)) - if negative(x): x = -x - return cls(x,y) - -class IsoEd448Point(RistrettoPoint): - F = GF(2^448-2^224-1) - d = F(39082/39081) - a = F(1) - mneg = F(-1) - qnr = -1 - magic = isqrt(a*d-1) - cofactor = 4 - encLen = 56 - - @classmethod - def base(cls): - return cls( # RFC has it wrong - 345397493039729516374008604150537410266655260075183290216406970281645695073672344430481787759340633221708391583424041788924124567700732, - -363419362147803445274661903944002267176820680343659030140745099590306164083365386343198191849338272965044442230921818680526749009182718 - ) - -class TwistedEd448GoldilocksPoint(Decaf_1_1_Point): - F = GF(2^448-2^224-1) - d = F(-39082) - a = F(-1) - qnr = -1 - cofactor = 4 - encLen = 56 - isoMagic = IsoEd448Point.magic - - @classmethod - def base(cls): - return cls.decodeSpec(Ed448GoldilocksPoint.base().encodeSpec()) - -class Ed448GoldilocksPoint(Decaf_1_1_Point): - F = GF(2^448-2^224-1) - d = F(-39081) - a = F(1) - qnr = -1 - cofactor = 4 - encLen = 56 - isoMagic = IsoEd448Point.magic - - @classmethod - def base(cls): - return 2*cls( - 224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710, 298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660 - ) - -class IsoEd25519Point(Decaf_1_1_Point): - # TODO: twisted iso too! - # TODO: twisted iso might have to IMAGINE_TWIST or whatever - F = GF(2^255-19) - d = F(-121665) - a = F(1) - i = sqrt(F(-1)) - qnr = i - magic = isqrt(a*d-1) - cofactor = 8 - encLen = 32 - isoMagic = Ed25519Point.magic - isoA = Ed25519Point.a - - @classmethod - def base(cls): - return cls.decodeSpec(Ed25519Point.base().encode()) - -class TestFailedException(Exception): pass - -def test(cls,n): - print "Testing curve %s" % cls.__name__ - - specials = [1] - ii = cls.F(-1) - while is_square(ii): - specials.append(ii) - ii = sqrt(ii) - specials.append(ii) - for i in specials: - if negative(cls.F(i)): i = -i - i = enc_le(i,cls.encLen) - try: - Q = cls.decode(i) - QE = Q.encode() - if QE != i: - raise TestFailedException("Round trip special %s != %s" % - (binascii.hexlify(QE),binascii.hexlify(i))) - except NotOnCurveException: pass - except InvalidEncodingException: pass - - - P = cls.base() - Q = cls() - for i in xrange(n): - #print binascii.hexlify(Q.encode()) - QE = Q.encode() - QQ = cls.decode(QE) - if QQ != Q: raise TestFailedException("Round trip %s != %s" % (str(QQ),str(Q))) - - # Testing s -> 1/s: encodes -point on cofactor - s = cls.bytesToGf(QE) - if s != 0: - ss = cls.gfToBytes(1/s,mustBePositive=True) - try: - QN = cls.decode(ss) - if cls.cofactor == 8: - raise TestFailedException("1/s shouldnt work for cofactor 8") - if QN != -Q: - raise TestFailedException("s -> 1/s should negate point for cofactor 4") - except InvalidEncodingException as e: - # Should be raised iff cofactor==8 - if cls.cofactor == 4: - raise TestFailedException("s -> 1/s should work for cofactor 4") - - QT = Q - for h in xrange(cls.cofactor): - QT = QT.torque() - if QT.encode() != QE: - raise TestFailedException("Can't torque %s,%d" % (str(Q),h+1)) - - Q0 = Q + P - if Q0 == Q: raise TestFailedException("Addition doesn't work") - if Q0-P != Q: raise TestFailedException("Subtraction doesn't work") - - r = randint(1,1000) - Q1 = Q0*r - Q2 = Q0*(r+1) - if Q1 + Q0 != Q2: raise TestFailedException("Scalarmul doesn't work") - Q = Q1 - -def testElligator(cls,n): - print "Testing elligator on %s" % cls.__name__ - for i in xrange(n): - r = randombytes(cls.encLen) - P = cls.elligator(r) - if hasattr(P,"invertElligator"): - iv = P.invertElligator() - modr = bytes(cls.gfToBytes(cls.bytesToGf(r,mustBeProper=False,maskHiBits=True))) - iv2 = P.torque().invertElligator() - if modr not in iv: print "Failed to invert Elligator!" - if len(iv) != len(set(iv)): - print "Elligator inverses not unique!", len(set(iv)), len(iv) - if iv != iv2: - print "Elligator is untorqueable!" - #print [binascii.hexlify(j) for j in iv] - #print [binascii.hexlify(j) for j in iv2] - #break - else: - pass # TODO - -def gangtest(classes,n): - print "Gang test",[cls.__name__ for cls in classes] - specials = [1] - ii = classes[0].F(-1) - while is_square(ii): - specials.append(ii) - ii = sqrt(ii) - specials.append(ii) - - for i in xrange(n): - rets = [bytes((cls.base()*i).encode()) for cls in classes] - if len(set(rets)) != 1: - print "Divergence in encode at %d" % i - for c,ret in zip(classes,rets): - print c,binascii.hexlify(ret) - print - - if i < len(specials): r0 = enc_le(specials[i],classes[0].encLen) - else: r0 = randombytes(classes[0].encLen) - - rets = [bytes((cls.elligator(r0)*i).encode()) for cls in classes] - if len(set(rets)) != 1: - print "Divergence in elligator at %d" % i - for c,ret in zip(classes,rets): - print c,binascii.hexlify(ret) - print - -def testDoubleAndEncode(cls,n): - print "Testing doubleAndEncode on %s" % cls.__name__ - for i in xrange(n): - r1 = randombytes(cls.encLen) - r2 = randombytes(cls.encLen) - u = cls.elligator(r1) + cls.elligator(r2) - u.doubleAndEncode() - -testDoubleAndEncode(Ed25519Point,100) -testDoubleAndEncode(NegEd25519Point,100) -testDoubleAndEncode(IsoEd25519Point,100) -testDoubleAndEncode(IsoEd448Point,100) -testDoubleAndEncode(TwistedEd448GoldilocksPoint,100) -#test(Ed25519Point,100) -#test(NegEd25519Point,100) -#test(IsoEd25519Point,100) -#test(IsoEd448Point,100) -#test(TwistedEd448GoldilocksPoint,100) -#test(Ed448GoldilocksPoint,100) -#testElligator(Ed25519Point,100) -#testElligator(NegEd25519Point,100) -#testElligator(IsoEd25519Point,100) -#testElligator(IsoEd448Point,100) -#testElligator(Ed448GoldilocksPoint,100) -#testElligator(TwistedEd448GoldilocksPoint,100) -#gangtest([IsoEd448Point,TwistedEd448GoldilocksPoint,Ed448GoldilocksPoint],100) -#gangtest([Ed25519Point,IsoEd25519Point],100)