Compare commits

..

61 Commits

Author SHA1 Message Date
Jordan Rose
7c6d347563 bench: Don't depend on FieldElement and elligator_ristretto_flavor
Some checks failed
All / Check formatting (push) Has been cancelled
All / Check docs (push) Has been cancelled
Cross / Test (stable, aarch64-unknown-linux-gnu) (push) Has been cancelled
Cross / Test (stable, armv7-unknown-linux-gnueabihf) (push) Has been cancelled
Cross / Test (stable, powerpc-unknown-linux-gnu) (push) Has been cancelled
curve25519 Rust / Test fiat backend (sudo apt update && sudo apt install gcc-multilib, i686-unknown-linux-gnu) (push) Has been cancelled
curve25519 Rust / Test fiat backend (x86_64-unknown-linux-gnu) (push) Has been cancelled
curve25519 Rust / Build fiat on no_std target (thumbv7em-none-eabi) (curve25519-dalek) (push) Has been cancelled
All / Check that clippy is happy (push) Has been cancelled
curve25519 Rust / Test serial backend (sudo apt update && sudo apt install gcc-multilib, i686-unknown-linux-gnu) (push) Has been cancelled
curve25519 Rust / Test serial backend (x86_64-unknown-linux-gnu) (push) Has been cancelled
curve25519 Rust / Test Build Script (push) Has been cancelled
curve25519 Rust / Test simd backend (nightly) (push) Has been cancelled
curve25519 Rust / Test simd backend (stable) (push) Has been cancelled
curve25519 Rust / Current MSRV is 1.60.0 (push) Has been cancelled
All / Test 32/64 bit stable (sudo apt update && sudo apt install gcc-multilib, i686-unknown-linux-gnu) (push) Has been cancelled
All / Test 32/64 bit stable (x86_64-unknown-linux-gnu) (push) Has been cancelled
All / Test Nightly (push) Has been cancelled
All / Check that benchmarks compile (push) Has been cancelled
All / Build serial on no_std target (thumbv7em-none-eabi) (curve25519-dalek) (push) Has been cancelled
All / Build serial on no_std target (thumbv7em-none-eabi) (ed25519-dalek) (push) Has been cancelled
All / Build serial on no_std target (thumbv7em-none-eabi) (x25519-dalek) (push) Has been cancelled
An Elligator benchmark was added for the "lizard" extensions, and it
made sure it was only measuring the Elligator part of the work by
expanding a set of bytes into a FieldElement ahead of time. However,
this requires the FieldElement alias to be pub, and depends on the
elligator_ristretto_flavor helper function being pub as well. Changing
it to test the lizard-based entry point means we're diverging less
from upstream, and it's more realistic anyway.
2024-06-18 15:18:14 -07:00
Jordan Rose
fa03398012 Merge upstream tag 'curve25519-4.1.3' 2024-06-18 15:17:10 -07:00
Michael Rosenberg
5312a0311e
curve: Bump version to 4.1.3 (#660)
* Bumped to v4.1.3

* Added recent PRs to changelog
2024-06-18 21:18:51 +02:00
Tony Arcieri
b4f9e4df92
SECURITY: fix timing variability in backend/serial/u32/scalar.rs (#661)
Similar security fix to #659, but for the 32-bit backend. See that PR
for more information about the problem. Relevant compiler outputs (thanks to @tarcieri):

Without fix
https://godbolt.org/z/zvaWxzvqv
Notice the `jns` ("jump if not sign") instruction on line 106.

With fix
https://godbolt.org/z/jc9j7eb8E
2024-06-18 21:02:37 +02:00
Michael Rosenberg
415892acf1
SECURITY: fix timing variability in backend/serial/u64/scalar.rs (#659)
Timing variability of any kind is problematic when working with
potentially secret values such as elliptic curve scalars, and such
issues can potentially leak private keys and other secrets. Such a
problem was recently discovered in `curve25519-dalek`.

The `Scalar52::sub` function contained usage of a mask value inside of a
loop where LLVM saw an opportunity to insert a branch instruction
(`jns` on x86) to conditionally bypass this code section when the mask
value is set to zero, as can be seen in godbolt:

https://godbolt.org/z/PczYj7Pda

A similar problem was recently discovered in the Kyber reference
implementation:

https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/hqbtIGFKIpU/m/cnE3pbueBgAJ

As discussed on that thread, one portable solution, which is also used
in this PR, is to introduce a volatile read as an optimization barrier,
which prevents the compiler from optimizing it away.

The fix can be validated in godbolt here:

https://godbolt.org/z/x8d46Yfah

The problem was discovered and the solution independently verified by
Alexander Wagner <alexander.wagner@aisec.fraunhofer.de> and
Lea Themint <lea.thiemt@tum.de> using their DATA tool:

https://github.com/Fraunhofer-AISEC/DATA

Co-authored-by: Tony Arcieri <bascule@gmail.com>
2024-06-18 19:49:31 +02:00
Isaiah Becker-Mayer
56bf398d0c
Updates license field to valid SPDX format (#647) 2024-06-03 14:30:13 -06:00
pinkforest(she/her)
9252fa5c0d
Mitigate check-cfg until MSRV 1.77 (#652) 2024-05-09 07:24:16 -06:00
Hiroki Kobayashi
1efe6a93b1
Fix a minor typo in signing.rs (#649)
an -> a
2024-04-13 19:37:33 -06:00
Boyd Kane
cc3421a22f
Indicate that the rand_core feature is required (#641) 2024-03-16 07:43:25 -06:00
pinkforest(she/her)
858c4ca8ae
Address new nightly clippy unnecessary qualifications (#639) 2024-03-07 16:58:20 -07:00
pinkforest(she/her)
31ccb67050
Remove platforms in favor using CARGO_CFG_TARGET_POINTER_WIDTH (#636) 2024-03-01 07:35:23 -07:00
pinkforest(she/her)
19c7f4a5d5
Fix new nightly redundant import lint warns (#638) 2024-02-29 18:56:52 -07:00
Flori
a62e4a5c57
Fix minor spelling mistakes (#629) 2024-02-14 12:01:05 -07:00
Jack Lloyd
17eab3d6c1
ed: Make it possible to convert between VerifyingKey and EdwardsPoint (#624)
Adds VerifyingKey::to_edwards and a From conversion

See #623
2024-02-12 14:36:43 -05:00
Tony Arcieri
50401ab430
curve: mark ValidityCheck trait as allow(dead_code) (#625)
Recent nightlies have started emitting a dead code lint
2024-02-12 11:56:06 -05:00
Michael Rosenberg
4ac84dd066
curve,ed,x: Bump patch version to reflect fix to nightly SIMD build (#621) 2024-02-06 20:09:18 -05:00
Jimmy Chen
ff1c309b23
Fix nightly build (#619)
* Fix nightly build

* Add nightly feature constraint so AVX-512 requires either x86 or x86_64

Co-authored-by: Tony Arcieri <bascule@gmail.com>

* fmt

---------

Co-authored-by: Michael Rosenberg <micro@fastmail.com>
Co-authored-by: Tony Arcieri <bascule@gmail.com>
Co-authored-by: Michael Rosenberg <michael@mrosenberg.pub>
2024-02-06 15:09:29 -05:00
Ford
ba737a3790
Update README.md (#613) 2024-01-21 08:27:47 -07:00
Pioua
0b45e00ad5
chore: typo fix (#608) 2023-12-13 08:10:05 -07:00
Alex Konradi
a12ab4e584
Update to upstream v4.1.1 2023-12-06 13:07:48 -05:00
Alex Konradi
7051bd8dcf Merge upstream tag 'curve25519-4.1.1' 2023-12-06 09:41:54 -05:00
Wiktor Kwapisiewicz
ba7a073487
doc: Fix markdown PR reference (#605) 2023-11-22 06:21:20 -07:00
Bram Westerbaan
a2ff6ba9e4
{Signing,Verifying}KeyVisitor: visit_borrowed_bytes -> visit_bytes (#602) 2023-11-17 02:44:28 -05:00
Michael Rosenberg
f08bbb7f57
ed: Prep to release v2.1.0 (#600) 2023-11-14 15:35:42 -05:00
Michael Rosenberg
04f811ad21
ed: Add back SigningKey::to_scalar_bytes (#599)
* Brought back SigningKey::to_scalar_bytes; added regression test

* Updated SigningKey::to_scalar docs and tests
2023-11-14 13:23:48 -05:00
Tony Arcieri
ac51ef6ecf
ed25519: loosen signature crate dependency again (#598)
Like #582, there is a new release of `signature` (v2.2.0) which contains
no breaking changes from ed25519-dalek's perspective. The main notable
one is it bumps MSRV to 1.60, which so also happens to also be
ed25519-dalek's MSRV.

This commit loosens the version requirement to allow `>=2.0, <2.3` to
allow the `signature` 2.2 series.
2023-11-14 00:09:16 -05:00
Tony Arcieri
89aabac235
README.md: remove broken image (#595)
This image duplicates the `curve25519-dalek` table entry below.

It also doesn't actually link to anything, making README.md look broken.
2023-11-01 13:33:43 -04:00
Michael Rosenberg
72761ca6b4
derive: Bump version to 0.1.1 (#594)
* derive: Bump version to 0.1.1

* Added changelog
2023-10-31 13:40:12 -04:00
Tony Arcieri
3c85f778b3
CI: fix minimal-versions resolution (#593)
To avoid nightly regressions breaking the build, the CI configuration
has been updated to *only* use nightly for resolving Cargo.lock by using
`cargo update -Z minimal-versions`.

Previously, it was running `cargo check` which would attempt to compile
all of the dependencies and the code, which is why the diagnostic bug
was triggered. By avoiding any kind of code compilation using nightly we
can avoid such regressions in the future.

Additionally, the clippy job has been changed to run on the latest
stable release (1.73.0) rather than nightly, which will prevent future
clippy lints from breaking the build. Instead, they can be addressed
when clippy is updated.
2023-10-31 12:04:34 -04:00
Tony Arcieri
78a86f1c49
ed25519-dalek: hide secret in SigningKey's Debug impl (#592)
Uses `finish_non_exhaustive` in lieu of printing the `secret_key`
component of a `SigningKey`, only showing the corresponding
`verifying_key` field which can be used to identify the public key.

Closes #591
2023-10-31 12:01:09 -04:00
Michael Rosenberg
f4cd43f606
Merge pull request #590 from dalek-cryptography/derive-license
Fix licensing on -derive repo
2023-10-31 11:35:38 -04:00
Michael Rosenberg
81d0756bdc
Made unnecessarily pub contents of field.rs pub(crate) 2023-10-29 22:06:47 -04:00
Michael Rosenberg
cd9378e6fd
Removed unnecessary 'pub use' 2023-10-29 21:53:08 -04:00
Michael Rosenberg
8a41a29939
Forgot the license files 2023-10-29 10:50:17 -04:00
Michael Rosenberg
b92421916d
Copy licensing from previous repo 2023-10-29 10:47:45 -04:00
Tony Arcieri
598695c400
ed25519: loosen signature crate dependency (#582)
The `signature` crate contains unstable, minor version-gated
functionality.

The v2.1 release did not change any of that, and only added new
functionality. So it's safe to relax the requirement for `signature` to
`>=2.0, <2.2`.
2023-10-27 00:29:56 -04:00
Victor Graf
e6675c67ce
add cfg statements to only build doctest on x86 (#585) 2023-10-03 12:51:05 -06:00
Michael Rosenberg
0cd099a9fb
curve: Bump version to 4.1.1 (#584) 2023-09-20 17:42:22 -05:00
Luke Parker
76a8b2a081
Add PrimeFieldBits support to Scalar (#579)
Co-authored-by: Michael Rosenberg <micro@fastmail.com>
Co-authored-by: pinkforest(she/her) <36498018+pinkforest@users.noreply.github.com>
2023-09-19 23:21:43 -04:00
pinkforest(she/her)
533b53a0ec
Deprecate BASEPOINT_ORDER from pub API consts (#581)
* Mark constants::BASEPOINT_ORDER_PRIVATE deprecated from pub API

* Move all BASEPOINT_ORDER use private internally

Co-authored-by: Tony Arcieri <bascule@gmail.com>

* Fix CHANGELOG for 4.1.1

---------

Co-authored-by: Tony Arcieri <bascule@gmail.com>
2023-09-17 23:59:05 -04:00
Luke Parker
c157a1ed6d
Add group to documented features (#578) 2023-09-12 07:41:15 -06:00
Michael Rosenberg
e94a5fe5ab
curve: README typos 2023-09-06 00:53:30 -04:00
pinkforest(she/her)
9db51a6bf7
curve: Release 4.1.0 (#574)
Co-authored-by: Rob Ede <robjtede@icloud.com>
2023-09-06 00:51:15 -04:00
Michael Rosenberg
8ed1666b97
ed,x: updated repo links 2023-09-06 00:49:26 -04:00
Tony Arcieri
1ec4a36a80
curve: update repository in Cargo.toml (#575)
Point to the subdirectory which contains the crate
2023-09-06 00:08:06 -04:00
David Cook
a3a08b01ab
Adapt to new types introduced in fiat-crypto 0.2 (#566) 2023-09-05 10:07:49 -06:00
Wiktor Kwapisiewicz
135476c9f5
Fix variable names in the invariant description (#573)
Previously the variable names referred to `public` and `secret` which do
not exist. Update them to `verifying_key` and `secret_key`.
2023-09-05 08:50:10 -06:00
pinkforest(she/her)
5c5a32057c
curve: Fix no_std for fiat backend and add test for it (#572) 2023-09-04 13:49:58 -06:00
Rob Ede
c8d1d400f1
curve,ed: chore: update dev deps (#569) 2023-08-28 09:46:38 -04:00
Tony Arcieri
60dd3100c0
curve: add doc(hidden) to serial backend modules (#568)
We have a lot of backend types leaking via the public API, including
e.g. `FieldElement51`:

https://docs.rs/curve25519-dalek/latest/curve25519_dalek/backend/serial/u64/field/struct.FieldElement51.html

At the very least, these types shouldn't be visible in the rustdoc.

This PR hides them from the docs, but ideally we would hide them
completely from the public API (which might technically be considered a
breaking change, but IMO leaking them at all is a bug).
2023-08-28 02:38:11 -04:00
Michael Rosenberg
594b1f9ffe
Updated Cargo.toml repo and homepage links to the Github monorepo 2023-08-28 02:36:14 -04:00
Tony Arcieri
c058cd9057
curve: Expand lints (#530)
Adds a lints section to the top of lib.rs with the following:

    #![warn(
        clippy::unwrap_used,
        missing_docs,
        rust_2018_idioms,
        unused_lifetimes,
        unused_qualifications
    )]

`warn` is used instead of `deny` to prevent the lints from firing during
local development, however we already configure `-D warnings` in CI so
if any lint fails on checked-in code, it will cause a CI failure.

This commit also fixes or explicitly allows any current violations of
these lints. The main ones were:

- `clippy::unwrap_used`: replaces usages of `unwrap` with `expect`
- `rust_2018_idioms`: no implicit lifetimes, which were present on
  usages of `core::fmt::Formatter`
2023-08-28 02:32:31 -04:00
Michael Rosenberg
8e0cef5b72
curve: Add arbitrary integer multiplication with MontgomeryPoint::mul_bits_be (#555)
There is occasionally [a need](https://github.com/dalek-cryptography/curve25519-dalek/pull/519#issuecomment-1637770888) to multiply a non-prime-order Montgomery point by an integer. There's currently no way to do this, since our only methods are multiplication by `Scalar` (doesn't make sense in the non-prime-order case), and `MontgomeryPoint::mul_base_clamped` clamps the integer before multiplying.

This defines `MontgomeryPoint::mul_bits_be`, which takes a big-endian representation of an integer and multiplies the point by that integer. Its usage is not recommended by default, but it is also not so unsafe as to be gated behind a `hazmat` feature.
2023-08-28 01:58:41 -04:00
pinkforest(she/her)
4373695c50
curve: implement ff and group traits (#562)
Originally authored by @str4d as #473
2023-08-27 14:41:06 -06:00
Sören Meier
098658dc8b
ed: Add SigningKey::as_bytes (#561)
Allows to get a reference to the secret bytes without making a copy.
2023-08-27 14:28:06 -06:00
Rob Ede
b93ace8c7f
Address Clippy lints (#543) 2023-08-27 12:47:12 -06:00
Matt Johnston
c66973c823
ed: ConstantTimeEq and PartialEq for SigningKey (#557) 2023-08-12 01:49:16 -04:00
moiseev-signal
bf2c4eea23
curve: Mark scalar::clamp_integer as must_use (#558) 2023-08-12 01:44:09 -04:00
Elichai Turkel
6dd17b2836
x: Mark x25519-dalek version 2 as stable (#554) 2023-08-11 18:18:15 -04:00
Michael Rosenberg
42b55fd117
ed: Bump ed25519-dalek to 2.0.0 (#559)
* Made clippy happy
2023-08-11 11:38:43 -04:00
Tony Arcieri
345364d4ec
Update README.md
Use non-breaking hyphens in crate names in table
2023-07-27 18:17:00 -06:00
145 changed files with 39404 additions and 20973 deletions

44
.github/workflows/cross.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: Cross
on:
push:
branches: [ '**' ]
pull_request:
branches: [ '**' ]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
jobs:
test-cross:
name: Test
strategy:
matrix:
include:
# ARM32
- target: armv7-unknown-linux-gnueabihf
rust: stable
# ARM64
- target: aarch64-unknown-linux-gnu
rust: stable
# PPC32
- target: powerpc-unknown-linux-gnu
rust: stable
# TODO: We only test x/ed/curve for cross as derive is platform specifics
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ${{ matrix.deps }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- uses: RustCrypto/actions/cross-install@master
- run: cross test -p curve25519-dalek --release --target ${{ matrix.target }}
- run: cross test -p ed25519-dalek --release --target ${{ matrix.target }}
- run: cross test -p x25519-dalek --release --target ${{ matrix.target }}

145
.github/workflows/curve25519-dalek.yml vendored Normal file
View File

@ -0,0 +1,145 @@
name: curve25519 Rust
on:
push:
branches: [ '**' ]
paths:
- 'curve25519-dalek/**'
- '.github/workflows/curve25519-dalek.yml'
pull_request:
branches: [ '**' ]
paths:
- 'curve25519-dalek/**'
- '.github/workflows/curve25519-dalek.yml'
defaults:
run:
working-directory: curve25519-dalek
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
jobs:
test-fiat:
name: Test fiat backend
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib
# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
run: cargo test --target ${{ matrix.target }}
# Default no_std test only tests using serial across all crates
build-nostd-fiat:
name: Build fiat on no_std target (thumbv7em-none-eabi)
runs-on: ubuntu-latest
strategy:
matrix:
include:
- crate: curve25519-dalek
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: thumbv7em-none-eabi
- uses: taiki-e/install-action@cargo-hack
# No default features build
- name: no_std fiat / no feat ${{ matrix.crate }}
env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features
- name: no_std fiat / cargo hack ${{ matrix.crate }}
env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,getrandom
test-serial:
name: Test serial backend
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib
# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="serial"'
run: cargo test --target ${{ matrix.target }}
build-script:
name: Test Build Script
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu,i686-unknown-linux-gnu
- run: bash tests/build_tests.sh
test-simd-nightly:
name: Test simd backend (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
- env:
# This will:
# 1) build all of the x86_64 SIMD code,
# 2) run all of the SIMD-specific tests that the test runner supports,
# 3) run all of the normal tests using the best available SIMD backend.
# This should automatically pick up the simd backend in a x84_64 runner
RUSTFLAGS: '-C target_cpu=native'
run: cargo test --target x86_64-unknown-linux-gnu
test-simd-stable:
name: Test simd backend (stable)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- env:
# This will run AVX2-specific tests and run all of the normal tests
# with the AVX2 backend, even if the runner supports AVX512.
# This should automatically pick up the simd backend in a x86_64 runner
# It should pick AVX2 due to stable toolchain used since AVX512 requires nigthly
RUSTFLAGS: '-C target_feature=+avx2'
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,group-bits --target x86_64-unknown-linux-gnu
msrv:
name: Current MSRV is 1.60.0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Re-resolve Cargo.lock with minimal versions
- uses: dtolnay/rust-toolchain@nightly
- run: cargo update -Z minimal-versions
# Now check that `cargo build` works with respect to the oldest possible
# deps and the stated MSRV
- uses: dtolnay/rust-toolchain@1.60.0
- run: cargo build --no-default-features --features serde
# Also make sure the AVX2 build works
- run: cargo build --target x86_64-unknown-linux-gnu

33
.github/workflows/ed25519-dalek.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: ed25519 Rust
on:
push:
branches: [ '**' ]
paths: 'ed25519-dalek/**'
pull_request:
branches: [ '**' ]
paths: 'ed25519-dalek/**'
defaults:
run:
working-directory: ed25519-dalek
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
RUSTDOCFLAGS: '-D warnings'
jobs:
msrv:
name: Current MSRV is 1.60.0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Re-resolve Cargo.lock with minimal versions
- uses: dtolnay/rust-toolchain@nightly
- run: cargo update -Z minimal-versions
# Now check that `cargo build` works with respect to the oldest possible
# deps and the stated MSRV
- uses: dtolnay/rust-toolchain@1.60.0
- run: cargo build

View File

@ -1,131 +0,0 @@
name: Rust
on:
push:
branches: [ '*' ]
pull_request:
branches: [ main, develop ]
env:
CARGO_TERM_COLOR: always
jobs:
test-u32:
name: Test u32 backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features "std u32_backend"
test-u64:
name: Test u64 backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features "std u64_backend"
test-simd:
name: Test simd backend (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features "std simd_backend"
test-defaults-serde:
name: Test default feature selection and serde
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --features "serde"
test-alloc-u32:
name: Test no_std+alloc with u32 backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --lib --no-default-features --features "alloc u32_backend"
nightly:
name: Test nightly compiler
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --features "nightly"
msrv:
name: Current MSRV is 1.41
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.41
override: true
- uses: actions-rs/cargo@v1
with:
command: build
bench:
name: Check that benchmarks compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: bench
# This filter selects no benchmarks, so we don't run any, only build them.
args: "DONTRUNBENCHMARKS"

111
.github/workflows/workspace.yml vendored Normal file
View File

@ -0,0 +1,111 @@
name: All
on:
push:
branches: [ '**' ]
pull_request:
branches: [ '**' ]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
jobs:
test-stable:
name: Test 32/64 bit stable
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib
# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --no-default-features
- run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --all-features
test-nightly:
name: Test Nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test
bench:
name: Check that benchmarks compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Build u32 bench
env:
RUSTFLAGS: '--cfg curve25519_dalek_bits="32"'
run: cargo build --benches
- name: Build u64 bench
env:
RUSTFLAGS: '--cfg curve25519_dalek_bits="64"'
run: cargo build --benches
- name: Build default (host native) bench
run: cargo build --benches
# Test no_std with serial (default)
build-nostd-serial:
name: Build serial on no_std target (thumbv7em-none-eabi)
runs-on: ubuntu-latest
strategy:
matrix:
include:
- crate: curve25519-dalek
- crate: ed25519-dalek
- crate: x25519-dalek
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: thumbv7em-none-eabi
- uses: taiki-e/install-action@cargo-hack
# No default features build
- name: no_std / no feat ${{ matrix.crate }}
run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features
- name: no_std / cargo hack ${{ matrix.crate }}
run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,getrandom
clippy:
name: Check that clippy is happy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@1.73.0
with:
components: clippy
- run: cargo clippy --target x86_64-unknown-linux-gnu --all-features
rustfmt:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
doc:
name: Check docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- run: cargo doc --all-features

33
.github/workflows/x25519-dalek.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: x25519 Rust
on:
push:
branches: [ '**' ]
paths: 'x25519-dalek/**'
pull_request:
branches: [ '**' ]
paths: 'x25519-dalek/**'
defaults:
run:
working-directory: x25519-dalek
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
RUSTDOCFLAGS: '-D warnings'
jobs:
msrv:
name: Current MSRV is 1.60.0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Re-resolve Cargo.lock with minimal versions
- uses: dtolnay/rust-toolchain@nightly
- run: cargo update -Z minimal-versions
# Now check that `cargo build` works with respect to the oldest possible
# deps and the stated MSRV
- uses: dtolnay/rust-toolchain@1.60.0
- run: cargo build

4
.gitignore vendored
View File

@ -1,6 +1,8 @@
*/target/*
target
Cargo.lock
*/Cargo.lock
build*.txt
*~
\#*
.\#*

View File

@ -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

View File

@ -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.

View File

@ -12,7 +12,7 @@ Patches are welcomed as pull requests on
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.
ask @rozbb or @tarcieri.
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

View File

@ -1,74 +1,12 @@
[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.1"
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
"Henry de Valence <hdevalence@hdevalence.ca>"]
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"
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",
[workspace]
members = [
"curve25519-dalek",
"curve25519-dalek-derive",
"ed25519-dalek",
"x25519-dalek"
]
resolver = "2"
[package.metadata.docs.rs]
# Disabled for now since this is borked; tracking https://github.com/rust-lang/docs.rs/issues/302
# 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"]
[profile.dev]
opt-level = 2
[badges]
travis-ci = { repository = "dalek-cryptography/curve25519-dalek", branch = "master"}
[dev-dependencies]
sha2 = { version = "0.9", default-features = false }
bincode = "1"
criterion = { version = "0.3.0", features = ["html_reports"] }
hex = "0.4.2"
rand = "0.7"
[[bench]]
name = "dalek_benchmarks"
harness = false
[dependencies]
rand_core = { version = "0.5", default-features = false }
byteorder = { version = "^1.2.3", default-features = false, features = ["i128"] }
digest = { version = "0.9", default-features = false }
subtle = { version = "^2.2.1", default-features = false }
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
# The original packed_simd package was orphaned, see
# https://github.com/rust-lang/packed_simd/issues/303#issuecomment-701361161
packed_simd = { version = "0.3.4", package = "packed_simd_2", features = ["into_bits"], optional = true }
zeroize = { version = ">=1, <1.4", default-features = false }
fiat-crypto = { version = "0.1.6", optional = true}
[features]
nightly = ["subtle/nightly"]
default = ["std", "u64_backend"]
std = ["alloc", "subtle/std", "rand_core/std"]
alloc = ["zeroize/alloc"]
# The u32 backend uses u32s with u64 products.
u32_backend = []
# The u64 backend uses u64s with u128 products.
u64_backend = []
# fiat-u64 backend (with formally-verified field arith) uses u64s with u128 products.
fiat_u64_backend = ["fiat-crypto"]
# fiat-u32 backend (with formally-verified field arith) uses u32s with u64 products.
fiat_u32_backend = ["fiat-crypto"]
# The SIMD backend uses parallel formulas, using either AVX2 or AVX512-IFMA.
simd_backend = ["nightly", "u64_backend", "packed_simd"]
# DEPRECATED: this is now an alias for `simd_backend` and may be removed
# in some future release.
avx2_backend = ["simd_backend"]

View File

@ -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

235
README.md
View File

@ -1,226 +1,31 @@
# 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)
<p align="center">
<img
width="33%"
align="right"
src="https://doc.dalek.rs/assets/dalek-logo-clear.png"/>
alt="dalek-cryptography logo: a dalek with edwards curves as sparkles coming out of its radar-schnozzley blaster thingies"
width="200px"
src="https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png"/>
</p>
**A pure-Rust implementation of group operations on Ristretto and Curve25519.**
# Dalek elliptic curve cryptography
`curve25519-dalek` is a library providing group operations on the Edwards and
Montgomery forms of Curve25519, and on the prime-order Ristretto group.
This repo contains pure-Rust crates for elliptic curve cryptography:
`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.
| Crate | Description | Crates.io | Docs | CI |
-------------------------------------------|----------------|-----------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| [`curve25519dalek`](./curve25519-dalek) | A library for arithmetic over the Curve25519 and Ristretto elliptic curves and their associated scalars. | [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) | [![](https://img.shields.io/docsrs/curve25519-dalek)](https://docs.rs/curve25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml) |
| [`ed25519dalek`](./ed25519-dalek) | An implementation of the EdDSA digital signature scheme over Curve25519. | [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) | [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml) |
| [`x25519dalek`](./x25519-dalek) | An implementation of elliptic curve Diffie-Hellman key exchange over Curve25519. | [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) | [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml) |
`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.
There is also the [`curve25519-dalek-derive`](./curve25519-dalek-derive) crate, which is just a helper crate with some macros that make curve25519-dalek easier to write.
# Contributing
Please see [CONTRIBUTING.md][contributing].
Please see [`CONTRIBUTING.md`](./CONTRIBUTING.md).
Patches and pull requests should be make against the `develop`
branch, **not** `main`.
# Code of Conduct
# About
We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html),
with the following additional clauses:
**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/
* 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.

View File

@ -0,0 +1,8 @@
# Changelog
Entries are listed in reverse chronological order per undeprecated
major series.
### 0.1.1
* Copied over license files from [original](https://github.com/koute/unsafe_target_feature/tree/389ae00d34cf0ff589cb8d9b38a85ae1b05ebfdc) repo

View File

@ -0,0 +1,19 @@
[package]
name = "curve25519-dalek-derive"
version = "0.1.1"
edition = "2021"
repository = "https://github.com/dalek-cryptography/curve25519-dalek"
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/curve25519-dalek-derive"
license = "MIT OR Apache-2.0"
readme = "README.md"
description = "curve25519-dalek Derives"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.66"
quote = "1.0.31"
syn = { version = "2.0.27", features = ["full"] }

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,198 @@
# A more convenient `#[target_feature]` replacement
To get good performance out of SIMD everything on the SIMD codepath must be inlined.
With how SIMD is currently implemented in Rust one of two things have to be true for
a function using SIMD to be inlinable: (and this includes the SIMD intrinsics themselves)
a) The whole program has to be compiled with the relevant `-C target-cpu` or `-C target-feature` flags.
b) SIMD support must be automatically detected at runtime, and every function on the SIMD codepath must be marked with `#[target_feature]`.
Both have their downsides. Setting the `target-cpu` or `target-features` makes the resulting binary
incompatible with older CPUs, while using `#[target_feature]` is incredibly inconvenient.
This crate is meant to make `#[target_feature]` less painful to use.
## Problems with `#[target_feature]`
When we're not compiling with the relevant `target-cpu`/`target-feature` flags everything on
the SIMD codepath must be marked with the `#[target_feature]` attribute. This is not a problem
when all of your SIMD code is neatly encapsulated inside of a single function, but once you start
to build out more elaborate abstractions it starts to become painful to use.
* It can only be used on `unsafe` functions, so everything on your SIMD codepath now has to be `unsafe`.
In theory this is nice - these functions require the relevant SIMD instructions to be present at runtime,
so calling them without checking is obviously unsafe! But in practice this is rarely what you want. When
you build an abstraction over SIMD code you usually want to assume that *internally* within your module
all of the necessary SIMD instructions are available, and you only want to check this at the boundaries
when you're first entering your module. You do *not* want to infect everything *inside* of the module with
`unsafe` since you've already checked this invariant at the module's API boundary.
* It cannot be used on non-`unsafe` trait methods.
If you're implementing a trait, say for example `std::ops::Add`, then you cannot mark the method `unsafe`
unless the original trait also has it marked as `unsafe`, and usually it doesn't.
* It makes it impossible to abstract over a given SIMD instruction set using a trait.
For example, let's assume you want to abstract over which SIMD instructions you use using a trait in the following way:
```rust
trait Backend {
unsafe fn sum(input: &[u32]) -> u32;
}
struct AVX;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Backend for AVX {
#[target_feature(enable = "avx")]
unsafe fn sum(xs: &[u32]) -> u32 {
// ...
todo!();
}
}
struct AVX2;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Backend for AVX2 {
#[target_feature(enable = "avx2")]
unsafe fn sum(xs: &[u32]) -> u32 {
// ...
todo!();
}
}
// And now you want a have function which calls into that trait:
unsafe fn do_calculations<B>(xs: &[u32]) -> u32 where B: Backend {
let value = B::sum(xs);
// ...do some more calculations here...
value
}
```
We have a problem here. This has to be marked with `#[target_feature]`, and that has to specify the concrete
feature flag for a given SIMD instruction set, but this function is generic so we can't do that!
## How does this crate make it better?
### You can now mark safe functions with `#[target_feature]`
This crate exposes an `#[unsafe_target_feature]` macro which works just like `#[target_feature]` except
it moves the `unsafe` from the function prototype into the macro name, and can be used on safe functions.
```rust,compile_fail
// ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions
#[target_feature(enable = "avx2")]
fn func() {}
```
```rust
// It works, but must be `unsafe`
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx2")]
unsafe fn func() {}
```
```rust
use curve25519_dalek_derive::unsafe_target_feature;
// No `unsafe` on the function itself!
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature("avx2")]
fn func() {}
```
It can also be used to mark functions inside of impls:
```rust,compile_fail
struct S;
impl core::ops::Add for S {
type Output = S;
// ERROR: method `add` has an incompatible type for trait
#[target_feature(enable = "avx2")]
unsafe fn add(self, rhs: S) -> S {
S
}
}
```
```rust
use curve25519_dalek_derive::unsafe_target_feature;
struct S;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature("avx2")]
impl core::ops::Add for S {
type Output = S;
// No `unsafe` on the function itself!
fn add(self, rhs: S) -> S {
S
}
}
```
### You can generate specialized copies of a module for each target feature
```rust
use curve25519_dalek_derive::unsafe_target_feature_specialize;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))]
mod simd {
#[for_target_feature("sse2")]
pub const CONSTANT: u32 = 1;
#[for_target_feature("avx2")]
pub const CONSTANT: u32 = 2;
#[for_target_feature("avx512ifma")]
pub const CONSTANT: u32 = 3;
pub fn func() { /* ... */ }
}
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn entry_point() {
#[cfg(nightly)]
if std::is_x86_feature_detected!("avx512ifma") {
return simd_avx512ifma::func();
}
if std::is_x86_feature_detected!("avx2") {
return simd_avx2::func();
}
if std::is_x86_feature_detected!("sse2") {
return simd_sse2::func();
}
unimplemented!();
}
```
## How to use `#[unsafe_target_feature]`?
- Can be used on `fn`s, `impl`s and `mod`s.
- When used on a function will only apply to that function; it won't apply to any nested functions, traits, mods, etc.
- When used on an `impl` will only apply to all of the functions directly defined inside of that `impl`.
- When used on a `mod` will only apply to all of the `fn`s and `impl`s directly defined inside of that `mod`.
- Cannot be used on methods which use `self` or `Self`; instead use it on the `impl` in which the method is defined.
## License
Licensed under either of
* Apache License, Version 2.0, [LICENSE-APACHE](LICENSE-APACHE)
* MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View File

@ -0,0 +1,466 @@
#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::spanned::Spanned;
macro_rules! unsupported_if_some {
($value:expr) => {
if let Some(value) = $value {
return syn::Error::new(value.span(), "unsupported by #[unsafe_target_feature(...)]")
.into_compile_error()
.into();
}
};
}
macro_rules! unsupported {
($value: expr) => {
return syn::Error::new(
$value.span(),
"unsupported by #[unsafe_target_feature(...)]",
)
.into_compile_error()
.into()
};
}
mod kw {
syn::custom_keyword!(conditional);
}
enum SpecializeArg {
LitStr(syn::LitStr),
Conditional(Conditional),
}
impl SpecializeArg {
fn lit(&self) -> &syn::LitStr {
match self {
SpecializeArg::LitStr(lit) => lit,
SpecializeArg::Conditional(conditional) => &conditional.lit,
}
}
fn condition(&self) -> Option<&TokenStream2> {
match self {
SpecializeArg::LitStr(..) => None,
SpecializeArg::Conditional(conditional) => Some(&conditional.attr),
}
}
}
struct Conditional {
lit: syn::LitStr,
attr: TokenStream2,
}
impl syn::parse::Parse for Conditional {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lit = input.parse()?;
input.parse::<syn::Token![,]>()?;
let attr = input.parse()?;
Ok(Conditional { lit, attr })
}
}
impl syn::parse::Parse for SpecializeArg {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::conditional) {
input.parse::<kw::conditional>()?;
let content;
syn::parenthesized!(content in input);
let conditional = content.parse()?;
Ok(SpecializeArg::Conditional(conditional))
} else {
Ok(SpecializeArg::LitStr(input.parse()?))
}
}
}
struct SpecializeArgs(syn::punctuated::Punctuated<SpecializeArg, syn::Token![,]>);
impl syn::parse::Parse for SpecializeArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self(syn::punctuated::Punctuated::parse_terminated(input)?))
}
}
#[proc_macro_attribute]
pub fn unsafe_target_feature(attributes: TokenStream, input: TokenStream) -> TokenStream {
let attributes = syn::parse_macro_input!(attributes as syn::LitStr);
let item = syn::parse_macro_input!(input as syn::Item);
process_item(&attributes, item, true)
}
#[proc_macro_attribute]
pub fn unsafe_target_feature_specialize(
attributes: TokenStream,
input: TokenStream,
) -> TokenStream {
let attributes = syn::parse_macro_input!(attributes as SpecializeArgs);
let item_mod = syn::parse_macro_input!(input as syn::ItemMod);
let mut out = Vec::new();
for attributes in attributes.0 {
let features: Vec<_> = attributes
.lit()
.value()
.split(',')
.map(|feature| feature.replace(' ', ""))
.collect();
let name = format!("{}_{}", item_mod.ident, features.join("_"));
let ident = syn::Ident::new(&name, item_mod.ident.span());
let mut attrs = item_mod.attrs.clone();
if let Some(condition) = attributes.condition() {
attrs.push(syn::Attribute {
pound_token: Default::default(),
style: syn::AttrStyle::Outer,
bracket_token: Default::default(),
meta: syn::Meta::List(syn::MetaList {
path: syn::Ident::new("cfg", attributes.lit().span()).into(),
delimiter: syn::MacroDelimiter::Paren(Default::default()),
tokens: condition.clone(),
}),
});
}
let item_mod = process_mod(
attributes.lit(),
syn::ItemMod {
attrs,
ident,
..item_mod.clone()
},
Some(features),
);
out.push(item_mod);
}
quote::quote! {
#(#out)*
}
.into()
}
fn process_item(attributes: &syn::LitStr, item: syn::Item, strict: bool) -> TokenStream {
match item {
syn::Item::Fn(function) => process_function(attributes, function, None),
syn::Item::Impl(item_impl) => process_impl(attributes, item_impl),
syn::Item::Mod(item_mod) => process_mod(attributes, item_mod, None).into(),
item => {
if strict {
unsupported!(item)
} else {
quote::quote! { #item }.into()
}
}
}
}
fn process_mod(
attributes: &syn::LitStr,
mut item_mod: syn::ItemMod,
spec_features: Option<Vec<String>>,
) -> TokenStream2 {
if let Some((_, ref mut content)) = item_mod.content {
'next_item: for item in content {
if let Some(ref spec_features) = spec_features {
match item {
syn::Item::Const(syn::ItemConst { ref mut attrs, .. })
| syn::Item::Enum(syn::ItemEnum { ref mut attrs, .. })
| syn::Item::ExternCrate(syn::ItemExternCrate { ref mut attrs, .. })
| syn::Item::Fn(syn::ItemFn { ref mut attrs, .. })
| syn::Item::ForeignMod(syn::ItemForeignMod { ref mut attrs, .. })
| syn::Item::Impl(syn::ItemImpl { ref mut attrs, .. })
| syn::Item::Macro(syn::ItemMacro { ref mut attrs, .. })
| syn::Item::Mod(syn::ItemMod { ref mut attrs, .. })
| syn::Item::Static(syn::ItemStatic { ref mut attrs, .. })
| syn::Item::Struct(syn::ItemStruct { ref mut attrs, .. })
| syn::Item::Trait(syn::ItemTrait { ref mut attrs, .. })
| syn::Item::TraitAlias(syn::ItemTraitAlias { ref mut attrs, .. })
| syn::Item::Type(syn::ItemType { ref mut attrs, .. })
| syn::Item::Union(syn::ItemUnion { ref mut attrs, .. })
| syn::Item::Use(syn::ItemUse { ref mut attrs, .. }) => {
let mut index = 0;
while index < attrs.len() {
let attr = &attrs[index];
if matches!(attr.style, syn::AttrStyle::Outer) {
match attr.meta {
syn::Meta::List(ref list)
if is_path_eq(&list.path, "for_target_feature") =>
{
let feature: syn::LitStr = match list.parse_args() {
Ok(feature) => feature,
Err(error) => {
return error.into_compile_error();
}
};
let feature = feature.value();
if !spec_features
.iter()
.any(|enabled_feature| feature == *enabled_feature)
{
*item = syn::Item::Verbatim(Default::default());
continue 'next_item;
}
attrs.remove(index);
continue;
}
_ => {}
}
}
index += 1;
continue;
}
}
_ => {
unsupported!(item_mod);
}
}
}
*item = syn::Item::Verbatim(
process_item(
attributes,
std::mem::replace(item, syn::Item::Verbatim(Default::default())),
false,
)
.into(),
);
}
}
quote::quote! {
#item_mod
}
}
fn process_impl(attributes: &syn::LitStr, mut item_impl: syn::ItemImpl) -> TokenStream {
unsupported_if_some!(item_impl.defaultness);
unsupported_if_some!(item_impl.unsafety);
let mut items = Vec::new();
for item in item_impl.items.drain(..) {
match item {
syn::ImplItem::Fn(function) => {
unsupported_if_some!(function.defaultness);
let function = syn::ItemFn {
attrs: function.attrs,
vis: function.vis,
sig: function.sig,
block: Box::new(function.block),
};
let output_item = process_function(
attributes,
function,
Some((item_impl.generics.clone(), item_impl.self_ty.clone())),
);
items.push(syn::ImplItem::Verbatim(output_item.into()));
}
item => items.push(item),
}
}
item_impl.items = items;
quote::quote! {
#item_impl
}
.into()
}
fn is_path_eq(path: &syn::Path, ident: &str) -> bool {
let segments: Vec<_> = ident.split("::").collect();
path.segments.len() == segments.len()
&& path
.segments
.iter()
.zip(segments.iter())
.all(|(segment, expected)| segment.ident == expected && segment.arguments.is_none())
}
fn process_function(
attributes: &syn::LitStr,
function: syn::ItemFn,
outer: Option<(syn::Generics, Box<syn::Type>)>,
) -> TokenStream {
if function.sig.unsafety.is_some() {
return quote::quote! {
#[target_feature(enable = #attributes)]
#function
}
.into();
}
unsupported_if_some!(function.sig.constness);
unsupported_if_some!(function.sig.asyncness);
unsupported_if_some!(function.sig.abi);
unsupported_if_some!(function.sig.variadic);
let function_visibility = function.vis;
let function_name = function.sig.ident;
let function_return = function.sig.output;
let function_inner_name =
syn::Ident::new(&format!("_impl_{}", function_name), function_name.span());
let function_args = function.sig.inputs;
let function_body = function.block;
let mut function_call_args = Vec::new();
let mut function_args_outer = Vec::new();
let mut function_args_inner = Vec::new();
for (index, arg) in function_args.iter().enumerate() {
match arg {
syn::FnArg::Receiver(receiver) => {
unsupported_if_some!(receiver.attrs.first());
unsupported_if_some!(receiver.colon_token);
if outer.is_none() {
return syn::Error::new(receiver.span(), "unsupported by #[unsafe_target_feature(...)]; put the attribute on the outer `impl`").into_compile_error().into();
}
function_args_inner.push(syn::FnArg::Receiver(receiver.clone()));
function_args_outer.push(syn::FnArg::Receiver(receiver.clone()));
function_call_args.push(syn::Ident::new("self", receiver.self_token.span()));
}
syn::FnArg::Typed(ty) => {
unsupported_if_some!(ty.attrs.first());
match &*ty.pat {
syn::Pat::Ident(pat_ident) => {
unsupported_if_some!(pat_ident.attrs.first());
function_args_inner.push(arg.clone());
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
attrs: Vec::new(),
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: Vec::new(),
by_ref: None,
mutability: None,
ident: pat_ident.ident.clone(),
subpat: None,
})),
colon_token: ty.colon_token,
ty: ty.ty.clone(),
}));
function_call_args.push(pat_ident.ident.clone());
}
syn::Pat::Wild(pat_wild) => {
unsupported_if_some!(pat_wild.attrs.first());
let ident = syn::Ident::new(
&format!("__arg_{}__", index),
pat_wild.underscore_token.span(),
);
function_args_inner.push(arg.clone());
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
attrs: Vec::new(),
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: Vec::new(),
by_ref: None,
mutability: None,
ident: ident.clone(),
subpat: None,
})),
colon_token: ty.colon_token,
ty: ty.ty.clone(),
}));
function_call_args.push(ident);
}
_ => unsupported!(arg),
}
}
}
}
let mut maybe_inline = quote::quote! {};
let mut maybe_outer_attributes = Vec::new();
let mut maybe_cfg = quote::quote! {};
for attribute in function.attrs {
match &attribute.meta {
syn::Meta::Path(path) if is_path_eq(path, "inline") => {
maybe_inline = quote::quote! { #[inline] };
}
syn::Meta::Path(path) if is_path_eq(path, "test") => {
maybe_outer_attributes.push(attribute);
maybe_cfg = quote::quote! { #[cfg(target_feature = #attributes)] };
}
syn::Meta::List(syn::MetaList { path, tokens, .. })
if is_path_eq(path, "inline") && tokens.to_string() == "always" =>
{
maybe_inline = quote::quote! { #[inline] };
}
syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if is_path_eq(path, "doc") => {
maybe_outer_attributes.push(attribute);
}
syn::Meta::List(syn::MetaList { path, .. })
if is_path_eq(path, "cfg")
|| is_path_eq(path, "allow")
|| is_path_eq(path, "deny") =>
{
maybe_outer_attributes.push(attribute);
}
syn::Meta::Path(path) if is_path_eq(path, "rustfmt::skip") => {
maybe_outer_attributes.push(attribute);
}
_ => unsupported!(attribute),
}
}
let (fn_impl_generics, fn_ty_generics, fn_where_clause) =
function.sig.generics.split_for_impl();
let fn_call_generics = fn_ty_generics.as_turbofish();
if let Some((generics, self_ty)) = outer {
let (outer_impl_generics, outer_ty_generics, outer_where_clause) =
generics.split_for_impl();
let trait_ident =
syn::Ident::new(&format!("__Impl_{}__", function_name), function_name.span());
let item_trait = quote::quote! {
#[allow(non_camel_case_types)]
trait #trait_ident #outer_impl_generics #outer_where_clause {
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause;
}
};
let item_trait_impl = quote::quote! {
impl #outer_impl_generics #trait_ident #outer_ty_generics for #self_ty #outer_where_clause {
#[target_feature(enable = #attributes)]
#maybe_inline
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
}
};
quote::quote! {
#[inline(always)]
#(#maybe_outer_attributes)*
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
#item_trait
#item_trait_impl
unsafe {
<Self as #trait_ident #outer_ty_generics> ::#function_inner_name #fn_call_generics (#(#function_call_args),*)
}
}
}.into()
} else {
quote::quote! {
#[inline(always)]
#maybe_cfg
#(#maybe_outer_attributes)*
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
#[target_feature(enable = #attributes)]
#maybe_inline
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
unsafe {
#function_inner_name #fn_call_generics (#(#function_call_args),*)
}
}
}.into()
}
}

View File

@ -0,0 +1,141 @@
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#![allow(dead_code)]
#![allow(unused_imports)]
use curve25519_dalek_derive::{unsafe_target_feature, unsafe_target_feature_specialize};
#[unsafe_target_feature("sse2")]
/// A doc comment.
fn function(a: u32, b: u32) -> u32 {
a - b
}
#[unsafe_target_feature("sse2")]
fn function_with_const_arg<const N: u32>(b: u32) -> u32 {
N - b
}
#[unsafe_target_feature("sse2")]
fn function_with_where_clause<T>(a: T, b: T) -> T::Output
where
T: Copy + core::ops::Sub,
{
a - b
}
#[unsafe_target_feature("sse2")]
#[rustfmt::skip]
fn function_with_rustfmt_skip() {}
struct Struct {
a: u32,
}
#[unsafe_target_feature("sse2")]
impl Struct {
#[allow(unused_mut)]
fn member_function(&self, mut b: u32) -> u32 {
self.a - b
}
fn member_function_with_const_arg<const N: u32>(self) -> u32 {
self.a - N
}
}
struct StructWithGenerics<T>
where
T: Copy + core::ops::Sub,
{
a: T,
}
#[unsafe_target_feature("sse2")]
impl<T> StructWithGenerics<T>
where
T: Copy + core::ops::Sub,
{
#[inline]
fn member_function(&self, b: T) -> T::Output {
self.a - b
}
}
struct StructWithGenericsNoWhere<T: Copy + core::ops::Sub> {
a: T,
}
#[unsafe_target_feature("sse2")]
impl<T: Copy + core::ops::Sub> StructWithGenericsNoWhere<T> {
#[inline(always)]
fn member_function(&self, b: T) -> T::Output {
self.a - b
}
}
#[unsafe_target_feature("sse2")]
#[allow(dead_code)]
impl<'a> From<&'a Struct> for () {
fn from(_: &'a Struct) -> Self {}
}
#[unsafe_target_feature("sse2")]
mod inner {
fn inner_function(a: u32, b: u32) -> u32 {
a - b
}
}
#[unsafe_target_feature_specialize("sse2", "avx2")]
mod inner_spec {
#[for_target_feature("sse2")]
const CONST: u32 = 1;
#[for_target_feature("avx2")]
const CONST: u32 = 2;
pub fn spec_function(a: u32, b: u32) -> u32 {
a - b - CONST
}
#[for_target_feature("sse2")]
const IS_AVX2: bool = false;
#[for_target_feature("avx2")]
const IS_AVX2: bool = true;
#[test]
fn test_specialized() {
assert!(!IS_AVX2);
}
#[cfg(test)]
mod tests {
#[test]
fn test_specialized_inner() {
assert!(!super::IS_AVX2);
}
}
}
#[unsafe_target_feature("sse2")]
#[test]
fn test_sse2_only() {}
#[unsafe_target_feature("avx2")]
#[test]
fn test_avx2_only() {
compile_error!();
}
#[test]
fn test_function() {
assert_eq!(function(10, 3), 7);
assert_eq!(function_with_where_clause(10, 3), 7);
assert_eq!(function_with_const_arg::<10>(3), 7);
assert_eq!(Struct { a: 10 }.member_function(3), 7);
assert_eq!(StructWithGenerics { a: 10 }.member_function(3), 7);
assert_eq!(StructWithGenericsNoWhere { a: 10 }.member_function(3), 7);
assert_eq!(inner_spec_sse2::spec_function(10, 3), 6);
assert_eq!(inner_spec_avx2::spec_function(10, 3), 5);
}

View File

@ -3,6 +3,70 @@
Entries are listed in reverse chronological order per undeprecated
major series.
## 4.x series
### 4.1.3
* Security: Fix timing leak in Scalar subtraction on u32, u64, fiat_u32, and fiat_u64 backends
* Fix assorted new warnings and lints from rustc and clippy
### 4.1.2
* Fix nightly SIMD build
### 4.1.1
* Mark `constants::BASEPOINT_ORDER` deprecated from pub API
* Add implementation for `PrimeFieldBits`, behind the `group-bits` feature flag.
### 4.1.0
* Add arbitrary integer multiplication with `MontgomeryPoint::mul_bits_be`
* Add implementations of the `ff` and `group` traits, behind the `group` feature flag
* Adapt to new types introduced in `fiat-crypto` 0.2 in `fiat` backend
* Fix `no_std` for `fiat` backend
* Mark `Scalar::clamp_integer` as `#[must_use]`
* Various documentation fixes
### 4.0.0
#### Breaking changes
* Update the MSRV from 1.41 to 1.60
* Provide SemVer policy
* Make `digest` an optional feature
* Make `rand_core` an optional feature
* Remove `std` feature flag
* Remove `nightly` feature flag
* Automatic serial backend selection between `u32` and `u64` over the default `u32`
* Backend `simd` is now automatically selected over `serial` when a supported CPU is detected
* Backend override is now via cfg(curve25519_dalek_backend) over additive features
* Provide override to select `u32` or `u64` backend via cfg(curve25519_dalek_bits)
* Replace methods `Scalar::{zero, one}` with constants `Scalar::{ZERO, ONE}`
* Deprecate `EdwardsPoint::hash_from_bytes` and rename it `EdwardsPoint::nonspec_map_to_curve`
* Require including a new trait, `use curve25519_dalek::traits::BasepointTable`
whenever using `EdwardsBasepointTable` or `RistrettoBasepointTable`
* `Scalar::from_canonical_bytes` now returns `CtOption`
* `Scalar::is_canonical` now returns `Choice`
* Remove `Scalar::from_bytes_clamped` and `Scalar::reduce`
* Deprecate and feature-gate `Scalar::from_bits` behind `legacy_compatibility`
#### Other changes
* Add `EdwardsPoint::{mul_base, mul_base_clamped}`, `MontgomeryPoint::{mul_base, mul_base_clamped}`, and `BasepointTable::mul_base_clamped`
* Add `precomputed-tables` feature
* Update Maintenance Policies for SemVer
* Migrate documentation to docs.rs hosted
* Fix backend documentation generation
* Fix panic when `Ristretto::double_and_compress_batch` receives the identity point
* Remove `byteorder` dependency
* Update the `criterion` dependency to 0.4.0
* Include README.md into crate Documentation
* Update the `rand_core` dependency version and the `rand` dev-dependency
version.
* Relax the `zeroize` dependency to `^1`
* Update the edition from 2015 to 2021
## 3.x series
### 3.2.0
@ -53,6 +117,8 @@ major series.
### 3.0.0
#### Breaking changes
* 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.
@ -66,7 +132,7 @@ major series.
### 2.1.2
* Multiple documenation typo fixes.
* Multiple documentation typo fixes.
* Fix `alloc` feature working with stable rust.
### 2.1.1
@ -80,12 +146,20 @@ major series.
### 2.0.0
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.
#### Breaking changes
* 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.
#### Other changes
* 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`
@ -94,9 +168,6 @@ major 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

View File

@ -0,0 +1,74 @@
[package]
name = "curve25519-dalek"
# Before incrementing:
# - update CHANGELOG
# - update README if required by semver
# - if README was updated, also update module documentation in src/lib.rs
version = "4.1.3"
edition = "2021"
rust-version = "1.60.0"
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
"Henry de Valence <hdevalence@hdevalence.ca>"]
readme = "README.md"
license = "BSD-3-Clause"
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek"
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/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",
]
[package.metadata.docs.rs]
rustdoc-args = [
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
"--cfg", "docsrs",
]
features = ["serde", "rand_core", "digest", "legacy_compatibility", "group-bits"]
[dev-dependencies]
sha2 = { version = "0.10", default-features = false }
bincode = "1"
criterion = { version = "0.5", features = ["html_reports"] }
hex = "0.4.2"
rand = "0.8"
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
[build-dependencies]
rustc_version = "0.4.0"
[[bench]]
name = "dalek_benchmarks"
harness = false
required-features = ["alloc", "rand_core"]
[dependencies]
cfg-if = "1"
ff = { version = "0.13", default-features = false, optional = true }
group = { version = "0.13", default-features = false, optional = true }
rand_core = { version = "0.6.4", default-features = false, optional = true }
digest = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2.3.0", default-features = false }
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
zeroize = { version = "1", default-features = false, optional = true }
[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2.6"
[target.'cfg(curve25519_dalek_backend = "fiat")'.dependencies]
fiat-crypto = { version = "0.2.1", default-features = false }
[features]
default = ["alloc", "precomputed-tables", "zeroize", "lizard"]
alloc = ["zeroize?/alloc"]
precomputed-tables = []
legacy_compatibility = []
group = ["dep:group", "rand_core"]
group-bits = ["group", "ff/bits"]
lizard = ["digest"]
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
curve25519-dalek-derive = { version = "0.1", path = "../curve25519-dalek-derive" }

11
curve25519-dalek/Makefile Normal file
View File

@ -0,0 +1,11 @@
FEATURES := serde rand_core digest legacy_compatibility
export RUSTDOCFLAGS := \
--cfg docsrs \
--html-in-header docs/assets/rustdoc-include-katex-header.html
doc:
cargo +nightly rustdoc --features "$(FEATURES)"
doc-internal:
cargo +nightly rustdoc --features "$(FEATURES)" -- --document-private-items

323
curve25519-dalek/README.md Normal file
View File

@ -0,0 +1,323 @@
# curve25519-dalek [![](https://buildstats.info/crate/curve25519-dalek)](https://crates.io/crates/curve25519-dalek) [![](https://img.shields.io/docsrs/curve25519-dalek)](https://docs.rs/curve25519-dalek) [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml)
<p align="center">
<img
alt="dalek-cryptography logo: a dalek with edwards curves as sparkles coming out of its radar-schnozzley blaster thingies"
width="200px"
src="https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png"/>
</p>
**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.
# Use
## Stable
To import `curve25519-dalek`, add the following to the dependencies section of
your project's `Cargo.toml`:
```toml
curve25519-dalek = "4"
```
If opting into [SemVer-exempted features](#public-api-semver-exemptions) a range
can be used to scope the tested compatible version range e.g.:
```toml
curve25519-dalek = ">= 4.0, < 4.2"
```
## Feature Flags
| Feature | Default? | Description |
| :--- | :---: | :--- |
| `alloc` | ✓ | Enables Edwards and Ristretto multiscalar multiplication, batch scalar inversion, and batch Ristretto double-and-compress. Also enables `zeroize`. |
| `zeroize` | ✓ | Enables [`Zeroize`][zeroize-trait] for all scalar and curve point types. |
| `precomputed-tables` | ✓ | Includes precomputed basepoint multiplication tables. This speeds up `EdwardsPoint::mul_base` and `RistrettoPoint::mul_base` by ~4x, at the cost of ~30KB added to the code size. |
| `rand_core` | | Enables `Scalar::random` and `RistrettoPoint::random`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `digest` | | Enables `RistrettoPoint::{from_hash, hash_from_bytes}` and `Scalar::{from_hash, hash_from_bytes}`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
| `legacy_compatibility`| | Enables `Scalar::from_bits`, which allows the user to build unreduced scalars whose arithmetic is broken. Do not use this unless you know what you're doing. |
| `group` | | Enables external `group` and `ff` crate traits |
To disable the default features when using `curve25519-dalek` as a dependency,
add `default-features = false` to the dependency in your `Cargo.toml`. To
disable it when running `cargo`, add the `--no-default-features` CLI flag.
## Major Version API Changes
Breaking changes for each major version release can be found in
[`CHANGELOG.md`](CHANGELOG.md), under the "Breaking changes" subheader. The
latest breaking changes in high level are below:
### Breaking changes in 4.0.0
* Update the MSRV from 1.41 to 1.60
* Provide SemVer policy
* Make `digest` and `rand_core` optional features
* Remove `std` and `nightly` features
* Replace backend selection - See [CHANGELOG.md](CHANGELOG.md) and [backends](#backends)
* Replace methods `Scalar::{zero, one}` with constants `Scalar::{ZERO, ONE}`
* `Scalar::from_canonical_bytes` now returns `CtOption`
* `Scalar::is_canonical` now returns `Choice`
* Remove `Scalar::from_bytes_clamped` and `Scalar::reduce`
* Deprecate and feature-gate `Scalar::from_bits` behind `legacy_compatibility`
* Deprecate `EdwardsPoint::hash_from_bytes` and rename it
`EdwardsPoint::nonspec_map_to_curve`
* Require including a new trait, `use curve25519_dalek::traits::BasepointTable`
whenever using `EdwardsBasepointTable` or `RistrettoBasepointTable`
This release also does a lot of dependency updates and relaxations to unblock upstream build issues.
# Backends
Curve arithmetic is implemented and used by one of the following backends:
| Backend | Selection | Implementation | Bits / Word sizes |
| :--- | :--- | :--- | :--- |
| `serial` | Automatic | An optimized, non-parllel implementation | `32` and `64` |
| `fiat` | Manual | Formally verified field arithmetic from [fiat-crypto] | `32` and `64` |
| `simd` | Automatic | Intel AVX2 / AVX512 IFMA accelerated backend | `64` only |
At runtime, `curve25519-dalek` selects an arithmetic backend from the set of backends it was compiled to support. For Intel x86-64 targets, unless otherwise specified, it will build itself with `simd` support, and default to `serial` at runtime if the appropriate CPU features aren't detected. See [SIMD backend] for more details.
In the future, `simd` backend may be extended to cover more instruction sets. This change will be non-breaking as this is considered as implementation detail.
## Manual Backend Override
You can force the crate to compile with specific backend support, e.g., `serial` for x86-64 targets to save code size, or `fiat` to force the runtime to use verified code. To do this, set the environment variable:
```sh
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
```
Equivalently, you can write to
`~/.cargo/config`:
```toml
[build]
rustflags = ['--cfg=curve25519_dalek_backend="BACKEND"']
```
More info [here](https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags).
Note for contributors: The target backends are not entirely independent of each
other. The [SIMD backend] directly depends on parts of the serial backend to
function.
## Bits / Word size
`curve25519-dalek` will automatically choose the word size for the `fiat` and
`serial` backends, based on the build target.
For example, building for a 64-bit machine, the default 64 bit word size is
automatically chosen when either the `serial` or `fiat` backend is selected.
In some targets it might be required to override the word size for better
performance.
Backend word size can be overridden for `serial` and `fiat` by setting the
environment variable:
```sh
RUSTFLAGS='--cfg curve25519_dalek_bits="SIZE"'
```
`SIZE` is `32` or `64`. As in the above section, this can also be placed
in `~/.cargo/config`.
Note: The [SIMD backend] requires a word size of 64 bits. Attempting to set bits=32 and backend=`simd` will yield a compile error.
### Cross-compilation
Because backend selection is done by target, cross-compiling will select the correct word size automatically. For example, if a x86-64 Linux machine runs the following commands, `curve25519-dalek` will be compiled with the 32-bit `serial` backend.
```console
$ sudo apt install gcc-multilib # (or whatever package manager you use)
$ rustup target add i686-unknown-linux-gnu
$ cargo build --target i686-unknown-linux-gnu
```
## SIMD backend
The specific SIMD backend (AVX512 / AVX2 / `serial` default) is selected automatically at runtime, depending on the currently available CPU features, and whether Rust nightly is being used for compilation. The precise conditions are specified below.
For a given CPU feature, you can also specify an appropriate `-C target_feature` to build a binary which assumes the required SIMD instructions are always available. Don't do this if you don't have a good reason.
| Backend | `RUSTFLAGS` | Requires nightly? |
| :--- | :--- | :--- |
| avx2 | `-C target_feature=+avx2` | no |
| avx512 | `-C target_feature=+avx512ifma,+avx512vl` | yes |
If compiled on a non-nightly compiler, `curve25519-dalek` will not include AVX512 code, and therefore will never select it at runtime.
# Documentation
The semver-stable, public-facing `curve25519-dalek` API is documented [here][docs].
## Building Docs Locally
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
```
for regular docs, and
```sh
make doc-internal
```
for docs that include private items.
# Maintenance Policies
All on-by-default features of this library are covered by
[semantic versioning][semver] (SemVer). SemVer exemptions are outlined below
for MSRV and public API.
## Minimum Supported Rust Version
| Releases | MSRV |
| :--- |:-------|
| 4.x | 1.60.0 |
| 3.x | 1.41.0 |
From 4.x and on, MSRV changes will be accompanied by a minor version bump.
## Public API SemVer Exemptions
Breaking changes to SemVer-exempted components affecting the public API will be accompanied by
_some_ version bump. Below are the specific policies:
| Releases | Public API Component(s) | Policy |
| :--- | :--- | :--- |
| 4.x | Dependencies `group`, `digest` and `rand_core` | Minor SemVer bump |
# 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
invoked when the appropriate CPU features are detected at runtime, or
when the whole program is compiled with the appropriate `target_feature`s.
# Performance
Benchmarks are run using [`criterion.rs`][criterion]:
```sh
cargo bench --features "rand_core"
export RUSTFLAGS='-C target_cpu=native'
cargo +nightly bench --features "rand_core"
```
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].
# 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 `fiat_backend` integrates Rust code generated by the
[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) and was
contributed by François Garillot.
Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg,
Pratyush Mishra, Michael Rosenberg, @pinkforest, and countless others for their
contributions.
[ed25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek
[x25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek
[docs]: https://docs.rs/curve25519-dalek/
[contributing]: https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md
[criterion]: https://github.com/japaric/criterion.rs
[parallel_doc]: https://docs.rs/curve25519-dalek/latest/curve25519_dalek/backend/vector/index.html
[subtle_doc]: https://docs.rs/subtle
[fiat-crypto]: https://github.com/mit-plv/fiat-crypto
[semver]: https://semver.org/spec/v2.0.0.html
[rngcorestd]: https://github.com/rust-random/rand/tree/7aa25d577e2df84a5156f824077bb7f6bdf28d97/rand_core#crate-features
[zeroize-trait]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
[SIMD backend]: #simd-backend

View File

@ -1,22 +1,13 @@
#![allow(non_snake_case)]
extern crate rand;
use rand::rngs::OsRng;
use rand::thread_rng;
use rand::{rngs::OsRng, thread_rng};
#[macro_use]
extern crate criterion;
use criterion::measurement::Measurement;
use criterion::BatchSize;
use criterion::Criterion;
use criterion::{BenchmarkGroup, BenchmarkId};
extern crate curve25519_dalek;
use criterion::{
criterion_main, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, Criterion,
};
use curve25519_dalek::constants;
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::field::FieldElement;
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];
@ -26,27 +17,26 @@ mod edwards_benches {
use curve25519_dalek::edwards::EdwardsPoint;
fn compress(c: &mut Criterion) {
fn compress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let B = &constants::ED25519_BASEPOINT_POINT;
c.bench_function("EdwardsPoint compression", move |b| b.iter(|| B.compress()));
}
fn decompress(c: &mut Criterion) {
fn decompress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let B_comp = &constants::ED25519_BASEPOINT_COMPRESSED;
c.bench_function("EdwardsPoint decompression", move |b| {
b.iter(|| B_comp.decompress().unwrap())
});
}
fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
let B = &constants::ED25519_BASEPOINT_TABLE;
fn consttime_fixed_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let s = Scalar::from(897987897u64).invert();
c.bench_function("Constant-time fixed-base scalar mul", move |b| {
b.iter(|| B * &s)
b.iter(|| EdwardsPoint::mul_base(&s))
});
}
fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
fn consttime_variable_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let B = &constants::ED25519_BASEPOINT_POINT;
let s = Scalar::from(897987897u64).invert();
c.bench_function("Constant-time variable-base scalar mul", move |b| {
@ -54,10 +44,10 @@ mod edwards_benches {
});
}
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
fn vartime_double_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
let mut rng = thread_rng();
let A = &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE;
let A = EdwardsPoint::mul_base(&Scalar::random(&mut rng));
bench.iter_batched(
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|(a, b)| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b),
@ -66,15 +56,15 @@ mod edwards_benches {
});
}
criterion_group! {
name = edwards_benches;
config = Criterion::default();
targets =
compress,
decompress,
consttime_fixed_base_scalar_mul,
consttime_variable_base_scalar_mul,
vartime_double_base_scalar_mul,
pub(crate) fn edwards_benches() {
let mut c = Criterion::default();
let mut g = c.benchmark_group("edwards benches");
compress(&mut g);
decompress(&mut g);
consttime_fixed_base_scalar_mul(&mut g);
consttime_variable_base_scalar_mul(&mut g);
vartime_double_base_scalar_mul(&mut g);
}
}
@ -95,14 +85,10 @@ mod multiscalar_benches {
fn construct_points(n: usize) -> Vec<EdwardsPoint> {
let mut rng = thread_rng();
(0..n)
.map(|_| &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE)
.map(|_| EdwardsPoint::mul_base(&Scalar::random(&mut rng)))
.collect()
}
fn construct(n: usize) -> (Vec<Scalar>, Vec<EdwardsPoint>) {
(construct_scalars(n), construct_points(n))
}
fn consttime_multiscalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
for multiscalar_size in &MULTISCALAR_SIZES {
c.bench_with_input(
@ -154,21 +140,21 @@ mod multiscalar_benches {
c.bench_with_input(
BenchmarkId::new(
"Variable-time fixed-base multiscalar multiplication",
&multiscalar_size,
multiscalar_size,
),
&multiscalar_size,
move |b, &&total_size| {
let static_size = total_size;
let static_points = construct_points(static_size);
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
let precomp = VartimeEdwardsPrecomputation::new(static_points);
// Rerandomize the scalars for every call to prevent
// false timings from better caching (e.g., the CPU
// cache lifts exactly the right table entries for the
// benchmark into the highest cache levels).
b.iter_batched(
|| construct_scalars(static_size),
|scalars| precomp.vartime_multiscalar_mul(&scalars),
|scalars| precomp.vartime_multiscalar_mul(scalars),
BatchSize::SmallInput,
);
},
@ -183,66 +169,59 @@ mod multiscalar_benches {
for multiscalar_size in &MULTISCALAR_SIZES {
let bench_id = BenchmarkId::new(
"Variable-time mixed-base",
format!("(size: {:?}), ({:.0}pct dyn)", multiscalar_size, 100.0 * dynamic_fraction),
format!(
"(size: {:?}), ({:.0}pct dyn)",
multiscalar_size,
100.0 * dynamic_fraction
),
);
c.bench_with_input(bench_id, &multiscalar_size,
move |b, &&total_size| {
let dynamic_size = ((total_size as f64) * dynamic_fraction) as usize;
let static_size = total_size - dynamic_size;
c.bench_with_input(bench_id, &multiscalar_size, move |b, &&total_size| {
let dynamic_size = ((total_size as f64) * dynamic_fraction) as usize;
let static_size = total_size - dynamic_size;
let static_points = construct_points(static_size);
let dynamic_points = construct_points(dynamic_size);
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
// Rerandomize the scalars for every call to prevent
// false timings from better caching (e.g., the CPU
// cache lifts exactly the right table entries for the
// benchmark into the highest cache levels). Timings
// should be independent of points so we don't
// randomize them.
b.iter_batched(
|| {
(
construct_scalars(static_size),
construct_scalars(dynamic_size),
)
},
|(static_scalars, dynamic_scalars)| {
precomp.vartime_mixed_multiscalar_mul(
&static_scalars,
&dynamic_scalars,
&dynamic_points,
)
},
BatchSize::SmallInput,
);
},
);
let static_points = construct_points(static_size);
let dynamic_points = construct_points(dynamic_size);
let precomp = VartimeEdwardsPrecomputation::new(static_points);
// Rerandomize the scalars for every call to prevent
// false timings from better caching (e.g., the CPU
// cache lifts exactly the right table entries for the
// benchmark into the highest cache levels). Timings
// should be independent of points so we don't
// randomize them.
b.iter_batched(
|| {
(
construct_scalars(static_size),
construct_scalars(dynamic_size),
)
},
|(static_scalars, dynamic_scalars)| {
precomp.vartime_mixed_multiscalar_mul(
&static_scalars,
&dynamic_scalars,
&dynamic_points,
)
},
BatchSize::SmallInput,
);
});
}
}
fn multiscalar_multiplications(c: &mut Criterion) {
let mut group: BenchmarkGroup<_> = c.benchmark_group("Multiscalar multiplications");
pub(crate) fn multiscalar_benches() {
let mut c = Criterion::default();
let mut g = c.benchmark_group("multiscalar benches");
consttime_multiscalar_mul(&mut group);
vartime_multiscalar_mul(&mut group);
vartime_precomputed_pure_static(&mut group);
consttime_multiscalar_mul(&mut g);
vartime_multiscalar_mul(&mut g);
vartime_precomputed_pure_static(&mut g);
let dynamic_fracs = [0.0, 0.2, 0.5];
for frac in dynamic_fracs.iter() {
vartime_precomputed_helper(&mut group, *frac);
vartime_precomputed_helper(&mut g, *frac);
}
group.finish();
}
criterion_group! {
name = multiscalar_benches;
// Lower the sample size to run the benchmarks faster
config = Criterion::default().sample_size(15);
targets =
multiscalar_multiplications,
}
}
@ -250,26 +229,23 @@ mod ristretto_benches {
use super::*;
use curve25519_dalek::ristretto::RistrettoPoint;
fn compress(c: &mut Criterion) {
fn compress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
c.bench_function("RistrettoPoint compression", |b| {
let B = &constants::RISTRETTO_BASEPOINT_POINT;
b.iter(|| B.compress())
});
}
fn decompress(c: &mut Criterion) {
fn decompress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
c.bench_function("RistrettoPoint decompression", |b| {
let B_comp = &constants::RISTRETTO_BASEPOINT_COMPRESSED;
b.iter(|| B_comp.decompress().unwrap())
});
}
fn elligator(c: &mut Criterion) {
let fe_bytes = [0u8; 32];
let fe = FieldElement::from_bytes(&fe_bytes);
fn elligator<M: Measurement>(c: &mut BenchmarkGroup<M>) {
c.bench_function("RistrettoPoint Elligator", |b| {
b.iter(|| RistrettoPoint::elligator_ristretto_flavor(&fe));
b.iter(|| RistrettoPoint::from_uniform_bytes_single_elligator(&[0u8; 32]));
});
}
@ -289,26 +265,22 @@ mod ristretto_benches {
}
}
fn double_and_compress_group(c: &mut Criterion) {
let mut group: BenchmarkGroup<_> = c.benchmark_group("double & compress batched");
double_and_compress_batch(&mut group);
group.finish();
}
pub(crate) fn ristretto_benches() {
let mut c = Criterion::default();
let mut g = c.benchmark_group("ristretto benches");
criterion_group! {
name = ristretto_benches;
config = Criterion::default();
targets =
compress,
decompress,
double_and_compress_group,
compress(&mut g);
decompress(&mut g);
elligator(&mut g);
double_and_compress_batch(&mut g);
}
}
mod montgomery_benches {
use super::*;
use curve25519_dalek::montgomery::MontgomeryPoint;
fn montgomery_ladder(c: &mut Criterion) {
fn montgomery_ladder<M: Measurement>(c: &mut BenchmarkGroup<M>) {
c.bench_function("Montgomery pseudomultiplication", |b| {
let B = constants::X25519_BASEPOINT;
let s = Scalar::from(897987897u64).invert();
@ -316,21 +288,53 @@ mod montgomery_benches {
});
}
criterion_group! {
name = montgomery_benches;
config = Criterion::default();
targets = montgomery_ladder,
fn consttime_fixed_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let s = Scalar::from(897987897u64).invert();
c.bench_function("Constant-time fixed-base scalar mul", move |b| {
b.iter(|| MontgomeryPoint::mul_base(&s))
});
}
pub(crate) fn montgomery_benches() {
let mut c = Criterion::default();
let mut g = c.benchmark_group("montgomery benches");
montgomery_ladder(&mut g);
consttime_fixed_base_scalar_mul(&mut g);
}
}
mod scalar_benches {
use super::*;
fn scalar_inversion(c: &mut Criterion) {
fn scalar_arith<M: Measurement>(c: &mut BenchmarkGroup<M>) {
let mut rng = thread_rng();
c.bench_function("Scalar inversion", |b| {
let s = Scalar::from(897987897u64).invert();
b.iter(|| s.invert());
});
c.bench_function("Scalar addition", |b| {
b.iter_batched(
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|(a, b)| a + b,
BatchSize::SmallInput,
);
});
c.bench_function("Scalar subtraction", |b| {
b.iter_batched(
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|(a, b)| a - b,
BatchSize::SmallInput,
);
});
c.bench_function("Scalar multiplication", |b| {
b.iter_batched(
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|(a, b)| a * b,
BatchSize::SmallInput,
);
});
}
fn batch_scalar_inversion<M: Measurement>(c: &mut BenchmarkGroup<M>) {
@ -351,18 +355,12 @@ mod scalar_benches {
}
}
fn batch_scalar_inversion_group(c: &mut Criterion) {
let mut group: BenchmarkGroup<_> = c.benchmark_group("batch scalar inversion");
batch_scalar_inversion(&mut group);
group.finish();
}
pub(crate) fn scalar_benches() {
let mut c = Criterion::default();
let mut g = c.benchmark_group("scalar benches");
criterion_group! {
name = scalar_benches;
config = Criterion::default();
targets =
scalar_inversion,
batch_scalar_inversion_group,
scalar_arith(&mut g);
batch_scalar_inversion(&mut g);
}
}

126
curve25519-dalek/build.rs Normal file
View File

@ -0,0 +1,126 @@
//! This selects the curve25519_dalek_bits either by default from target_pointer_width or explicitly set
#![deny(clippy::unwrap_used, dead_code)]
#[allow(non_camel_case_types)]
#[derive(PartialEq, Debug)]
enum DalekBits {
Dalek32,
Dalek64,
}
use std::fmt::Formatter;
impl std::fmt::Display for DalekBits {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let w_bits = match self {
DalekBits::Dalek32 => "32",
DalekBits::Dalek64 => "64",
};
write!(f, "{}", w_bits)
}
}
fn main() {
let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") {
Ok(arch) => arch,
_ => "".to_string(),
};
let curve25519_dalek_bits = match std::env::var("CARGO_CFG_CURVE25519_DALEK_BITS").as_deref() {
Ok("32") => DalekBits::Dalek32,
Ok("64") => DalekBits::Dalek64,
_ => deterministic::determine_curve25519_dalek_bits(&target_arch),
};
println!("cargo:rustc-cfg=curve25519_dalek_bits=\"{curve25519_dalek_bits}\"");
if rustc_version::version_meta()
.expect("failed to detect rustc version")
.channel
== rustc_version::Channel::Nightly
{
println!("cargo:rustc-cfg=nightly");
}
let rustc_version = rustc_version::version().expect("failed to detect rustc version");
if rustc_version.major == 1 && rustc_version.minor <= 64 {
// Old versions of Rust complain when you have an `unsafe fn` and you use `unsafe {}` inside,
// so for those we want to apply the `#[allow(unused_unsafe)]` attribute to get rid of that warning.
println!("cargo:rustc-cfg=allow_unused_unsafe");
}
// Backend overrides / defaults
let curve25519_dalek_backend =
match std::env::var("CARGO_CFG_CURVE25519_DALEK_BACKEND").as_deref() {
Ok("fiat") => "fiat",
Ok("serial") => "serial",
Ok("simd") => {
// simd can only be enabled on x86_64 & 64bit target_pointer_width
match is_capable_simd(&target_arch, curve25519_dalek_bits) {
true => "simd",
// If override is not possible this must result to compile error
// See: issues/532
false => panic!("Could not override curve25519_dalek_backend to simd"),
}
}
// default between serial / simd (if potentially capable)
_ => match is_capable_simd(&target_arch, curve25519_dalek_bits) {
true => "simd",
false => "serial",
},
};
println!("cargo:rustc-cfg=curve25519_dalek_backend=\"{curve25519_dalek_backend}\"");
}
// Is the target arch & curve25519_dalek_bits potentially simd capable ?
fn is_capable_simd(arch: &str, bits: DalekBits) -> bool {
arch == "x86_64" && bits == DalekBits::Dalek64
}
// Deterministic cfg(curve25519_dalek_bits) when this is not explicitly set.
mod deterministic {
use super::*;
// Custom Rust non-cargo build tooling needs to set CARGO_CFG_TARGET_POINTER_WIDTH
static ERR_MSG_NO_POINTER_WIDTH: &str =
"Standard Cargo TARGET_POINTER_WIDTH environment variable is not set.";
// When either non-32 or 64 TARGET_POINTER_WIDTH detected
static ERR_MSG_UNKNOWN_POINTER_WIDTH: &str = "Unknown TARGET_POINTER_WIDTH detected.";
// Warning when the curve25519_dalek_bits cannot be determined
fn determine_curve25519_dalek_bits_warning(cause: &str) {
println!("cargo:warning=\"Defaulting to curve25519_dalek_bits=32: {cause}\"");
}
// Determine the curve25519_dalek_bits based on Rust standard TARGET triplet
pub(super) fn determine_curve25519_dalek_bits(target_arch: &String) -> DalekBits {
let target_pointer_width = match std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH") {
Ok(pw) => pw,
Err(_) => {
determine_curve25519_dalek_bits_warning(ERR_MSG_NO_POINTER_WIDTH);
return DalekBits::Dalek32;
}
};
#[allow(clippy::match_single_binding)]
match &target_arch {
//Issues: 449 and 456
//TODO: When adding arch defaults use proper types not String match
//TODO(Arm): Needs tests + benchmarks to back this up
//TODO(Wasm32): Needs tests + benchmarks to back this up
_ => match target_pointer_width.as_ref() {
"64" => DalekBits::Dalek64,
"32" => DalekBits::Dalek32,
// Intended default solely for non-32/64 target pointer widths
// Otherwise known target platforms only.
_ => {
determine_curve25519_dalek_bits_warning(ERR_MSG_UNKNOWN_POINTER_WIDTH);
DalekBits::Dalek32
}
},
}
}
}

View File

@ -0,0 +1,12 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.css" integrity="sha384-Juol1FqnotbkyZUT5Z7gUPjQ9gzlwCENvUZTpQBAPxtusdwFLRy382PSDx5UUJ4/" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.js" integrity="sha384-97gW6UIJxnlKemYavrqDHSX3SiygeOwIZhwyOKRfSaf0JWKRVj9hLASHgFTzT+0O" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
<style>
.katex { font-size: 1em !important; }
</style>

View File

@ -351,7 +351,7 @@ 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
terms with coefficient \\(1\\) and \\(2\\) separately means that the
longest dependency chain has length 5, so the critical path has length
20 cycles and the bottleneck is throughput.

View File

@ -145,16 +145,16 @@ 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
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' )
(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')\\)
This costs \\( 1\mathbf D\\); with \\( (S\_2\', S\_3\', Z\_2\', T\_2\')\\)
in hand, the addition formulas above become
$$
\begin{aligned}
@ -164,7 +164,7 @@ $$
\\\\
(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\_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
@ -207,7 +207,7 @@ $$
(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 \\).
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.
@ -327,7 +327,7 @@ There are several directions for future improvement:
[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
[curve_models]: https://docs.rs/curve25519-dalek/latest/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

View File

@ -0,0 +1,251 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! **INTERNALS:** Pluggable implementations for different architectures.
//!
//! The backend code is split into two parts: a serial backend,
//! and a vector backend.
//!
//! The [`serial`] backend contains 32- and 64-bit implementations of
//! field arithmetic and scalar arithmetic, as well as implementations
//! of point operations using the mixed-model strategy (passing
//! between different curve models depending on the operation).
//!
//! The [`vector`] backend contains implementations of vectorized
//! field arithmetic, used to implement point operations using a novel
//! implementation strategy derived from parallel formulas of Hisil,
//! Wong, Carter, and Dawson.
//!
//! Because the two strategies give rise to different curve models,
//! it's not possible to reuse exactly the same scalar multiplication
//! code (or to write it generically), so both serial and vector
//! backends contain matching implementations of scalar multiplication
//! algorithms. These are intended to be selected by a `#[cfg]`-based
//! type alias.
//!
//! The [`vector`] backend is selected by the `simd_backend` cargo
//! feature; it uses the [`serial`] backend for non-vectorized operations.
use crate::EdwardsPoint;
use crate::Scalar;
pub mod serial;
#[cfg(curve25519_dalek_backend = "simd")]
pub mod vector;
#[derive(Copy, Clone)]
enum BackendKind {
#[cfg(curve25519_dalek_backend = "simd")]
Avx2,
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
Avx512,
Serial,
}
#[inline]
fn get_selected_backend() -> BackendKind {
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
{
cpufeatures::new!(cpuid_avx512, "avx512ifma", "avx512vl");
let token_avx512: cpuid_avx512::InitToken = cpuid_avx512::init();
if token_avx512.get() {
return BackendKind::Avx512;
}
}
#[cfg(curve25519_dalek_backend = "simd")]
{
cpufeatures::new!(cpuid_avx2, "avx2");
let token_avx2: cpuid_avx2::InitToken = cpuid_avx2::init();
if token_avx2.get() {
return BackendKind::Avx2;
}
}
BackendKind::Serial
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub fn pippenger_optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: core::borrow::Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
use crate::traits::VartimeMultiscalarMul;
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 =>
vector::scalar_mul::pippenger::spec_avx2::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 =>
vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
BackendKind::Serial =>
serial::scalar_mul::pippenger::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
}
}
#[cfg(feature = "alloc")]
pub(crate) enum VartimePrecomputedStraus {
#[cfg(curve25519_dalek_backend = "simd")]
Avx2(vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
Avx512ifma(
vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
),
Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus),
}
#[cfg(feature = "alloc")]
impl VartimePrecomputedStraus {
pub fn new<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: core::borrow::Borrow<EdwardsPoint>,
{
use crate::traits::VartimePrecomputedMultiscalarMul;
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 =>
VartimePrecomputedStraus::Avx2(vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus::new(static_points)),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 =>
VartimePrecomputedStraus::Avx512ifma(vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
BackendKind::Serial =>
VartimePrecomputedStraus::Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(static_points))
}
}
pub fn optional_mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: core::borrow::Borrow<Scalar>,
J: IntoIterator,
J::Item: core::borrow::Borrow<Scalar>,
K: IntoIterator<Item = Option<EdwardsPoint>>,
{
use crate::traits::VartimePrecomputedMultiscalarMul;
match self {
#[cfg(curve25519_dalek_backend = "simd")]
VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
static_scalars,
dynamic_scalars,
dynamic_points,
),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
static_scalars,
dynamic_scalars,
dynamic_points,
),
VartimePrecomputedStraus::Scalar(inner) => inner.optional_mixed_multiscalar_mul(
static_scalars,
dynamic_scalars,
dynamic_points,
),
}
}
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub fn straus_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: core::borrow::Borrow<Scalar>,
J: IntoIterator,
J::Item: core::borrow::Borrow<EdwardsPoint>,
{
use crate::traits::MultiscalarMul;
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 => {
vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(scalars, points)
}
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 => {
vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<I, J>(
scalars, points,
)
}
BackendKind::Serial => {
serial::scalar_mul::straus::Straus::multiscalar_mul::<I, J>(scalars, points)
}
}
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub fn straus_optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: core::borrow::Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
use crate::traits::VartimeMultiscalarMul;
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 => {
vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
scalars, points,
)
}
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 => {
vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
I,
J,
>(scalars, points)
}
BackendKind::Serial => {
serial::scalar_mul::straus::Straus::optional_multiscalar_mul::<I, J>(scalars, points)
}
}
}
/// Perform constant-time, variable-base scalar multiplication.
pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 => vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 => {
vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
}
BackendKind::Serial => serial::scalar_mul::variable_base::mul(point, scalar),
}
}
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
#[allow(non_snake_case)]
pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
match get_selected_backend() {
#[cfg(curve25519_dalek_backend = "simd")]
BackendKind::Avx2 => vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
BackendKind::Avx512 => {
vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
}
BackendKind::Serial => serial::scalar_mul::vartime_double_base::mul(a, A, b),
}
}

View File

@ -129,13 +129,14 @@ use core::ops::{Add, Neg, Sub};
use subtle::Choice;
use subtle::ConditionallySelectable;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use constants;
use crate::constants;
use edwards::EdwardsPoint;
use field::FieldElement;
use traits::ValidityCheck;
use crate::edwards::EdwardsPoint;
use crate::field::FieldElement;
use crate::traits::ValidityCheck;
// ------------------------------------------------------------------------
// Internal point representations
@ -148,6 +149,7 @@ use traits::ValidityCheck;
///
/// More details on the relationships between the different curve models
/// can be found in the module-level documentation.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub struct ProjectivePoint {
pub X: FieldElement,
@ -180,11 +182,12 @@ pub struct CompletedPoint {
#[derive(Copy, Clone, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct AffineNielsPoint {
pub y_plus_x: FieldElement,
pub y_plus_x: FieldElement,
pub y_minus_x: FieldElement,
pub xy2d: FieldElement,
pub xy2d: FieldElement,
}
#[cfg(feature = "zeroize")]
impl Zeroize for AffineNielsPoint {
fn zeroize(&mut self) {
self.y_plus_x.zeroize();
@ -199,13 +202,15 @@ impl Zeroize for AffineNielsPoint {
/// More details on the relationships between the different curve models
/// can be found in the module-level documentation.
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub struct ProjectiveNielsPoint {
pub Y_plus_X: FieldElement,
pub Y_plus_X: FieldElement,
pub Y_minus_X: FieldElement,
pub Z: FieldElement,
pub T2d: FieldElement,
pub Z: FieldElement,
pub T2d: FieldElement,
}
#[cfg(feature = "zeroize")]
impl Zeroize for ProjectiveNielsPoint {
fn zeroize(&mut self) {
self.Y_plus_X.zeroize();
@ -219,25 +224,25 @@ impl Zeroize for ProjectiveNielsPoint {
// Constructors
// ------------------------------------------------------------------------
use traits::Identity;
use crate::traits::Identity;
impl Identity for ProjectivePoint {
fn identity() -> ProjectivePoint {
ProjectivePoint {
X: FieldElement::zero(),
Y: FieldElement::one(),
Z: FieldElement::one(),
X: FieldElement::ZERO,
Y: FieldElement::ONE,
Z: FieldElement::ONE,
}
}
}
impl Identity for ProjectiveNielsPoint {
fn identity() -> ProjectiveNielsPoint {
ProjectiveNielsPoint{
Y_plus_X: FieldElement::one(),
Y_minus_X: FieldElement::one(),
Z: FieldElement::one(),
T2d: FieldElement::zero(),
ProjectiveNielsPoint {
Y_plus_X: FieldElement::ONE,
Y_minus_X: FieldElement::ONE,
Z: FieldElement::ONE,
T2d: FieldElement::ZERO,
}
}
}
@ -250,10 +255,10 @@ impl Default for ProjectiveNielsPoint {
impl Identity for AffineNielsPoint {
fn identity() -> AffineNielsPoint {
AffineNielsPoint{
y_plus_x: FieldElement::one(),
y_minus_x: FieldElement::one(),
xy2d: FieldElement::zero(),
AffineNielsPoint {
y_plus_x: FieldElement::ONE,
y_minus_x: FieldElement::ONE,
xy2d: FieldElement::ZERO,
}
}
}
@ -330,7 +335,7 @@ impl ProjectivePoint {
/// \\( \mathbb P\^3 \\) model.
///
/// This costs \\(3 \mathrm M + 1 \mathrm S\\).
pub fn to_extended(&self) -> EdwardsPoint {
pub fn as_extended(&self) -> EdwardsPoint {
EdwardsPoint {
X: &self.X * &self.Z,
Y: &self.Y * &self.Z,
@ -345,7 +350,7 @@ impl CompletedPoint {
/// \\) model to the \\( \mathbb P\^2 \\) model.
///
/// This costs \\(3 \mathrm M \\).
pub fn to_projective(&self) -> ProjectivePoint {
pub fn as_projective(&self) -> ProjectivePoint {
ProjectivePoint {
X: &self.X * &self.T,
Y: &self.Y * &self.Z,
@ -357,7 +362,7 @@ impl CompletedPoint {
/// \\) model to the \\( \mathbb P\^3 \\) model.
///
/// This costs \\(4 \mathrm M \\).
pub fn to_extended(&self) -> EdwardsPoint {
pub fn as_extended(&self) -> EdwardsPoint {
EdwardsPoint {
X: &self.X * &self.T,
Y: &self.Y * &self.Z,
@ -373,20 +378,21 @@ impl CompletedPoint {
impl ProjectivePoint {
/// Double this point: return self + self
pub fn double(&self) -> CompletedPoint { // Double()
let XX = self.X.square();
let YY = self.Y.square();
let ZZ2 = self.Z.square2();
let X_plus_Y = &self.X + &self.Y;
pub fn double(&self) -> CompletedPoint {
// Double()
let XX = self.X.square();
let YY = self.Y.square();
let ZZ2 = self.Z.square2();
let X_plus_Y = &self.X + &self.Y;
let X_plus_Y_sq = X_plus_Y.square();
let YY_plus_XX = &YY + &XX;
let YY_plus_XX = &YY + &XX;
let YY_minus_XX = &YY - &XX;
CompletedPoint{
CompletedPoint {
X: &X_plus_Y_sq - &YY_plus_XX,
Y: YY_plus_XX,
Z: YY_minus_XX,
T: &ZZ2 - &YY_minus_XX
T: &ZZ2 - &YY_minus_XX,
}
}
}
@ -406,19 +412,19 @@ impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
type Output = CompletedPoint;
fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
let Y_plus_X = &self.Y + &self.X;
let Y_plus_X = &self.Y + &self.X;
let Y_minus_X = &self.Y - &self.X;
let PP = &Y_plus_X * &other.Y_plus_X;
let PP = &Y_plus_X * &other.Y_plus_X;
let MM = &Y_minus_X * &other.Y_minus_X;
let TT2d = &self.T * &other.T2d;
let ZZ = &self.Z * &other.Z;
let ZZ2 = &ZZ + &ZZ;
let ZZ = &self.Z * &other.Z;
let ZZ2 = &ZZ + &ZZ;
CompletedPoint{
CompletedPoint {
X: &PP - &MM,
Y: &PP + &MM,
Z: &ZZ2 + &TT2d,
T: &ZZ2 - &TT2d
T: &ZZ2 - &TT2d,
}
}
}
@ -428,19 +434,19 @@ impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
type Output = CompletedPoint;
fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
let Y_plus_X = &self.Y + &self.X;
let Y_plus_X = &self.Y + &self.X;
let Y_minus_X = &self.Y - &self.X;
let PM = &Y_plus_X * &other.Y_minus_X;
let MP = &Y_minus_X * &other.Y_plus_X;
let MP = &Y_minus_X * &other.Y_plus_X;
let TT2d = &self.T * &other.T2d;
let ZZ = &self.Z * &other.Z;
let ZZ2 = &ZZ + &ZZ;
let ZZ = &self.Z * &other.Z;
let ZZ2 = &ZZ + &ZZ;
CompletedPoint{
CompletedPoint {
X: &PM - &MP,
Y: &PM + &MP,
Z: &ZZ2 - &TT2d,
T: &ZZ2 + &TT2d
T: &ZZ2 + &TT2d,
}
}
}
@ -450,18 +456,18 @@ impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a EdwardsPoint {
type Output = CompletedPoint;
fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint {
let Y_plus_X = &self.Y + &self.X;
let Y_plus_X = &self.Y + &self.X;
let Y_minus_X = &self.Y - &self.X;
let PP = &Y_plus_X * &other.y_plus_x;
let MM = &Y_minus_X * &other.y_minus_x;
let Txy2d = &self.T * &other.xy2d;
let Z2 = &self.Z + &self.Z;
let PP = &Y_plus_X * &other.y_plus_x;
let MM = &Y_minus_X * &other.y_minus_x;
let Txy2d = &self.T * &other.xy2d;
let Z2 = &self.Z + &self.Z;
CompletedPoint{
CompletedPoint {
X: &PP - &MM,
Y: &PP + &MM,
Z: &Z2 + &Txy2d,
T: &Z2 - &Txy2d
T: &Z2 - &Txy2d,
}
}
}
@ -471,18 +477,18 @@ impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a EdwardsPoint {
type Output = CompletedPoint;
fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint {
let Y_plus_X = &self.Y + &self.X;
let Y_plus_X = &self.Y + &self.X;
let Y_minus_X = &self.Y - &self.X;
let PM = &Y_plus_X * &other.y_minus_x;
let MP = &Y_minus_X * &other.y_plus_x;
let Txy2d = &self.T * &other.xy2d;
let Z2 = &self.Z + &self.Z;
let PM = &Y_plus_X * &other.y_minus_x;
let MP = &Y_minus_X * &other.y_plus_x;
let Txy2d = &self.T * &other.xy2d;
let Z2 = &self.Z + &self.Z;
CompletedPoint{
CompletedPoint {
X: &PM - &MP,
Y: &PM + &MP,
Z: &Z2 - &Txy2d,
T: &Z2 + &Txy2d
T: &Z2 + &Txy2d,
}
}
}
@ -495,11 +501,11 @@ impl<'a> Neg for &'a ProjectiveNielsPoint {
type Output = ProjectiveNielsPoint;
fn neg(self) -> ProjectiveNielsPoint {
ProjectiveNielsPoint{
Y_plus_X: self.Y_minus_X,
Y_minus_X: self.Y_plus_X,
Z: self.Z,
T2d: -(&self.T2d),
ProjectiveNielsPoint {
Y_plus_X: self.Y_minus_X,
Y_minus_X: self.Y_plus_X,
Z: self.Z,
T2d: -(&self.T2d),
}
}
}
@ -508,10 +514,10 @@ impl<'a> Neg for &'a AffineNielsPoint {
type Output = AffineNielsPoint;
fn neg(self) -> AffineNielsPoint {
AffineNielsPoint{
y_plus_x: self.y_minus_x,
y_minus_x: self.y_plus_x,
xy2d: -(&self.xy2d)
AffineNielsPoint {
y_plus_x: self.y_minus_x,
y_minus_x: self.y_plus_x,
xy2d: -(&self.xy2d),
}
}
}
@ -521,31 +527,38 @@ impl<'a> Neg for &'a AffineNielsPoint {
// ------------------------------------------------------------------------
impl Debug for ProjectivePoint {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
&self.X, &self.Y, &self.Z)
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
&self.X, &self.Y, &self.Z
)
}
}
impl Debug for CompletedPoint {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
&self.X, &self.Y, &self.Z, &self.T)
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
&self.X, &self.Y, &self.Z, &self.T
)
}
}
impl Debug for AffineNielsPoint {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
&self.y_plus_x, &self.y_minus_x, &self.xy2d)
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
&self.y_plus_x, &self.y_minus_x, &self.xy2d
)
}
}
impl Debug for ProjectiveNielsPoint {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}",
&self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
}
}

View File

@ -27,6 +27,7 @@ use core::ops::{Sub, SubAssign};
use subtle::Choice;
use subtle::ConditionallySelectable;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use fiat_crypto::curve25519_32::*;
@ -54,72 +55,78 @@ use fiat_crypto::curve25519_32::*;
/// The backend-specific type `FieldElement2625` should not be used
/// outside of the `curve25519_dalek::field` module.
#[derive(Copy, Clone)]
pub struct FieldElement2625(pub(crate) [u32; 10]);
pub struct FieldElement2625(pub(crate) fiat_25519_tight_field_element);
impl Debug for FieldElement2625 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "FieldElement2625({:?})", &self.0[..])
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FieldElement2625({:?})", &(self.0).0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for FieldElement2625 {
fn zeroize(&mut self) {
self.0.zeroize();
(self.0).0.zeroize();
}
}
impl<'b> AddAssign<&'b FieldElement2625> for FieldElement2625 {
fn add_assign(&mut self, _rhs: &'b FieldElement2625) {
let input = self.0;
fiat_25519_add(&mut self.0, &input, &_rhs.0);
let input = self.0;
fiat_25519_carry(&mut self.0, &input);
fn add_assign(&mut self, rhs: &'b FieldElement2625) {
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
fiat_25519_carry(&mut self.0, &result_loose);
}
}
impl<'a, 'b> Add<&'b FieldElement2625> for &'a FieldElement2625 {
type Output = FieldElement2625;
fn add(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut output = *self;
fiat_25519_add(&mut output.0, &self.0, &_rhs.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
fn add(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry(&mut output.0, &result_loose);
output
}
}
impl<'b> SubAssign<&'b FieldElement2625> for FieldElement2625 {
fn sub_assign(&mut self, _rhs: &'b FieldElement2625) {
let input = self.0;
fiat_25519_sub(&mut self.0, &input, &_rhs.0);
let input = self.0;
fiat_25519_carry(&mut self.0, &input);
fn sub_assign(&mut self, rhs: &'b FieldElement2625) {
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
fiat_25519_carry(&mut self.0, &result_loose);
}
}
impl<'a, 'b> Sub<&'b FieldElement2625> for &'a FieldElement2625 {
type Output = FieldElement2625;
fn sub(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut output = *self;
fiat_25519_sub(&mut output.0, &self.0, &_rhs.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
fn sub(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry(&mut output.0, &result_loose);
output
}
}
impl<'b> MulAssign<&'b FieldElement2625> for FieldElement2625 {
fn mul_assign(&mut self, _rhs: &'b FieldElement2625) {
let input = self.0;
fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0);
fn mul_assign(&mut self, rhs: &'b FieldElement2625) {
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut rhs_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut rhs_loose, &rhs.0);
fiat_25519_carry_mul(&mut self.0, &self_loose, &rhs_loose);
}
}
impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
type Output = FieldElement2625;
fn mul(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut output = *self;
fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0);
fn mul(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut rhs_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut rhs_loose, &rhs.0);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry_mul(&mut output.0, &self_loose, &rhs_loose);
output
}
}
@ -127,10 +134,10 @@ impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
impl<'a> Neg for &'a FieldElement2625 {
type Output = FieldElement2625;
fn neg(self) -> FieldElement2625 {
let mut output = *self;
fiat_25519_opp(&mut output.0, &self.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
let mut output_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_opp(&mut output_loose, &self.0);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry(&mut output.0, &output_loose);
output
}
}
@ -141,8 +148,13 @@ impl ConditionallySelectable for FieldElement2625 {
b: &FieldElement2625,
choice: Choice,
) -> FieldElement2625 {
let mut output = [0u32; 10];
fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0);
let mut output = fiat_25519_tight_field_element([0u32; 10]);
fiat_25519_selectznz(
&mut output.0,
choice.unwrap_u8() as fiat_25519_u1,
&(a.0).0,
&(b.0).0,
);
FieldElement2625(output)
}
@ -159,7 +171,7 @@ impl ConditionallySelectable for FieldElement2625 {
fiat_25519_cmovznz_u32(&mut output[7], choicebit, self.0[7], other.0[7]);
fiat_25519_cmovznz_u32(&mut output[8], choicebit, self.0[8], other.0[8]);
fiat_25519_cmovznz_u32(&mut output[9], choicebit, self.0[9], other.0[9]);
*self = FieldElement2625(output);
*self = FieldElement2625::from_limbs(output);
}
fn conditional_swap(a: &mut FieldElement2625, b: &mut FieldElement2625, choice: Choice) {
@ -177,30 +189,26 @@ impl ConditionallySelectable for FieldElement2625 {
}
impl FieldElement2625 {
pub(crate) const fn from_limbs(limbs: [u32; 10]) -> FieldElement2625 {
FieldElement2625(fiat_25519_tight_field_element(limbs))
}
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
pub const ONE: FieldElement2625 = FieldElement2625::from_limbs([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( -1 \\).
pub const MINUS_ONE: FieldElement2625 = FieldElement2625::from_limbs([
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
0x3ffffff, 0x1ffffff,
]);
/// Invert the sign of this field element
pub fn negate(&mut self) {
let neg = self.neg();
self.0 = neg.0;
}
/// Construct zero.
pub fn zero() -> FieldElement2625 {
FieldElement2625([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
}
/// Construct one.
pub fn one() -> FieldElement2625 {
FieldElement2625([1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
}
/// Construct -1.
pub fn minus_one() -> FieldElement2625 {
FieldElement2625([
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
0x3ffffff, 0x1ffffff,
])
}
/// Given `k > 0`, return `self^(2^k)`.
pub fn pow2k(&self, k: u32) -> FieldElement2625 {
debug_assert!(k > 0);
@ -226,35 +234,38 @@ impl FieldElement2625 {
let mut temp = [0u8; 32];
temp.copy_from_slice(data);
temp[31] &= 127u8;
let mut output = [0u32; 10];
let mut output = fiat_25519_tight_field_element([0u32; 10]);
fiat_25519_from_bytes(&mut output, &temp);
FieldElement2625(output)
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn to_bytes(&self) -> [u8; 32] {
pub fn as_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
fiat_25519_to_bytes(&mut bytes, &self.0);
return bytes;
bytes
}
/// Compute `self^2`.
pub fn square(&self) -> FieldElement2625 {
let mut output = *self;
fiat_25519_carry_square(&mut output.0, &self.0);
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry_square(&mut output.0, &self_loose);
output
}
/// Compute `2*self^2`.
pub fn square2(&self) -> FieldElement2625 {
let mut output = *self;
let mut temp = *self;
// Void vs return type, measure cost of copying self
fiat_25519_carry_square(&mut temp.0, &self.0);
fiat_25519_add(&mut output.0, &temp.0, &temp.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut square = fiat_25519_tight_field_element([0; 10]);
fiat_25519_carry_square(&mut square, &self_loose);
let mut output_loose = fiat_25519_loose_field_element([0; 10]);
fiat_25519_add(&mut output_loose, &square, &square);
let mut output = FieldElement2625::ZERO;
fiat_25519_carry(&mut output.0, &output_loose);
output
}
}

View File

@ -23,6 +23,7 @@ use core::ops::{Sub, SubAssign};
use subtle::Choice;
use subtle::ConditionallySelectable;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use fiat_crypto::curve25519_64::*;
@ -43,72 +44,78 @@ use fiat_crypto::curve25519_64::*;
/// The backend-specific type `FieldElement51` should not be used
/// outside of the `curve25519_dalek::field` module.
#[derive(Copy, Clone)]
pub struct FieldElement51(pub(crate) [u64; 5]);
pub struct FieldElement51(pub(crate) fiat_25519_tight_field_element);
impl Debug for FieldElement51 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "FieldElement51({:?})", &self.0[..])
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FieldElement51({:?})", &(self.0).0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for FieldElement51 {
fn zeroize(&mut self) {
self.0.zeroize();
(self.0).0.zeroize();
}
}
impl<'b> AddAssign<&'b FieldElement51> for FieldElement51 {
fn add_assign(&mut self, _rhs: &'b FieldElement51) {
let input = self.0;
fiat_25519_add(&mut self.0, &input, &_rhs.0);
let input = self.0;
fiat_25519_carry(&mut self.0, &input);
fn add_assign(&mut self, rhs: &'b FieldElement51) {
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
fiat_25519_carry(&mut self.0, &result_loose);
}
}
impl<'a, 'b> Add<&'b FieldElement51> for &'a FieldElement51 {
type Output = FieldElement51;
fn add(self, _rhs: &'b FieldElement51) -> FieldElement51 {
let mut output = *self;
fiat_25519_add(&mut output.0, &self.0, &_rhs.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
fn add(self, rhs: &'b FieldElement51) -> FieldElement51 {
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
let mut output = FieldElement51::ZERO;
fiat_25519_carry(&mut output.0, &result_loose);
output
}
}
impl<'b> SubAssign<&'b FieldElement51> for FieldElement51 {
fn sub_assign(&mut self, _rhs: &'b FieldElement51) {
let input = self.0;
fiat_25519_sub(&mut self.0, &input, &_rhs.0);
let input = self.0;
fiat_25519_carry(&mut self.0, &input);
fn sub_assign(&mut self, rhs: &'b FieldElement51) {
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
fiat_25519_carry(&mut self.0, &result_loose);
}
}
impl<'a, 'b> Sub<&'b FieldElement51> for &'a FieldElement51 {
type Output = FieldElement51;
fn sub(self, _rhs: &'b FieldElement51) -> FieldElement51 {
let mut output = *self;
fiat_25519_sub(&mut output.0, &self.0, &_rhs.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
fn sub(self, rhs: &'b FieldElement51) -> FieldElement51 {
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
let mut output = FieldElement51::ZERO;
fiat_25519_carry(&mut output.0, &result_loose);
output
}
}
impl<'b> MulAssign<&'b FieldElement51> for FieldElement51 {
fn mul_assign(&mut self, _rhs: &'b FieldElement51) {
let input = self.0;
fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0);
fn mul_assign(&mut self, rhs: &'b FieldElement51) {
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut rhs_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut rhs_loose, &rhs.0);
fiat_25519_carry_mul(&mut self.0, &self_loose, &rhs_loose);
}
}
impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
type Output = FieldElement51;
fn mul(self, _rhs: &'b FieldElement51) -> FieldElement51 {
let mut output = *self;
fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0);
fn mul(self, rhs: &'b FieldElement51) -> FieldElement51 {
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut rhs_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut rhs_loose, &rhs.0);
let mut output = FieldElement51::ZERO;
fiat_25519_carry_mul(&mut output.0, &self_loose, &rhs_loose);
output
}
}
@ -116,10 +123,10 @@ impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
impl<'a> Neg for &'a FieldElement51 {
type Output = FieldElement51;
fn neg(self) -> FieldElement51 {
let mut output = *self;
fiat_25519_opp(&mut output.0, &self.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
let mut output_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_opp(&mut output_loose, &self.0);
let mut output = FieldElement51::ZERO;
fiat_25519_carry(&mut output.0, &output_loose);
output
}
}
@ -130,8 +137,13 @@ impl ConditionallySelectable for FieldElement51 {
b: &FieldElement51,
choice: Choice,
) -> FieldElement51 {
let mut output = [0u64; 5];
fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0);
let mut output = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_selectznz(
&mut output.0,
choice.unwrap_u8() as fiat_25519_u1,
&(a.0).0,
&(b.0).0,
);
FieldElement51(output)
}
@ -143,47 +155,44 @@ impl ConditionallySelectable for FieldElement51 {
u64::conditional_swap(&mut a.0[4], &mut b.0[4], choice);
}
fn conditional_assign(&mut self, _rhs: &FieldElement51, choice: Choice) {
fn conditional_assign(&mut self, rhs: &FieldElement51, choice: Choice) {
let mut output = [0u64; 5];
let choicebit = choice.unwrap_u8() as fiat_25519_u1;
fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], _rhs.0[0]);
fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], _rhs.0[1]);
fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], _rhs.0[2]);
fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], _rhs.0[3]);
fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], _rhs.0[4]);
*self = FieldElement51(output);
fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], rhs.0[0]);
fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], rhs.0[1]);
fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], rhs.0[2]);
fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], rhs.0[3]);
fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], rhs.0[4]);
*self = FieldElement51::from_limbs(output);
}
}
impl FieldElement51 {
/// Construct zero.
pub fn zero() -> FieldElement51 {
FieldElement51([0, 0, 0, 0, 0])
pub(crate) const fn from_limbs(limbs: [u64; 5]) -> FieldElement51 {
FieldElement51(fiat_25519_tight_field_element(limbs))
}
/// Construct one.
pub fn one() -> FieldElement51 {
FieldElement51([1, 0, 0, 0, 0])
}
/// Construct -1.
pub fn minus_one() -> FieldElement51 {
FieldElement51([
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
])
}
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
pub const ONE: FieldElement51 = FieldElement51::from_limbs([1, 0, 0, 0, 0]);
/// The scalar \\( -1 \\).
pub const MINUS_ONE: FieldElement51 = FieldElement51::from_limbs([
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
]);
/// Given 64-bit input limbs, reduce to enforce the bound 2^(51 + epsilon).
#[inline(always)]
#[allow(dead_code)] // Need this to not complain about reduce not being used
fn reduce(mut limbs: [u64; 5]) -> FieldElement51 {
let input = limbs;
fiat_25519_carry(&mut limbs, &input);
FieldElement51(limbs)
fn reduce(limbs: [u64; 5]) -> FieldElement51 {
let input = fiat_25519_loose_field_element(limbs);
let mut output = fiat_25519_tight_field_element([0; 5]);
fiat_25519_carry(&mut output, &input);
FieldElement51(output)
}
/// Load a `FieldElement51` from the low 255 bits of a 256-bit
@ -202,24 +211,25 @@ impl FieldElement51 {
let mut temp = [0u8; 32];
temp.copy_from_slice(bytes);
temp[31] &= 127u8;
let mut output = [0u64; 5];
let mut output = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_from_bytes(&mut output, &temp);
FieldElement51(output)
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn to_bytes(&self) -> [u8; 32] {
pub fn as_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
fiat_25519_to_bytes(&mut bytes, &self.0);
return bytes;
bytes
}
/// Given `k > 0`, return `self^(2^k)`.
pub fn pow2k(&self, mut k: u32) -> FieldElement51 {
let mut output = *self;
loop {
let input = output.0;
let mut input = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut input, &output.0);
fiat_25519_carry_square(&mut output.0, &input);
k -= 1;
if k == 0 {
@ -230,20 +240,23 @@ impl FieldElement51 {
/// Returns the square of this field element.
pub fn square(&self) -> FieldElement51 {
let mut output = *self;
fiat_25519_carry_square(&mut output.0, &self.0);
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut output = FieldElement51::ZERO;
fiat_25519_carry_square(&mut output.0, &self_loose);
output
}
/// Returns 2 times the square of this field element.
pub fn square2(&self) -> FieldElement51 {
let mut output = *self;
let mut temp = *self;
// Void vs return type, measure cost of copying self
fiat_25519_carry_square(&mut temp.0, &self.0);
fiat_25519_add(&mut output.0, &temp.0, &temp.0);
let input = output.0;
fiat_25519_carry(&mut output.0, &input);
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_relax(&mut self_loose, &self.0);
let mut square = fiat_25519_tight_field_element([0; 5]);
fiat_25519_carry_square(&mut square, &self_loose);
let mut output_loose = fiat_25519_loose_field_element([0; 5]);
fiat_25519_add(&mut output_loose, &square, &square);
let mut output = FieldElement51::ZERO;
fiat_25519_carry(&mut output.0, &output_loose);
output
}
}

View File

@ -0,0 +1,49 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Serial implementations of field, scalar, point arithmetic.
//!
//! When the vector backend is disabled, the crate uses the mixed-model strategy
//! for implementing point operations and scalar multiplication; see the
//! [`curve_models`] and [`scalar_mul`] documentation for more information.
//!
//! When the vector backend is enabled, the field and scalar
//! implementations are still used for non-vectorized operations.
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(curve25519_dalek_backend = "fiat")] {
#[cfg(curve25519_dalek_bits = "32")]
#[doc(hidden)]
pub mod fiat_u32;
#[cfg(curve25519_dalek_bits = "64")]
#[doc(hidden)]
pub mod fiat_u64;
} else {
#[cfg(curve25519_dalek_bits = "32")]
#[doc(hidden)]
pub mod u32;
#[cfg(curve25519_dalek_bits = "64")]
#[doc(hidden)]
pub mod u64;
}
}
pub mod curve_models;
pub mod scalar_mul;

View File

@ -17,8 +17,10 @@
//! scalar multiplication implementations, since it only uses one
//! curve model.
#[allow(missing_docs)]
pub mod variable_base;
#[allow(missing_docs)]
pub mod vartime_double_base;
#[cfg(feature = "alloc")]

View File

@ -11,14 +11,14 @@
#![allow(non_snake_case)]
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
use edwards::EdwardsPoint;
use scalar::Scalar;
use traits::VartimeMultiscalarMul;
#[allow(unused_imports)]
use prelude::*;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::VartimeMultiscalarMul;
/// Implements a version of Pippenger's algorithm.
///
@ -61,7 +61,6 @@ use prelude::*;
/// This algorithm is adapted from section 4 of <https://eprint.iacr.org/2012/549.pdf>.
pub struct Pippenger;
#[cfg(any(feature = "alloc", feature = "std"))]
impl VartimeMultiscalarMul for Pippenger {
type Point = EdwardsPoint;
@ -71,7 +70,7 @@ impl VartimeMultiscalarMul for Pippenger {
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
use traits::Identity;
use crate::traits::Identity;
let mut scalars = scalars.into_iter();
let size = scalars.by_ref().size_hint().0;
@ -93,12 +92,11 @@ impl VartimeMultiscalarMul for Pippenger {
// Collect optimized scalars and points in buffers for repeated access
// (scanning the whole set per digit position).
let scalars = scalars
.map(|s| s.borrow().to_radix_2w(w));
let scalars = scalars.map(|s| s.borrow().as_radix_2w(w));
let points = points
.into_iter()
.map(|p| p.map(|P| P.to_projective_niels()));
.map(|p| p.map(|P| P.as_projective_niels()));
let scalars_points = scalars
.zip(points)
@ -113,8 +111,8 @@ impl VartimeMultiscalarMul for Pippenger {
let mut columns = (0..digits_count).rev().map(|digit_index| {
// Clear the buckets when processing another digit.
for i in 0..buckets_count {
buckets[i] = EdwardsPoint::identity();
for bucket in &mut buckets {
*bucket = EdwardsPoint::identity();
}
// Iterate over pairs of (point, scalar)
@ -124,12 +122,16 @@ impl VartimeMultiscalarMul for Pippenger {
for (digits, pt) in scalars_points.iter() {
// Widen digit so that we don't run into edge cases when w=8.
let digit = digits[digit_index] as i16;
if digit > 0 {
let b = (digit - 1) as usize;
buckets[b] = (&buckets[b] + pt).to_extended();
} else if digit < 0 {
let b = (-digit - 1) as usize;
buckets[b] = (&buckets[b] - pt).to_extended();
match digit.cmp(&0) {
Ordering::Greater => {
let b = (digit - 1) as usize;
buckets[b] = (&buckets[b] + pt).as_extended();
}
Ordering::Less => {
let b = (-digit - 1) as usize;
buckets[b] = (&buckets[b] - pt).as_extended();
}
Ordering::Equal => {}
}
}
@ -152,21 +154,16 @@ impl VartimeMultiscalarMul for Pippenger {
});
// Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`.
// `unwrap()` always succeeds because we know we have more than zero digits.
let hi_column = columns.next().unwrap();
let hi_column = columns.next().expect("should have more than zero digits");
Some(
columns
.fold(hi_column, |total, p| total.mul_by_pow_2(w as u32) + p),
)
Some(columns.fold(hi_column, |total, p| total.mul_by_pow_2(w as u32) + p))
}
}
#[cfg(test)]
mod test {
use super::*;
use constants;
use scalar::Scalar;
use crate::constants;
#[test]
fn test_vartime_pippenger() {
@ -196,7 +193,7 @@ mod test {
assert_eq!(subject.compress(), control.compress());
n = n / 2;
n /= 2;
}
}
}

View File

@ -11,20 +11,21 @@
#![allow(non_snake_case)]
use core::borrow::Borrow;
use alloc::vec::Vec;
use backend::serial::curve_models::{
use core::borrow::Borrow;
use core::cmp::Ordering;
use crate::backend::serial::curve_models::{
AffineNielsPoint, CompletedPoint, ProjectiveNielsPoint, ProjectivePoint,
};
use edwards::EdwardsPoint;
use scalar::Scalar;
use traits::Identity;
use traits::VartimePrecomputedMultiscalarMul;
use window::{NafLookupTable5, NafLookupTable8};
#[allow(unused_imports)]
use prelude::*;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::traits::VartimePrecomputedMultiscalarMul;
use crate::window::{NafLookupTable5, NafLookupTable8};
#[allow(missing_docs)]
pub struct VartimePrecomputedStraus {
static_lookup_tables: Vec<NafLookupTable8<AffineNielsPoint>>,
}
@ -86,25 +87,34 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
for i in 0..dp {
let t_ij = dynamic_nafs[i][j];
if t_ij > 0 {
R = &R.to_extended() + &dynamic_lookup_tables[i].select(t_ij as usize);
} else if t_ij < 0 {
R = &R.to_extended() - &dynamic_lookup_tables[i].select(-t_ij as usize);
match t_ij.cmp(&0) {
Ordering::Greater => {
R = &R.as_extended() + &dynamic_lookup_tables[i].select(t_ij as usize)
}
Ordering::Less => {
R = &R.as_extended() - &dynamic_lookup_tables[i].select(-t_ij as usize)
}
Ordering::Equal => {}
}
}
#[allow(clippy::needless_range_loop)]
for i in 0..sp {
let t_ij = static_nafs[i][j];
if t_ij > 0 {
R = &R.to_extended() + &self.static_lookup_tables[i].select(t_ij as usize);
} else if t_ij < 0 {
R = &R.to_extended() - &self.static_lookup_tables[i].select(-t_ij as usize);
match t_ij.cmp(&0) {
Ordering::Greater => {
R = &R.as_extended() + &self.static_lookup_tables[i].select(t_ij as usize)
}
Ordering::Less => {
R = &R.as_extended() - &self.static_lookup_tables[i].select(-t_ij as usize)
}
Ordering::Equal => {}
}
}
S = R.to_projective();
S = R.as_projective();
}
Some(S.to_extended())
Some(S.as_extended())
}
}

View File

@ -13,15 +13,15 @@
#![allow(non_snake_case)]
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
use edwards::EdwardsPoint;
use scalar::Scalar;
use traits::MultiscalarMul;
use traits::VartimeMultiscalarMul;
#[allow(unused_imports)]
use prelude::*;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::MultiscalarMul;
use crate::traits::VartimeMultiscalarMul;
/// Perform multiscalar multiplication by the interleaved window
/// method, also known as Straus' method (since it was apparently
@ -107,11 +107,9 @@ impl MultiscalarMul for Straus {
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
use zeroize::Zeroizing;
use backend::serial::curve_models::ProjectiveNielsPoint;
use window::LookupTable;
use traits::Identity;
use crate::backend::serial::curve_models::ProjectiveNielsPoint;
use crate::traits::Identity;
use crate::window::LookupTable;
let lookup_tables: Vec<_> = points
.into_iter()
@ -121,11 +119,11 @@ impl MultiscalarMul for Straus {
// This puts the scalar digits into a heap-allocated Vec.
// To ensure that these are erased, pass ownership of the Vec into a
// Zeroizing wrapper.
let scalar_digits_vec: Vec<_> = scalars
#[cfg_attr(not(feature = "zeroize"), allow(unused_mut))]
let mut scalar_digits: Vec<_> = scalars
.into_iter()
.map(|s| s.borrow().to_radix_16())
.map(|s| s.borrow().as_radix_16())
.collect();
let scalar_digits = Zeroizing::new(scalar_digits_vec);
let mut Q = EdwardsPoint::identity();
for j in (0..64).rev() {
@ -135,10 +133,13 @@ impl MultiscalarMul for Straus {
// R_i = s_{i,j} * P_i
let R_i = lookup_table_i.select(s_i[j]);
// Q = Q + R_i
Q = (&Q + &R_i).to_extended();
Q = (&Q + &R_i).as_extended();
}
}
#[cfg(feature = "zeroize")]
zeroize::Zeroize::zeroize(&mut scalar_digits);
Q
}
}
@ -161,9 +162,11 @@ impl VartimeMultiscalarMul for Straus {
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
use backend::serial::curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
use window::NafLookupTable5;
use traits::Identity;
use crate::backend::serial::curve_models::{
CompletedPoint, ProjectiveNielsPoint, ProjectivePoint,
};
use crate::traits::Identity;
use crate::window::NafLookupTable5;
let nafs: Vec<_> = scalars
.into_iter()
@ -181,16 +184,18 @@ impl VartimeMultiscalarMul for Straus {
let mut t: CompletedPoint = r.double();
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
if naf[i] > 0 {
t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
} else if naf[i] < 0 {
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
match naf[i].cmp(&0) {
Ordering::Greater => {
t = &t.as_extended() + &lookup_table.select(naf[i] as usize)
}
Ordering::Less => t = &t.as_extended() - &lookup_table.select(-naf[i] as usize),
Ordering::Equal => {}
}
}
r = t.to_projective();
r = t.as_projective();
}
Some(r.to_extended())
Some(r.as_extended())
}
}

View File

@ -1,12 +1,13 @@
#![allow(non_snake_case)]
use traits::Identity;
use scalar::Scalar;
use edwards::EdwardsPoint;
use backend::serial::curve_models::ProjectiveNielsPoint;
use window::LookupTable;
use crate::backend::serial::curve_models::ProjectiveNielsPoint;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::window::LookupTable;
/// Perform constant-time, variable-base scalar multiplication.
#[rustfmt::skip] // keep alignment of explanatory comments
pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
let lookup_table = LookupTable::<ProjectiveNielsPoint>::from(point);
@ -15,7 +16,8 @@ pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
// s = s_0 + s_1*16^1 + ... + s_63*16^63,
//
// with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`.
let scalar_digits = scalar.to_radix_16();
// This decomposition requires s < 2^255, which is guaranteed by Scalar invariant #1.
let scalar_digits = scalar.as_radix_16();
// Compute s*P as
//
// s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63)
@ -30,17 +32,17 @@ pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
let mut tmp1 = &tmp3 + &lookup_table.select(scalar_digits[63]);
// Now tmp1 = s_63*P in P1xP1 coords
for i in (0..63).rev() {
tmp2 = tmp1.to_projective(); // tmp2 = (prev) in P2 coords
tmp2 = tmp1.as_projective(); // tmp2 = (prev) in P2 coords
tmp1 = tmp2.double(); // tmp1 = 2*(prev) in P1xP1 coords
tmp2 = tmp1.to_projective(); // tmp2 = 2*(prev) in P2 coords
tmp2 = tmp1.as_projective(); // tmp2 = 2*(prev) in P2 coords
tmp1 = tmp2.double(); // tmp1 = 4*(prev) in P1xP1 coords
tmp2 = tmp1.to_projective(); // tmp2 = 4*(prev) in P2 coords
tmp2 = tmp1.as_projective(); // tmp2 = 4*(prev) in P2 coords
tmp1 = tmp2.double(); // tmp1 = 8*(prev) in P1xP1 coords
tmp2 = tmp1.to_projective(); // tmp2 = 8*(prev) in P2 coords
tmp2 = tmp1.as_projective(); // tmp2 = 8*(prev) in P2 coords
tmp1 = tmp2.double(); // tmp1 = 16*(prev) in P1xP1 coords
tmp3 = tmp1.to_extended(); // tmp3 = 16*(prev) in P3 coords
tmp3 = tmp1.as_extended(); // tmp3 = 16*(prev) in P3 coords
tmp1 = &tmp3 + &lookup_table.select(scalar_digits[i]);
// Now tmp1 = s_i*P + 16*(prev) in P1xP1 coords
}
tmp1.to_extended()
tmp1.as_extended()
}

View File

@ -0,0 +1,72 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![allow(non_snake_case)]
use core::cmp::Ordering;
use crate::backend::serial::curve_models::{ProjectiveNielsPoint, ProjectivePoint};
use crate::constants;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::window::NafLookupTable5;
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
let a_naf = a.non_adjacent_form(5);
#[cfg(feature = "precomputed-tables")]
let b_naf = b.non_adjacent_form(8);
#[cfg(not(feature = "precomputed-tables"))]
let b_naf = b.non_adjacent_form(5);
// Find starting index
let mut i: usize = 255;
for j in (0..256).rev() {
i = j;
if a_naf[i] != 0 || b_naf[i] != 0 {
break;
}
}
let table_A = NafLookupTable5::<ProjectiveNielsPoint>::from(A);
#[cfg(feature = "precomputed-tables")]
let table_B = &constants::AFFINE_ODD_MULTIPLES_OF_BASEPOINT;
#[cfg(not(feature = "precomputed-tables"))]
let table_B =
&NafLookupTable5::<ProjectiveNielsPoint>::from(&constants::ED25519_BASEPOINT_POINT);
let mut r = ProjectivePoint::identity();
loop {
let mut t = r.double();
match a_naf[i].cmp(&0) {
Ordering::Greater => t = &t.as_extended() + &table_A.select(a_naf[i] as usize),
Ordering::Less => t = &t.as_extended() - &table_A.select(-a_naf[i] as usize),
Ordering::Equal => {}
}
match b_naf[i].cmp(&0) {
Ordering::Greater => t = &t.as_extended() + &table_B.select(b_naf[i] as usize),
Ordering::Less => t = &t.as_extended() - &table_B.select(-b_naf[i] as usize),
Ordering::Equal => {}
}
r = t.as_projective();
if i == 0 {
break;
}
i -= 1;
}
r.as_extended()
}

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ use core::ops::{Sub, SubAssign};
use subtle::Choice;
use subtle::ConditionallySelectable;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// A `FieldElement2625` represents an element of the field
@ -50,14 +51,15 @@ use zeroize::Zeroize;
/// The backend-specific type `FieldElement2625` should not be used
/// outside of the `curve25519_dalek::field` module.
#[derive(Copy, Clone)]
pub struct FieldElement2625(pub (crate) [u32; 10]);
pub struct FieldElement2625(pub(crate) [u32; 10]);
impl Debug for FieldElement2625 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FieldElement2625({:?})", &self.0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for FieldElement2625 {
fn zeroize(&mut self) {
self.0.zeroize();
@ -98,7 +100,8 @@ impl<'b> SubAssign<&'b FieldElement2625> for FieldElement2625 {
((self.0[7] + (0x1ffffff << 4)) - b[7]) as u64,
((self.0[8] + (0x3ffffff << 4)) - b[8]) as u64,
((self.0[9] + (0x1ffffff << 4)) - b[9]) as u64,
]).0;
])
.0;
}
}
@ -120,14 +123,19 @@ impl<'b> MulAssign<&'b FieldElement2625> for FieldElement2625 {
impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
type Output = FieldElement2625;
#[rustfmt::skip] // keep alignment of z* calculations
fn mul(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
/// Helper function to multiply two 32-bit integers with 64 bits
/// of output.
#[inline(always)]
fn m(x: u32, y: u32) -> u64 { (x as u64) * (y as u64) }
fn m(x: u32, y: u32) -> u64 {
(x as u64) * (y as u64)
}
// Alias self, _rhs for more readable formulas
let x: &[u32;10] = &self.0; let y: &[u32;10] = &_rhs.0;
let x: &[u32; 10] = &self.0;
let y: &[u32; 10] = &_rhs.0;
// We assume that the input limbs x[i], y[i] are bounded by:
//
@ -177,16 +185,16 @@ impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
let x7_2 = 2 * x[7];
let x9_2 = 2 * x[9];
let z0 = m(x[0],y[0]) + m(x1_2,y9_19) + m(x[2],y8_19) + m(x3_2,y7_19) + m(x[4],y6_19) + m(x5_2,y5_19) + m(x[6],y4_19) + m(x7_2,y3_19) + m(x[8],y2_19) + m(x9_2,y1_19);
let z1 = m(x[0],y[1]) + m(x[1],y[0]) + m(x[2],y9_19) + m(x[3],y8_19) + m(x[4],y7_19) + m(x[5],y6_19) + m(x[6],y5_19) + m(x[7],y4_19) + m(x[8],y3_19) + m(x[9],y2_19);
let z2 = m(x[0],y[2]) + m(x1_2,y[1]) + m(x[2],y[0]) + m(x3_2,y9_19) + m(x[4],y8_19) + m(x5_2,y7_19) + m(x[6],y6_19) + m(x7_2,y5_19) + m(x[8],y4_19) + m(x9_2,y3_19);
let z3 = m(x[0],y[3]) + m(x[1],y[2]) + m(x[2],y[1]) + m(x[3],y[0]) + m(x[4],y9_19) + m(x[5],y8_19) + m(x[6],y7_19) + m(x[7],y6_19) + m(x[8],y5_19) + m(x[9],y4_19);
let z4 = m(x[0],y[4]) + m(x1_2,y[3]) + m(x[2],y[2]) + m(x3_2,y[1]) + m(x[4],y[0]) + m(x5_2,y9_19) + m(x[6],y8_19) + m(x7_2,y7_19) + m(x[8],y6_19) + m(x9_2,y5_19);
let z5 = m(x[0],y[5]) + m(x[1],y[4]) + m(x[2],y[3]) + m(x[3],y[2]) + m(x[4],y[1]) + m(x[5],y[0]) + m(x[6],y9_19) + m(x[7],y8_19) + m(x[8],y7_19) + m(x[9],y6_19);
let z6 = m(x[0],y[6]) + m(x1_2,y[5]) + m(x[2],y[4]) + m(x3_2,y[3]) + m(x[4],y[2]) + m(x5_2,y[1]) + m(x[6],y[0]) + m(x7_2,y9_19) + m(x[8],y8_19) + m(x9_2,y7_19);
let z7 = m(x[0],y[7]) + m(x[1],y[6]) + m(x[2],y[5]) + m(x[3],y[4]) + m(x[4],y[3]) + m(x[5],y[2]) + m(x[6],y[1]) + m(x[7],y[0]) + m(x[8],y9_19) + m(x[9],y8_19);
let z8 = m(x[0],y[8]) + m(x1_2,y[7]) + m(x[2],y[6]) + m(x3_2,y[5]) + m(x[4],y[4]) + m(x5_2,y[3]) + m(x[6],y[2]) + m(x7_2,y[1]) + m(x[8],y[0]) + m(x9_2,y9_19);
let z9 = m(x[0],y[9]) + m(x[1],y[8]) + m(x[2],y[7]) + m(x[3],y[6]) + m(x[4],y[5]) + m(x[5],y[4]) + m(x[6],y[3]) + m(x[7],y[2]) + m(x[8],y[1]) + m(x[9],y[0]);
let z0 = m(x[0], y[0]) + m(x1_2, y9_19) + m(x[2], y8_19) + m(x3_2, y7_19) + m(x[4], y6_19) + m(x5_2, y5_19) + m(x[6], y4_19) + m(x7_2, y3_19) + m(x[8], y2_19) + m(x9_2, y1_19);
let z1 = m(x[0], y[1]) + m(x[1], y[0]) + m(x[2], y9_19) + m(x[3], y8_19) + m(x[4], y7_19) + m(x[5], y6_19) + m(x[6], y5_19) + m(x[7], y4_19) + m(x[8], y3_19) + m(x[9], y2_19);
let z2 = m(x[0], y[2]) + m(x1_2, y[1]) + m(x[2], y[0]) + m(x3_2, y9_19) + m(x[4], y8_19) + m(x5_2, y7_19) + m(x[6], y6_19) + m(x7_2, y5_19) + m(x[8], y4_19) + m(x9_2, y3_19);
let z3 = m(x[0], y[3]) + m(x[1], y[2]) + m(x[2], y[1]) + m(x[3], y[0]) + m(x[4], y9_19) + m(x[5], y8_19) + m(x[6], y7_19) + m(x[7], y6_19) + m(x[8], y5_19) + m(x[9], y4_19);
let z4 = m(x[0], y[4]) + m(x1_2, y[3]) + m(x[2], y[2]) + m(x3_2, y[1]) + m(x[4], y[0]) + m(x5_2, y9_19) + m(x[6], y8_19) + m(x7_2, y7_19) + m(x[8], y6_19) + m(x9_2, y5_19);
let z5 = m(x[0], y[5]) + m(x[1], y[4]) + m(x[2], y[3]) + m(x[3], y[2]) + m(x[4], y[1]) + m(x[5], y[0]) + m(x[6], y9_19) + m(x[7], y8_19) + m(x[8], y7_19) + m(x[9], y6_19);
let z6 = m(x[0], y[6]) + m(x1_2, y[5]) + m(x[2], y[4]) + m(x3_2, y[3]) + m(x[4], y[2]) + m(x5_2, y[1]) + m(x[6], y[0]) + m(x7_2, y9_19) + m(x[8], y8_19) + m(x9_2, y7_19);
let z7 = m(x[0], y[7]) + m(x[1], y[6]) + m(x[2], y[5]) + m(x[3], y[4]) + m(x[4], y[3]) + m(x[5], y[2]) + m(x[6], y[1]) + m(x[7], y[0]) + m(x[8], y9_19) + m(x[9], y8_19);
let z8 = m(x[0], y[8]) + m(x1_2, y[7]) + m(x[2], y[6]) + m(x3_2, y[5]) + m(x[4], y[4]) + m(x5_2, y[3]) + m(x[6], y[2]) + m(x7_2, y[1]) + m(x[8], y[0]) + m(x9_2, y9_19);
let z9 = m(x[0], y[9]) + m(x[1], y[8]) + m(x[2], y[7]) + m(x[3], y[6]) + m(x[4], y[5]) + m(x[5], y[4]) + m(x[6], y[3]) + m(x[7], y[2]) + m(x[8], y[1]) + m(x[9], y[0]);
// How big is the contribution to z[i+j] from x[i], y[j]?
//
@ -276,6 +284,20 @@ impl ConditionallySelectable for FieldElement2625 {
}
impl FieldElement2625 {
pub(crate) const fn from_limbs(limbs: [u32; 10]) -> FieldElement2625 {
FieldElement2625(limbs)
}
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
pub const ONE: FieldElement2625 = FieldElement2625::from_limbs([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// The scalar \\( -1 \\).
pub const MINUS_ONE: FieldElement2625 = FieldElement2625::from_limbs([
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
0x3ffffff, 0x1ffffff,
]);
/// Invert the sign of this field element
pub fn negate(&mut self) {
// Compute -b as ((2^4 * p) - b) to avoid underflow.
@ -294,27 +316,9 @@ impl FieldElement2625 {
self.0 = neg.0;
}
/// Construct zero.
pub fn zero() -> FieldElement2625 {
FieldElement2625([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
/// Construct one.
pub fn one() -> FieldElement2625 {
FieldElement2625([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
/// Construct -1.
pub fn minus_one() -> FieldElement2625 {
FieldElement2625([
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff,
0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
])
}
/// Given `k > 0`, return `self^(2^k)`.
pub fn pow2k(&self, k: u32) -> FieldElement2625 {
debug_assert!( k > 0 );
debug_assert!(k > 0);
let mut z = self.square();
for _ in 1..k {
z = z.square();
@ -328,6 +332,7 @@ impl FieldElement2625 {
///
/// In other words, each coefficient of the result is bounded by
/// either `2^(25 + 0.007)` or `2^(26 + 0.007)`, as appropriate.
#[rustfmt::skip] // keep alignment of carry chain
fn reduce(mut z: [u64; 10]) -> FieldElement2625 {
const LOW_25_BITS: u64 = (1 << 25) - 1;
@ -339,11 +344,11 @@ impl FieldElement2625 {
debug_assert!(i < 9);
if i % 2 == 0 {
// Even limbs have 26 bits
z[i+1] += z[i] >> 26;
z[i + 1] += z[i] >> 26;
z[i] &= LOW_26_BITS;
} else {
// Odd limbs have 25 bits
z[i+1] += z[i] >> 25;
z[i + 1] += z[i] >> 25;
z[i] &= LOW_25_BITS;
}
}
@ -360,7 +365,7 @@ impl FieldElement2625 {
// and z[5] < 2^25 + 2^13.0002 < 2^25.0004 (good enough)
// Last carry has a multiplication by 19:
z[0] += 19*(z[9] >> 25);
z[0] += 19 * (z[9] >> 25);
z[9] &= LOW_25_BITS;
// Since z[9] < 2^64, c < 2^(64-25) = 2^39,
@ -371,8 +376,16 @@ impl FieldElement2625 {
// and we're done.
FieldElement2625([
z[0] as u32, z[1] as u32, z[2] as u32, z[3] as u32, z[4] as u32,
z[5] as u32, z[6] as u32, z[7] as u32, z[8] as u32, z[9] as u32,
z[0] as u32,
z[1] as u32,
z[2] as u32,
z[3] as u32,
z[4] as u32,
z[5] as u32,
z[6] as u32,
z[7] as u32,
z[8] as u32,
z[9] as u32,
])
}
@ -387,7 +400,8 @@ impl FieldElement2625 {
/// encoding of every field element should decode, re-encode to
/// the canonical encoding, and check that the input was
/// canonical.
pub fn from_bytes(data: &[u8; 32]) -> FieldElement2625 { //FeFromBytes
#[rustfmt::skip] // keep alignment of h[*] values
pub fn from_bytes(data: &[u8; 32]) -> FieldElement2625 {
#[inline]
fn load3(b: &[u8]) -> u64 {
(b[0] as u64) | ((b[1] as u64) << 8) | ((b[2] as u64) << 16)
@ -416,15 +430,24 @@ impl FieldElement2625 {
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn to_bytes(&self) -> [u8; 32] {
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
let inp = &self.0;
// Reduce the value represented by `in` to the range [0,2*p)
let mut h: [u32; 10] = FieldElement2625::reduce([
// XXX this cast is annoying
inp[0] as u64, inp[1] as u64, inp[2] as u64, inp[3] as u64, inp[4] as u64,
inp[5] as u64, inp[6] as u64, inp[7] as u64, inp[8] as u64, inp[9] as u64,
]).0;
inp[0] as u64,
inp[1] as u64,
inp[2] as u64,
inp[3] as u64,
inp[4] as u64,
inp[5] as u64,
inp[6] as u64,
inp[7] as u64,
inp[8] as u64,
inp[9] as u64,
])
.0;
// Let h be the value to encode.
//
@ -446,40 +469,40 @@ impl FieldElement2625 {
q = (h[8] + q) >> 26;
q = (h[9] + q) >> 25;
debug_assert!( q == 0 || q == 1 );
debug_assert!(q == 0 || q == 1);
// Now we can compute r as r = h - pq = r - (2^255-19)q = r + 19q - 2^255q
const LOW_25_BITS: u32 = (1 << 25) - 1;
const LOW_26_BITS: u32 = (1 << 26) - 1;
h[0] += 19*q;
h[0] += 19 * q;
// Now carry the result to compute r + 19q...
h[1] += h[0] >> 26;
h[0] = h[0] & LOW_26_BITS;
h[0] &= LOW_26_BITS;
h[2] += h[1] >> 25;
h[1] = h[1] & LOW_25_BITS;
h[1] &= LOW_25_BITS;
h[3] += h[2] >> 26;
h[2] = h[2] & LOW_26_BITS;
h[2] &= LOW_26_BITS;
h[4] += h[3] >> 25;
h[3] = h[3] & LOW_25_BITS;
h[3] &= LOW_25_BITS;
h[5] += h[4] >> 26;
h[4] = h[4] & LOW_26_BITS;
h[4] &= LOW_26_BITS;
h[6] += h[5] >> 25;
h[5] = h[5] & LOW_25_BITS;
h[5] &= LOW_25_BITS;
h[7] += h[6] >> 26;
h[6] = h[6] & LOW_26_BITS;
h[6] &= LOW_26_BITS;
h[8] += h[7] >> 25;
h[7] = h[7] & LOW_25_BITS;
h[7] &= LOW_25_BITS;
h[9] += h[8] >> 26;
h[8] = h[8] & LOW_26_BITS;
h[8] &= LOW_26_BITS;
// ... but instead of carrying the value
// (h[9] >> 25) = q*2^255 into another limb,
// discard it, subtracting the value from h.
debug_assert!( (h[9] >> 25) == 0 || (h[9] >> 25) == 1);
h[9] = h[9] & LOW_25_BITS;
debug_assert!((h[9] >> 25) == 0 || (h[9] >> 25) == 1);
h[9] &= LOW_25_BITS;
let mut s = [0u8; 32];
s[0] = (h[0] >> 0) as u8;
@ -521,43 +544,46 @@ impl FieldElement2625 {
s
}
#[rustfmt::skip] // keep alignment of z* calculations
fn square_inner(&self) -> [u64; 10] {
// Optimized version of multiplication for the case of squaring.
// Pre- and post- conditions identical to multiplication function.
let x = &self.0;
let x0_2 = 2 * x[0];
let x1_2 = 2 * x[1];
let x2_2 = 2 * x[2];
let x3_2 = 2 * x[3];
let x4_2 = 2 * x[4];
let x5_2 = 2 * x[5];
let x6_2 = 2 * x[6];
let x7_2 = 2 * x[7];
let x5_19 = 19 * x[5];
let x6_19 = 19 * x[6];
let x7_19 = 19 * x[7];
let x8_19 = 19 * x[8];
let x9_19 = 19 * x[9];
let x0_2 = 2 * x[0];
let x1_2 = 2 * x[1];
let x2_2 = 2 * x[2];
let x3_2 = 2 * x[3];
let x4_2 = 2 * x[4];
let x5_2 = 2 * x[5];
let x6_2 = 2 * x[6];
let x7_2 = 2 * x[7];
let x5_19 = 19 * x[5];
let x6_19 = 19 * x[6];
let x7_19 = 19 * x[7];
let x8_19 = 19 * x[8];
let x9_19 = 19 * x[9];
/// Helper function to multiply two 32-bit integers with 64 bits
/// of output.
#[inline(always)]
fn m(x: u32, y: u32) -> u64 { (x as u64) * (y as u64) }
fn m(x: u32, y: u32) -> u64 {
(x as u64) * (y as u64)
}
// This block is rearranged so that instead of doing a 32-bit multiplication by 38, we do a
// 64-bit multiplication by 2 on the results. This is because lg(38) is too big: we would
// have less than 1 bit of headroom left, which is too little.
let mut z = [0u64;10];
z[0] = m(x[0],x[0]) + m(x2_2,x8_19) + m(x4_2,x6_19) + (m(x1_2,x9_19) + m(x3_2,x7_19) + m(x[5],x5_19))*2;
z[1] = m(x0_2,x[1]) + m(x3_2,x8_19) + m(x5_2,x6_19) + (m(x[2],x9_19) + m(x[4],x7_19))*2;
z[2] = m(x0_2,x[2]) + m(x1_2,x[1]) + m(x4_2,x8_19) + m(x[6],x6_19) + (m(x3_2,x9_19) + m(x5_2,x7_19))*2;
z[3] = m(x0_2,x[3]) + m(x1_2,x[2]) + m(x5_2,x8_19) + (m(x[4],x9_19) + m(x[6],x7_19))*2;
z[4] = m(x0_2,x[4]) + m(x1_2,x3_2) + m(x[2],x[2]) + m(x6_2,x8_19) + (m(x5_2,x9_19) + m(x[7],x7_19))*2;
z[5] = m(x0_2,x[5]) + m(x1_2,x[4]) + m(x2_2,x[3]) + m(x7_2,x8_19) + m(x[6],x9_19)*2;
z[6] = m(x0_2,x[6]) + m(x1_2,x5_2) + m(x2_2,x[4]) + m(x3_2,x[3]) + m(x[8],x8_19) + m(x7_2,x9_19)*2;
z[7] = m(x0_2,x[7]) + m(x1_2,x[6]) + m(x2_2,x[5]) + m(x3_2,x[4]) + m(x[8],x9_19)*2;
z[8] = m(x0_2,x[8]) + m(x1_2,x7_2) + m(x2_2,x[6]) + m(x3_2,x5_2) + m(x[4],x[4]) + m(x[9],x9_19)*2;
z[9] = m(x0_2,x[9]) + m(x1_2,x[8]) + m(x2_2,x[7]) + m(x3_2,x[6]) + m(x4_2,x[5]) ;
let mut z = [0u64; 10];
z[0] = m(x[0], x[0]) + m(x2_2, x8_19) + m(x4_2, x6_19) + (m(x1_2, x9_19) + m(x3_2, x7_19) + m(x[5], x5_19)) * 2;
z[1] = m(x0_2, x[1]) + m(x3_2, x8_19) + m(x5_2, x6_19) + (m(x[2], x9_19) + m(x[4], x7_19) ) * 2;
z[2] = m(x0_2, x[2]) + m(x1_2, x[1]) + m(x4_2, x8_19) + m(x[6], x6_19) + (m(x3_2, x9_19) + m(x5_2, x7_19)) * 2;
z[3] = m(x0_2, x[3]) + m(x1_2, x[2]) + m(x5_2, x8_19) + (m(x[4], x9_19) + m(x[6], x7_19) ) * 2;
z[4] = m(x0_2, x[4]) + m(x1_2, x3_2) + m(x[2], x[2]) + m(x6_2, x8_19) + (m(x5_2, x9_19) + m(x[7], x7_19)) * 2;
z[5] = m(x0_2, x[5]) + m(x1_2, x[4]) + m(x2_2, x[3]) + m(x7_2, x8_19) + m(x[6], x9_19) * 2;
z[6] = m(x0_2, x[6]) + m(x1_2, x5_2) + m(x2_2, x[4]) + m(x3_2, x[3]) + m(x[8], x8_19) + m(x7_2, x9_19) * 2;
z[7] = m(x0_2, x[7]) + m(x1_2, x[6]) + m(x2_2, x[5]) + m(x3_2, x[4]) + m(x[8], x9_19) * 2;
z[8] = m(x0_2, x[8]) + m(x1_2, x7_2) + m(x2_2, x[6]) + m(x3_2, x5_2) + m(x[4], x[4]) + m(x[9], x9_19) * 2;
z[9] = m(x0_2, x[9]) + m(x1_2, x[8]) + m(x2_2, x[7]) + m(x3_2, x[6]) + m(x4_2, x[5]) ;
z
}
@ -570,8 +596,8 @@ impl FieldElement2625 {
/// Compute `2*self^2`.
pub fn square2(&self) -> FieldElement2625 {
let mut coeffs = self.square_inner();
for i in 0..self.0.len() {
coeffs[i] += coeffs[i];
for coeff in &mut coeffs {
*coeff += *coeff;
}
FieldElement2625::reduce(coeffs)
}

View File

@ -13,20 +13,23 @@
use core::fmt::Debug;
use core::ops::{Index, IndexMut};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use constants;
use crate::constants;
/// The `Scalar29` struct represents an element in /l as 9 29-bit limbs
#[derive(Copy,Clone)]
/// The `Scalar29` struct represents an element in \\(\mathbb{Z} / \ell\mathbb{Z}\\) as 9 29-bit
/// limbs
#[derive(Copy, Clone)]
pub struct Scalar29(pub [u32; 9]);
impl Debug for Scalar29 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Scalar29: {:?}", &self.0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for Scalar29 {
fn zeroize(&mut self) {
self.0.zeroize();
@ -53,12 +56,11 @@ fn m(x: u32, y: u32) -> u64 {
}
impl Scalar29 {
/// Return the zero scalar.
pub fn zero() -> Scalar29 {
Scalar29([0,0,0,0,0,0,0,0,0])
}
/// The scalar \\( 0 \\).
pub const ZERO: Scalar29 = Scalar29([0, 0, 0, 0, 0, 0, 0, 0, 0]);
/// Unpack a 32 byte / 256 bit scalar into 9 29-bit limbs.
#[rustfmt::skip] // keep alignment of s[*] calculations
pub fn from_bytes(bytes: &[u8; 32]) -> Scalar29 {
let mut words = [0u32; 8];
for i in 0..8 {
@ -69,22 +71,23 @@ impl Scalar29 {
let mask = (1u32 << 29) - 1;
let top_mask = (1u32 << 24) - 1;
let mut s = Scalar29::zero();
let mut s = Scalar29::ZERO;
s[ 0] = words[0] & mask;
s[ 1] = ((words[0] >> 29) | (words[1] << 3)) & mask;
s[ 2] = ((words[1] >> 26) | (words[2] << 6)) & mask;
s[ 3] = ((words[2] >> 23) | (words[3] << 9)) & mask;
s[ 4] = ((words[3] >> 20) | (words[4] << 12)) & mask;
s[ 5] = ((words[4] >> 17) | (words[5] << 15)) & mask;
s[ 6] = ((words[5] >> 14) | (words[6] << 18)) & mask;
s[ 7] = ((words[6] >> 11) | (words[7] << 21)) & mask;
s[ 8] = (words[7] >> 8) & top_mask;
s[0] = words[0] & mask;
s[1] = ((words[0] >> 29) | (words[1] << 3)) & mask;
s[2] = ((words[1] >> 26) | (words[2] << 6)) & mask;
s[3] = ((words[2] >> 23) | (words[3] << 9)) & mask;
s[4] = ((words[3] >> 20) | (words[4] << 12)) & mask;
s[5] = ((words[4] >> 17) | (words[5] << 15)) & mask;
s[6] = ((words[5] >> 14) | (words[6] << 18)) & mask;
s[7] = ((words[6] >> 11) | (words[7] << 21)) & mask;
s[8] = (words[7] >> 8) & top_mask;
s
}
/// Reduce a 64 byte / 512 bit scalar mod l.
#[rustfmt::skip] // keep alignment of lo[*] calculations
pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar29 {
let mut words = [0u32; 16];
for i in 0..16 {
@ -94,8 +97,8 @@ impl Scalar29 {
}
let mask = (1u32 << 29) - 1;
let mut lo = Scalar29::zero();
let mut hi = Scalar29::zero();
let mut lo = Scalar29::ZERO;
let mut hi = Scalar29::ZERO;
lo[0] = words[ 0] & mask;
lo[1] = ((words[ 0] >> 29) | (words[ 1] << 3)) & mask;
@ -123,48 +126,50 @@ impl Scalar29 {
}
/// Pack the limbs of this `Scalar29` into 32 bytes.
pub fn to_bytes(&self) -> [u8; 32] {
#[rustfmt::skip] // keep alignment of s[*] calculations
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
let mut s = [0u8; 32];
s[0] = (self.0[ 0] >> 0) as u8;
s[1] = (self.0[ 0] >> 8) as u8;
s[2] = (self.0[ 0] >> 16) as u8;
s[3] = ((self.0[ 0] >> 24) | (self.0[ 1] << 5)) as u8;
s[4] = (self.0[ 1] >> 3) as u8;
s[5] = (self.0[ 1] >> 11) as u8;
s[6] = (self.0[ 1] >> 19) as u8;
s[7] = ((self.0[ 1] >> 27) | (self.0[ 2] << 2)) as u8;
s[8] = (self.0[ 2] >> 6) as u8;
s[9] = (self.0[ 2] >> 14) as u8;
s[10] = ((self.0[ 2] >> 22) | (self.0[ 3] << 7)) as u8;
s[11] = (self.0[ 3] >> 1) as u8;
s[12] = (self.0[ 3] >> 9) as u8;
s[13] = (self.0[ 3] >> 17) as u8;
s[14] = ((self.0[ 3] >> 25) | (self.0[ 4] << 4)) as u8;
s[15] = (self.0[ 4] >> 4) as u8;
s[16] = (self.0[ 4] >> 12) as u8;
s[17] = (self.0[ 4] >> 20) as u8;
s[18] = ((self.0[ 4] >> 28) | (self.0[ 5] << 1)) as u8;
s[19] = (self.0[ 5] >> 7) as u8;
s[20] = (self.0[ 5] >> 15) as u8;
s[21] = ((self.0[ 5] >> 23) | (self.0[ 6] << 6)) as u8;
s[22] = (self.0[ 6] >> 2) as u8;
s[23] = (self.0[ 6] >> 10) as u8;
s[24] = (self.0[ 6] >> 18) as u8;
s[25] = ((self.0[ 6] >> 26) | (self.0[ 7] << 3)) as u8;
s[26] = (self.0[ 7] >> 5) as u8;
s[27] = (self.0[ 7] >> 13) as u8;
s[28] = (self.0[ 7] >> 21) as u8;
s[29] = (self.0[ 8] >> 0) as u8;
s[30] = (self.0[ 8] >> 8) as u8;
s[31] = (self.0[ 8] >> 16) as u8;
s[ 0] = (self.0[0] >> 0) as u8;
s[ 1] = (self.0[0] >> 8) as u8;
s[ 2] = (self.0[0] >> 16) as u8;
s[ 3] = ((self.0[0] >> 24) | (self.0[1] << 5)) as u8;
s[ 4] = (self.0[1] >> 3) as u8;
s[ 5] = (self.0[1] >> 11) as u8;
s[ 6] = (self.0[1] >> 19) as u8;
s[ 7] = ((self.0[1] >> 27) | (self.0[2] << 2)) as u8;
s[ 8] = (self.0[2] >> 6) as u8;
s[ 9] = (self.0[2] >> 14) as u8;
s[10] = ((self.0[2] >> 22) | (self.0[3] << 7)) as u8;
s[11] = (self.0[3] >> 1) as u8;
s[12] = (self.0[3] >> 9) as u8;
s[13] = (self.0[3] >> 17) as u8;
s[14] = ((self.0[3] >> 25) | (self.0[4] << 4)) as u8;
s[15] = (self.0[4] >> 4) as u8;
s[16] = (self.0[4] >> 12) as u8;
s[17] = (self.0[4] >> 20) as u8;
s[18] = ((self.0[4] >> 28) | (self.0[5] << 1)) as u8;
s[19] = (self.0[5] >> 7) as u8;
s[20] = (self.0[5] >> 15) as u8;
s[21] = ((self.0[5] >> 23) | (self.0[6] << 6)) as u8;
s[22] = (self.0[6] >> 2) as u8;
s[23] = (self.0[6] >> 10) as u8;
s[24] = (self.0[6] >> 18) as u8;
s[25] = ((self.0[6] >> 26) | (self.0[7] << 3)) as u8;
s[26] = (self.0[7] >> 5) as u8;
s[27] = (self.0[7] >> 13) as u8;
s[28] = (self.0[7] >> 21) as u8;
s[29] = (self.0[8] >> 0) as u8;
s[30] = (self.0[8] >> 8) as u8;
s[31] = (self.0[8] >> 16) as u8;
s
}
/// Compute `a + b` (mod l).
pub fn add(a: &Scalar29, b: &Scalar29) -> Scalar29 {
let mut sum = Scalar29::zero();
let mut sum = Scalar29::ZERO;
let mask = (1u32 << 29) - 1;
// a + b
@ -180,7 +185,15 @@ impl Scalar29 {
/// Compute `a - b` (mod l).
pub fn sub(a: &Scalar29, b: &Scalar29) -> Scalar29 {
let mut difference = Scalar29::zero();
// Optimization barrier to prevent compiler from inserting branch instructions
// TODO(tarcieri): find a better home (or abstraction) for this
fn black_box(value: u32) -> u32 {
// SAFETY: `u32` is a simple integer `Copy` type and `value` lives on the stack so
// a pointer to it will be valid.
unsafe { core::ptr::read_volatile(&value) }
}
let mut difference = Scalar29::ZERO;
let mask = (1u32 << 29) - 1;
// a - b
@ -194,7 +207,7 @@ impl Scalar29 {
let underflow_mask = ((borrow >> 31) ^ 1).wrapping_sub(1);
let mut carry: u32 = 0;
for i in 0..9 {
carry = (carry >> 29) + difference[i] + (constants::L[i] & underflow_mask);
carry = (carry >> 29) + difference[i] + (constants::L[i] & black_box(underflow_mask));
difference[i] = carry & mask;
}
@ -205,26 +218,27 @@ impl Scalar29 {
///
/// This is implemented with a one-level refined Karatsuba decomposition
#[inline(always)]
#[rustfmt::skip] // keep alignment of z[*] calculations
pub (crate) fn mul_internal(a: &Scalar29, b: &Scalar29) -> [u64; 17] {
let mut z = [0u64; 17];
z[0] = m(a[0],b[0]); // c00
z[1] = m(a[0],b[1]) + m(a[1],b[0]); // c01
z[2] = m(a[0],b[2]) + m(a[1],b[1]) + m(a[2],b[0]); // c02
z[3] = m(a[0],b[3]) + m(a[1],b[2]) + m(a[2],b[1]) + m(a[3],b[0]); // c03
z[4] = m(a[0],b[4]) + m(a[1],b[3]) + m(a[2],b[2]) + m(a[3],b[1]) + m(a[4],b[0]); // c04
z[5] = m(a[1],b[4]) + m(a[2],b[3]) + m(a[3],b[2]) + m(a[4],b[1]); // c05
z[6] = m(a[2],b[4]) + m(a[3],b[3]) + m(a[4],b[2]); // c06
z[7] = m(a[3],b[4]) + m(a[4],b[3]); // c07
z[8] = (m(a[4],b[4])).wrapping_sub(z[3]); // c08 - c03
z[0] = m(a[0], b[0]); // c00
z[1] = m(a[0], b[1]) + m(a[1], b[0]); // c01
z[2] = m(a[0], b[2]) + m(a[1], b[1]) + m(a[2], b[0]); // c02
z[3] = m(a[0], b[3]) + m(a[1], b[2]) + m(a[2], b[1]) + m(a[3], b[0]); // c03
z[4] = m(a[0], b[4]) + m(a[1], b[3]) + m(a[2], b[2]) + m(a[3], b[1]) + m(a[4], b[0]); // c04
z[5] = m(a[1], b[4]) + m(a[2], b[3]) + m(a[3], b[2]) + m(a[4], b[1]); // c05
z[6] = m(a[2], b[4]) + m(a[3], b[3]) + m(a[4], b[2]); // c06
z[7] = m(a[3], b[4]) + m(a[4], b[3]); // c07
z[8] = (m(a[4], b[4])).wrapping_sub(z[3]); // c08 - c03
z[10] = z[5].wrapping_sub(m(a[5],b[5])); // c05mc10
z[11] = z[6].wrapping_sub(m(a[5],b[6]) + m(a[6],b[5])); // c06mc11
z[12] = z[7].wrapping_sub(m(a[5],b[7]) + m(a[6],b[6]) + m(a[7],b[5])); // c07mc12
z[13] = m(a[5],b[8]) + m(a[6],b[7]) + m(a[7],b[6]) + m(a[8],b[5]); // c13
z[14] = m(a[6],b[8]) + m(a[7],b[7]) + m(a[8],b[6]); // c14
z[15] = m(a[7],b[8]) + m(a[8],b[7]); // c15
z[16] = m(a[8],b[8]); // c16
z[10] = z[5].wrapping_sub(m(a[5], b[5])); // c05mc10
z[11] = z[6].wrapping_sub(m(a[5], b[6]) + m(a[6], b[5])); // c06mc11
z[12] = z[7].wrapping_sub(m(a[5], b[7]) + m(a[6], b[6]) + m(a[7], b[5])); // c07mc12
z[13] = m(a[5], b[8]) + m(a[6], b[7]) + m(a[7], b[6]) + m(a[8], b[5]); // c13
z[14] = m(a[6], b[8]) + m(a[7], b[7]) + m(a[8], b[6]); // c14
z[15] = m(a[7], b[8]) + m(a[8], b[7]); // c15
z[16] = m(a[8], b[8]); // c16
z[ 5] = z[10].wrapping_sub(z[ 0]); // c05mc10 - c00
z[ 6] = z[11].wrapping_sub(z[ 1]); // c06mc11 - c01
@ -235,68 +249,70 @@ impl Scalar29 {
z[11] = z[16].wrapping_add(z[11]); // c16 + c06mc11
let aa = [
a[0]+a[5],
a[1]+a[6],
a[2]+a[7],
a[3]+a[8]
a[0] + a[5],
a[1] + a[6],
a[2] + a[7],
a[3] + a[8]
];
let bb = [
b[0]+b[5],
b[1]+b[6],
b[2]+b[7],
b[3]+b[8]
b[0] + b[5],
b[1] + b[6],
b[2] + b[7],
b[3] + b[8]
];
z[ 5] = (m(aa[0],bb[0])) .wrapping_add(z[ 5]); // c20 + c05mc10 - c00
z[ 6] = (m(aa[0],bb[1]) + m(aa[1],bb[0])) .wrapping_add(z[ 6]); // c21 + c06mc11 - c01
z[ 7] = (m(aa[0],bb[2]) + m(aa[1],bb[1]) + m(aa[2],bb[0])) .wrapping_add(z[ 7]); // c22 + c07mc12 - c02
z[ 8] = (m(aa[0],bb[3]) + m(aa[1],bb[2]) + m(aa[2],bb[1]) + m(aa[3],bb[0])) .wrapping_add(z[ 8]); // c23 + c08mc13 - c03
z[ 9] = (m(aa[0], b[4]) + m(aa[1],bb[3]) + m(aa[2],bb[2]) + m(aa[3],bb[1]) + m(a[4],bb[0])).wrapping_sub(z[ 9]); // c24 - c14 - c04
z[10] = ( m(aa[1], b[4]) + m(aa[2],bb[3]) + m(aa[3],bb[2]) + m(a[4],bb[1])).wrapping_sub(z[10]); // c25 - c15 - c05mc10
z[11] = ( m(aa[2], b[4]) + m(aa[3],bb[3]) + m(a[4],bb[2])).wrapping_sub(z[11]); // c26 - c16 - c06mc11
z[12] = ( m(aa[3], b[4]) + m(a[4],bb[3])).wrapping_sub(z[12]); // c27 - c07mc12
z[ 5] = (m(aa[0], bb[0])) .wrapping_add(z[ 5]); // c20 + c05mc10 - c00
z[ 6] = (m(aa[0], bb[1]) + m(aa[1], bb[0])) .wrapping_add(z[ 6]); // c21 + c06mc11 - c01
z[ 7] = (m(aa[0], bb[2]) + m(aa[1], bb[1]) + m(aa[2], bb[0])) .wrapping_add(z[ 7]); // c22 + c07mc12 - c02
z[ 8] = (m(aa[0], bb[3]) + m(aa[1], bb[2]) + m(aa[2], bb[1]) + m(aa[3], bb[0])) .wrapping_add(z[ 8]); // c23 + c08mc13 - c03
z[ 9] = (m(aa[0], b[4]) + m(aa[1], bb[3]) + m(aa[2], bb[2]) + m(aa[3], bb[1]) + m(a[4], bb[0])).wrapping_sub(z[ 9]); // c24 - c14 - c04
z[10] = ( m(aa[1], b[4]) + m(aa[2], bb[3]) + m(aa[3], bb[2]) + m(a[4], bb[1])).wrapping_sub(z[10]); // c25 - c15 - c05mc10
z[11] = ( m(aa[2], b[4]) + m(aa[3], bb[3]) + m(a[4], bb[2])).wrapping_sub(z[11]); // c26 - c16 - c06mc11
z[12] = ( m(aa[3], b[4]) + m(a[4], bb[3])).wrapping_sub(z[12]); // c27 - c07mc12
z
}
/// Compute `a^2`.
#[inline(always)]
#[rustfmt::skip] // keep alignment of calculations
fn square_internal(a: &Scalar29) -> [u64; 17] {
let aa = [
a[0]*2,
a[1]*2,
a[2]*2,
a[3]*2,
a[4]*2,
a[5]*2,
a[6]*2,
a[7]*2
a[0] * 2,
a[1] * 2,
a[2] * 2,
a[3] * 2,
a[4] * 2,
a[5] * 2,
a[6] * 2,
a[7] * 2
];
[
m( a[0],a[0]),
m(aa[0],a[1]),
m(aa[0],a[2]) + m( a[1],a[1]),
m(aa[0],a[3]) + m(aa[1],a[2]),
m(aa[0],a[4]) + m(aa[1],a[3]) + m( a[2],a[2]),
m(aa[0],a[5]) + m(aa[1],a[4]) + m(aa[2],a[3]),
m(aa[0],a[6]) + m(aa[1],a[5]) + m(aa[2],a[4]) + m( a[3],a[3]),
m(aa[0],a[7]) + m(aa[1],a[6]) + m(aa[2],a[5]) + m(aa[3],a[4]),
m(aa[0],a[8]) + m(aa[1],a[7]) + m(aa[2],a[6]) + m(aa[3],a[5]) + m( a[4],a[4]),
m(aa[1],a[8]) + m(aa[2],a[7]) + m(aa[3],a[6]) + m(aa[4],a[5]),
m(aa[2],a[8]) + m(aa[3],a[7]) + m(aa[4],a[6]) + m( a[5],a[5]),
m(aa[3],a[8]) + m(aa[4],a[7]) + m(aa[5],a[6]),
m(aa[4],a[8]) + m(aa[5],a[7]) + m( a[6],a[6]),
m(aa[5],a[8]) + m(aa[6],a[7]),
m(aa[6],a[8]) + m( a[7],a[7]),
m(aa[7],a[8]),
m( a[8],a[8]),
m( a[0], a[0]),
m(aa[0], a[1]),
m(aa[0], a[2]) + m( a[1], a[1]),
m(aa[0], a[3]) + m(aa[1], a[2]),
m(aa[0], a[4]) + m(aa[1], a[3]) + m( a[2], a[2]),
m(aa[0], a[5]) + m(aa[1], a[4]) + m(aa[2], a[3]),
m(aa[0], a[6]) + m(aa[1], a[5]) + m(aa[2], a[4]) + m( a[3], a[3]),
m(aa[0], a[7]) + m(aa[1], a[6]) + m(aa[2], a[5]) + m(aa[3], a[4]),
m(aa[0], a[8]) + m(aa[1], a[7]) + m(aa[2], a[6]) + m(aa[3], a[5]) + m( a[4], a[4]),
m(aa[1], a[8]) + m(aa[2], a[7]) + m(aa[3], a[6]) + m(aa[4], a[5]),
m(aa[2], a[8]) + m(aa[3], a[7]) + m(aa[4], a[6]) + m( a[5], a[5]),
m(aa[3], a[8]) + m(aa[4], a[7]) + m(aa[5], a[6]),
m(aa[4], a[8]) + m(aa[5], a[7]) + m( a[6], a[6]),
m(aa[5], a[8]) + m(aa[6], a[7]),
m(aa[6], a[8]) + m( a[7], a[7]),
m(aa[7], a[8]),
m( a[8], a[8]),
]
}
/// Compute `limbs/R` (mod l), where R is the Montgomery modulus 2^261
#[inline(always)]
#[rustfmt::skip] // keep alignment of part1() and part2() computations
pub (crate) fn montgomery_reduce(limbs: &[u64; 17]) -> Scalar29 {
#[inline(always)]
@ -369,11 +385,12 @@ impl Scalar29 {
/// Puts a Scalar29 in to Montgomery form, i.e. computes `a*R (mod l)`
#[inline(never)]
pub fn to_montgomery(&self) -> Scalar29 {
pub fn as_montgomery(&self) -> Scalar29 {
Scalar29::montgomery_mul(self, &constants::RR)
}
/// Takes a Scalar29 out of Montgomery form, i.e. computes `a/R (mod l)`
#[allow(clippy::wrong_self_convention)]
pub fn from_montgomery(&self) -> Scalar29 {
let mut limbs = [0u64; 17];
for i in 0..9 {
@ -393,65 +410,65 @@ mod test {
/// x = 2^253-1 = 14474011154664524427946373126085988481658748083205070504932198000989141204991
/// x = 7237005577332262213973186563042994240801631723825162898930247062703686954002 mod l
/// x = 5147078182513738803124273553712992179887200054963030844803268920753008712037*R mod l in Montgomery form
pub static X: Scalar29 = Scalar29(
[0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff,
0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff,
0x001fffff]);
pub static X: Scalar29 = Scalar29([
0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff,
0x1fffffff, 0x001fffff,
]);
/// x^2 = 3078544782642840487852506753550082162405942681916160040940637093560259278169 mod l
pub static XX: Scalar29 = Scalar29(
[0x00217559, 0x000b3401, 0x103ff43b, 0x1462a62c,
0x1d6f9f38, 0x18e7a42f, 0x09a3dcee, 0x008dbe18,
0x0006ce65]);
pub static XX: Scalar29 = Scalar29([
0x00217559, 0x000b3401, 0x103ff43b, 0x1462a62c, 0x1d6f9f38, 0x18e7a42f, 0x09a3dcee,
0x008dbe18, 0x0006ce65,
]);
/// x^2 = 2912514428060642753613814151688322857484807845836623976981729207238463947987*R mod l in Montgomery form
pub static XX_MONT: Scalar29 = Scalar29(
[0x152b4d2e, 0x0571d53b, 0x1da6d964, 0x188663b6,
0x1d1b5f92, 0x19d50e3f, 0x12306c29, 0x0c6f26fe,
0x00030edb]);
pub static XX_MONT: Scalar29 = Scalar29([
0x152b4d2e, 0x0571d53b, 0x1da6d964, 0x188663b6, 0x1d1b5f92, 0x19d50e3f, 0x12306c29,
0x0c6f26fe, 0x00030edb,
]);
/// y = 6145104759870991071742105800796537629880401874866217824609283457819451087098
pub static Y: Scalar29 = Scalar29(
[0x1e1458fa, 0x165ba838, 0x1d787b36, 0x0e577f3a,
0x1d2baf06, 0x1d689a19, 0x1fff3047, 0x117704ab,
0x000d9601]);
pub static Y: Scalar29 = Scalar29([
0x1e1458fa, 0x165ba838, 0x1d787b36, 0x0e577f3a, 0x1d2baf06, 0x1d689a19, 0x1fff3047,
0x117704ab, 0x000d9601,
]);
/// x*y = 36752150652102274958925982391442301741
pub static XY: Scalar29 = Scalar29(
[0x0ba7632d, 0x017736bb, 0x15c76138, 0x0c69daa1,
0x000001ba, 0x00000000, 0x00000000, 0x00000000,
0x00000000]);
pub static XY: Scalar29 = Scalar29([
0x0ba7632d, 0x017736bb, 0x15c76138, 0x0c69daa1, 0x000001ba, 0x00000000, 0x00000000,
0x00000000, 0x00000000,
]);
/// x*y = 3783114862749659543382438697751927473898937741870308063443170013240655651591*R mod l in Montgomery form
pub static XY_MONT: Scalar29 = Scalar29(
[0x077b51e1, 0x1c64e119, 0x02a19ef5, 0x18d2129e,
0x00de0430, 0x045a7bc8, 0x04cfc7c9, 0x1c002681,
0x000bdc1c]);
pub static XY_MONT: Scalar29 = Scalar29([
0x077b51e1, 0x1c64e119, 0x02a19ef5, 0x18d2129e, 0x00de0430, 0x045a7bc8, 0x04cfc7c9,
0x1c002681, 0x000bdc1c,
]);
/// a = 2351415481556538453565687241199399922945659411799870114962672658845158063753
pub static A: Scalar29 = Scalar29(
[0x07b3be89, 0x02291b60, 0x14a99f03, 0x07dc3787,
0x0a782aae, 0x16262525, 0x0cfdb93f, 0x13f5718d,
0x000532da]);
pub static A: Scalar29 = Scalar29([
0x07b3be89, 0x02291b60, 0x14a99f03, 0x07dc3787, 0x0a782aae, 0x16262525, 0x0cfdb93f,
0x13f5718d, 0x000532da,
]);
/// b = 4885590095775723760407499321843594317911456947580037491039278279440296187236
pub static B: Scalar29 = Scalar29(
[0x15421564, 0x1e69fd72, 0x093d9692, 0x161785be,
0x1587d69f, 0x09d9dada, 0x130246c0, 0x0c0a8e72,
0x000acd25]);
pub static B: Scalar29 = Scalar29([
0x15421564, 0x1e69fd72, 0x093d9692, 0x161785be, 0x1587d69f, 0x09d9dada, 0x130246c0,
0x0c0a8e72, 0x000acd25,
]);
/// a+b = 0
/// a-b = 4702830963113076907131374482398799845891318823599740229925345317690316127506
pub static AB: Scalar29 = Scalar29(
[0x0f677d12, 0x045236c0, 0x09533e06, 0x0fb86f0f,
0x14f0555c, 0x0c4c4a4a, 0x19fb727f, 0x07eae31a,
0x000a65b5]);
pub static AB: Scalar29 = Scalar29([
0x0f677d12, 0x045236c0, 0x09533e06, 0x0fb86f0f, 0x14f0555c, 0x0c4c4a4a, 0x19fb727f,
0x07eae31a, 0x000a65b5,
]);
// c = (2^512 - 1) % l = 1627715501170711445284395025044413883736156588369414752970002579683115011840
pub static C: Scalar29 = Scalar29(
[0x049c0f00, 0x00308f1a, 0x0164d1e9, 0x1c374ed1,
0x1be65d00, 0x19e90bfa, 0x08f73bb1, 0x036f8613,
0x00039941]);
pub static C: Scalar29 = Scalar29([
0x049c0f00, 0x00308f1a, 0x0164d1e9, 0x1c374ed1, 0x1be65d00, 0x19e90bfa, 0x08f73bb1,
0x036f8613, 0x00039941,
]);
#[test]
fn mul_max() {
@ -504,7 +521,7 @@ mod test {
#[test]
fn add() {
let res = Scalar29::add(&A, &B);
let zero = Scalar29::zero();
let zero = Scalar29::ZERO;
for i in 0..9 {
assert!(res[i] == zero[i]);
}

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ use core::ops::{Sub, SubAssign};
use subtle::Choice;
use subtle::ConditionallySelectable;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// A `FieldElement51` represents an element of the field
@ -39,14 +40,15 @@ use zeroize::Zeroize;
/// The backend-specific type `FieldElement51` should not be used
/// outside of the `curve25519_dalek::field` module.
#[derive(Copy, Clone)]
pub struct FieldElement51(pub (crate) [u64; 5]);
pub struct FieldElement51(pub(crate) [u64; 5]);
impl Debug for FieldElement51 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "FieldElement51({:?})", &self.0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for FieldElement51 {
fn zeroize(&mut self) {
self.0.zeroize();
@ -108,6 +110,8 @@ impl<'b> MulAssign<&'b FieldElement51> for FieldElement51 {
impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
type Output = FieldElement51;
#[rustfmt::skip] // keep alignment of c* calculations
fn mul(self, _rhs: &'b FieldElement51) -> FieldElement51 {
/// Helper function to multiply two 64-bit integers with 128
/// bits of output.
@ -137,11 +141,11 @@ impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
let b4_19 = b[4] * 19;
// Multiply to get 128-bit coefficients of output
let c0: u128 = m(a[0],b[0]) + m(a[4],b1_19) + m(a[3],b2_19) + m(a[2],b3_19) + m(a[1],b4_19);
let mut c1: u128 = m(a[1],b[0]) + m(a[0],b[1]) + m(a[4],b2_19) + m(a[3],b3_19) + m(a[2],b4_19);
let mut c2: u128 = m(a[2],b[0]) + m(a[1],b[1]) + m(a[0],b[2]) + m(a[4],b3_19) + m(a[3],b4_19);
let mut c3: u128 = m(a[3],b[0]) + m(a[2],b[1]) + m(a[1],b[2]) + m(a[0],b[3]) + m(a[4],b4_19);
let mut c4: u128 = m(a[4],b[0]) + m(a[3],b[1]) + m(a[2],b[2]) + m(a[1],b[3]) + m(a[0],b[4]);
let c0: u128 = m(a[0], b[0]) + m(a[4], b1_19) + m(a[3], b2_19) + m(a[2], b3_19) + m(a[1], b4_19);
let mut c1: u128 = m(a[1], b[0]) + m(a[0], b[1]) + m(a[4], b2_19) + m(a[3], b3_19) + m(a[2], b4_19);
let mut c2: u128 = m(a[2], b[0]) + m(a[1], b[1]) + m(a[0], b[2]) + m(a[4], b3_19) + m(a[3], b4_19);
let mut c3: u128 = m(a[3], b[0]) + m(a[2], b[1]) + m(a[1], b[2]) + m(a[0], b[3]) + m(a[4], b4_19);
let mut c4: u128 = m(a[4], b[0]) + m(a[3], b[1]) + m(a[2], b[2]) + m(a[1], b[3]) + m(a[0] , b[4]);
// How big are the c[i]? We have
//
@ -198,7 +202,7 @@ impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
// out[0] + carry * 19 < 2^51 + 19 * 2^59.33 < 2^63.58
//
// and there is no overflow.
out[0] = out[0] + carry * 19;
out[0] += carry * 19;
// Now out[1] < 2^51 + 2^(64 -51) = 2^51 + 2^13 < 2^(51 + epsilon).
out[1] += out[0] >> 51;
@ -251,6 +255,23 @@ impl ConditionallySelectable for FieldElement51 {
}
impl FieldElement51 {
pub(crate) const fn from_limbs(limbs: [u64; 5]) -> FieldElement51 {
FieldElement51(limbs)
}
/// The scalar \\( 0 \\).
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
/// The scalar \\( 1 \\).
pub const ONE: FieldElement51 = FieldElement51::from_limbs([1, 0, 0, 0, 0]);
/// The scalar \\( -1 \\).
pub const MINUS_ONE: FieldElement51 = FieldElement51::from_limbs([
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
]);
/// Invert the sign of this field element
pub fn negate(&mut self) {
// See commentary in the Sub impl
@ -264,21 +285,6 @@ impl FieldElement51 {
self.0 = neg.0;
}
/// Construct zero.
pub fn zero() -> FieldElement51 {
FieldElement51([ 0, 0, 0, 0, 0 ])
}
/// Construct one.
pub fn one() -> FieldElement51 {
FieldElement51([ 1, 0, 0, 0, 0 ])
}
/// Construct -1.
pub fn minus_one() -> FieldElement51 {
FieldElement51([2251799813685228, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247])
}
/// Given 64-bit input limbs, reduce to enforce the bound 2^(51 + epsilon).
#[inline(always)]
fn reduce(mut limbs: [u64; 5]) -> FieldElement51 {
@ -328,6 +334,7 @@ impl FieldElement51 {
/// the canonical encoding, and check that the input was
/// canonical.
///
#[rustfmt::skip] // keep alignment of bit shifts
pub fn from_bytes(bytes: &[u8; 32]) -> FieldElement51 {
let load8 = |input: &[u8]| -> u64 {
(input[0] as u64)
@ -357,7 +364,8 @@ impl FieldElement51 {
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn to_bytes(&self) -> [u8; 32] {
#[rustfmt::skip] // keep alignment of s[*] calculations
pub fn as_bytes(&self) -> [u8; 32] {
// Let h = limbs[0] + limbs[1]*2^51 + ... + limbs[4]*2^204.
//
// Write h = pq + r with 0 <= r < p.
@ -384,56 +392,56 @@ impl FieldElement51 {
// Now we can compute r as r = h - pq = r - (2^255-19)q = r + 19q - 2^255q
limbs[0] += 19*q;
limbs[0] += 19 * q;
// Now carry the result to compute r + 19q ...
let low_51_bit_mask = (1u64 << 51) - 1;
limbs[1] += limbs[0] >> 51;
limbs[0] = limbs[0] & low_51_bit_mask;
limbs[2] += limbs[1] >> 51;
limbs[1] = limbs[1] & low_51_bit_mask;
limbs[3] += limbs[2] >> 51;
limbs[2] = limbs[2] & low_51_bit_mask;
limbs[4] += limbs[3] >> 51;
limbs[3] = limbs[3] & low_51_bit_mask;
limbs[1] += limbs[0] >> 51;
limbs[0] &= low_51_bit_mask;
limbs[2] += limbs[1] >> 51;
limbs[1] &= low_51_bit_mask;
limbs[3] += limbs[2] >> 51;
limbs[2] &= low_51_bit_mask;
limbs[4] += limbs[3] >> 51;
limbs[3] &= low_51_bit_mask;
// ... but instead of carrying (limbs[4] >> 51) = 2^255q
// into another limb, discard it, subtracting the value
limbs[4] = limbs[4] & low_51_bit_mask;
limbs[4] &= low_51_bit_mask;
// Now arrange the bits of the limbs.
let mut s = [0u8;32];
s[ 0] = limbs[0] as u8;
s[ 1] = (limbs[0] >> 8) as u8;
s[ 2] = (limbs[0] >> 16) as u8;
s[ 3] = (limbs[0] >> 24) as u8;
s[ 4] = (limbs[0] >> 32) as u8;
s[ 5] = (limbs[0] >> 40) as u8;
s[ 0] = limbs[0] as u8;
s[ 1] = (limbs[0] >> 8) as u8;
s[ 2] = (limbs[0] >> 16) as u8;
s[ 3] = (limbs[0] >> 24) as u8;
s[ 4] = (limbs[0] >> 32) as u8;
s[ 5] = (limbs[0] >> 40) as u8;
s[ 6] = ((limbs[0] >> 48) | (limbs[1] << 3)) as u8;
s[ 7] = (limbs[1] >> 5) as u8;
s[ 8] = (limbs[1] >> 13) as u8;
s[ 9] = (limbs[1] >> 21) as u8;
s[10] = (limbs[1] >> 29) as u8;
s[11] = (limbs[1] >> 37) as u8;
s[ 7] = (limbs[1] >> 5) as u8;
s[ 8] = (limbs[1] >> 13) as u8;
s[ 9] = (limbs[1] >> 21) as u8;
s[10] = (limbs[1] >> 29) as u8;
s[11] = (limbs[1] >> 37) as u8;
s[12] = ((limbs[1] >> 45) | (limbs[2] << 6)) as u8;
s[13] = (limbs[2] >> 2) as u8;
s[14] = (limbs[2] >> 10) as u8;
s[15] = (limbs[2] >> 18) as u8;
s[16] = (limbs[2] >> 26) as u8;
s[17] = (limbs[2] >> 34) as u8;
s[18] = (limbs[2] >> 42) as u8;
s[13] = (limbs[2] >> 2) as u8;
s[14] = (limbs[2] >> 10) as u8;
s[15] = (limbs[2] >> 18) as u8;
s[16] = (limbs[2] >> 26) as u8;
s[17] = (limbs[2] >> 34) as u8;
s[18] = (limbs[2] >> 42) as u8;
s[19] = ((limbs[2] >> 50) | (limbs[3] << 1)) as u8;
s[20] = (limbs[3] >> 7) as u8;
s[21] = (limbs[3] >> 15) as u8;
s[22] = (limbs[3] >> 23) as u8;
s[23] = (limbs[3] >> 31) as u8;
s[24] = (limbs[3] >> 39) as u8;
s[20] = (limbs[3] >> 7) as u8;
s[21] = (limbs[3] >> 15) as u8;
s[22] = (limbs[3] >> 23) as u8;
s[23] = (limbs[3] >> 31) as u8;
s[24] = (limbs[3] >> 39) as u8;
s[25] = ((limbs[3] >> 47) | (limbs[4] << 4)) as u8;
s[26] = (limbs[4] >> 4) as u8;
s[27] = (limbs[4] >> 12) as u8;
s[28] = (limbs[4] >> 20) as u8;
s[29] = (limbs[4] >> 28) as u8;
s[30] = (limbs[4] >> 36) as u8;
s[31] = (limbs[4] >> 44) as u8;
s[26] = (limbs[4] >> 4) as u8;
s[27] = (limbs[4] >> 12) as u8;
s[28] = (limbs[4] >> 20) as u8;
s[29] = (limbs[4] >> 28) as u8;
s[30] = (limbs[4] >> 36) as u8;
s[31] = (limbs[4] >> 44) as u8;
// High bit should be zero.
debug_assert!((s[31] & 0b1000_0000u8) == 0u8);
@ -442,13 +450,16 @@ impl FieldElement51 {
}
/// Given `k > 0`, return `self^(2^k)`.
#[rustfmt::skip] // keep alignment of c* calculations
pub fn pow2k(&self, mut k: u32) -> FieldElement51 {
debug_assert!( k > 0 );
/// Multiply two 64-bit integers with 128 bits of output.
#[inline(always)]
fn m(x: u64, y: u64) -> u128 { (x as u128) * (y as u128) }
fn m(x: u64, y: u64) -> u128 {
(x as u128) * (y as u128)
}
let mut a: [u64; 5] = self.0;
@ -530,7 +541,7 @@ impl FieldElement51 {
// a[0] + carry * 19 < 2^51 + 19 * 2^59.33 < 2^63.58
//
// and there is no overflow.
a[0] = a[0] + carry * 19;
a[0] += carry * 19;
// Now a[1] < 2^51 + 2^(64 -51) = 2^51 + 2^13 < 2^(51 + epsilon).
a[1] += a[0] >> 51;
@ -538,7 +549,7 @@ impl FieldElement51 {
// Now all a[i] < 2^(51 + epsilon) and a = self^(2^k).
k = k - 1;
k -= 1;
if k == 0 {
break;
}

View File

@ -14,21 +14,23 @@
use core::fmt::Debug;
use core::ops::{Index, IndexMut};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use constants;
use crate::constants;
/// The `Scalar52` struct represents an element in
/// \\(\mathbb Z / \ell \mathbb Z\\) as 5 \\(52\\)-bit limbs.
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Scalar52(pub [u64; 5]);
impl Debug for Scalar52 {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Scalar52: {:?}", &self.0[..])
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for Scalar52 {
fn zeroize(&mut self) {
self.0.zeroize();
@ -55,12 +57,11 @@ fn m(x: u64, y: u64) -> u128 {
}
impl Scalar52 {
/// Return the zero scalar
pub fn zero() -> Scalar52 {
Scalar52([0,0,0,0,0])
}
/// The scalar \\( 0 \\).
pub const ZERO: Scalar52 = Scalar52([0, 0, 0, 0, 0]);
/// Unpack a 32 byte / 256 bit scalar into 5 52-bit limbs.
#[rustfmt::skip] // keep alignment of s[*] calculations
pub fn from_bytes(bytes: &[u8; 32]) -> Scalar52 {
let mut words = [0u64; 4];
for i in 0..4 {
@ -71,18 +72,19 @@ impl Scalar52 {
let mask = (1u64 << 52) - 1;
let top_mask = (1u64 << 48) - 1;
let mut s = Scalar52::zero();
let mut s = Scalar52::ZERO;
s[ 0] = words[0] & mask;
s[ 1] = ((words[0] >> 52) | (words[1] << 12)) & mask;
s[ 2] = ((words[1] >> 40) | (words[2] << 24)) & mask;
s[ 3] = ((words[2] >> 28) | (words[3] << 36)) & mask;
s[ 4] = (words[3] >> 16) & top_mask;
s[0] = words[0] & mask;
s[1] = ((words[0] >> 52) | (words[1] << 12)) & mask;
s[2] = ((words[1] >> 40) | (words[2] << 24)) & mask;
s[3] = ((words[2] >> 28) | (words[3] << 36)) & mask;
s[4] = (words[3] >> 16) & top_mask;
s
}
/// Reduce a 64 byte / 512 bit scalar mod l
#[rustfmt::skip] // keep alignment of lo[*] and hi[*] calculations
pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar52 {
let mut words = [0u64; 8];
for i in 0..8 {
@ -92,19 +94,19 @@ impl Scalar52 {
}
let mask = (1u64 << 52) - 1;
let mut lo = Scalar52::zero();
let mut hi = Scalar52::zero();
let mut lo = Scalar52::ZERO;
let mut hi = Scalar52::ZERO;
lo[0] = words[ 0] & mask;
lo[1] = ((words[ 0] >> 52) | (words[ 1] << 12)) & mask;
lo[2] = ((words[ 1] >> 40) | (words[ 2] << 24)) & mask;
lo[3] = ((words[ 2] >> 28) | (words[ 3] << 36)) & mask;
lo[4] = ((words[ 3] >> 16) | (words[ 4] << 48)) & mask;
hi[0] = (words[ 4] >> 4) & mask;
hi[1] = ((words[ 4] >> 56) | (words[ 5] << 8)) & mask;
hi[2] = ((words[ 5] >> 44) | (words[ 6] << 20)) & mask;
hi[3] = ((words[ 6] >> 32) | (words[ 7] << 32)) & mask;
hi[4] = words[ 7] >> 20 ;
lo[0] = words[0] & mask;
lo[1] = ((words[0] >> 52) | (words[ 1] << 12)) & mask;
lo[2] = ((words[1] >> 40) | (words[ 2] << 24)) & mask;
lo[3] = ((words[2] >> 28) | (words[ 3] << 36)) & mask;
lo[4] = ((words[3] >> 16) | (words[ 4] << 48)) & mask;
hi[0] = (words[4] >> 4) & mask;
hi[1] = ((words[4] >> 56) | (words[ 5] << 8)) & mask;
hi[2] = ((words[5] >> 44) | (words[ 6] << 20)) & mask;
hi[3] = ((words[6] >> 32) | (words[ 7] << 32)) & mask;
hi[4] = words[7] >> 20 ;
lo = Scalar52::montgomery_mul(&lo, &constants::R); // (lo * R) / R = lo
hi = Scalar52::montgomery_mul(&hi, &constants::RR); // (hi * R^2) / R = hi * R
@ -113,19 +115,21 @@ impl Scalar52 {
}
/// Pack the limbs of this `Scalar52` into 32 bytes
pub fn to_bytes(&self) -> [u8; 32] {
#[rustfmt::skip] // keep alignment of s[*] calculations
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
let mut s = [0u8; 32];
s[0] = (self.0[ 0] >> 0) as u8;
s[1] = (self.0[ 0] >> 8) as u8;
s[2] = (self.0[ 0] >> 16) as u8;
s[3] = (self.0[ 0] >> 24) as u8;
s[4] = (self.0[ 0] >> 32) as u8;
s[5] = (self.0[ 0] >> 40) as u8;
s[6] = ((self.0[ 0] >> 48) | (self.0[ 1] << 4)) as u8;
s[7] = (self.0[ 1] >> 4) as u8;
s[8] = (self.0[ 1] >> 12) as u8;
s[9] = (self.0[ 1] >> 20) as u8;
s[ 0] = (self.0[ 0] >> 0) as u8;
s[ 1] = (self.0[ 0] >> 8) as u8;
s[ 2] = (self.0[ 0] >> 16) as u8;
s[ 3] = (self.0[ 0] >> 24) as u8;
s[ 4] = (self.0[ 0] >> 32) as u8;
s[ 5] = (self.0[ 0] >> 40) as u8;
s[ 6] = ((self.0[ 0] >> 48) | (self.0[ 1] << 4)) as u8;
s[ 7] = (self.0[ 1] >> 4) as u8;
s[ 8] = (self.0[ 1] >> 12) as u8;
s[ 9] = (self.0[ 1] >> 20) as u8;
s[10] = (self.0[ 1] >> 28) as u8;
s[11] = (self.0[ 1] >> 36) as u8;
s[12] = (self.0[ 1] >> 44) as u8;
@ -154,7 +158,7 @@ impl Scalar52 {
/// Compute `a + b` (mod l)
pub fn add(a: &Scalar52, b: &Scalar52) -> Scalar52 {
let mut sum = Scalar52::zero();
let mut sum = Scalar52::ZERO;
let mask = (1u64 << 52) - 1;
// a + b
@ -170,7 +174,15 @@ impl Scalar52 {
/// Compute `a - b` (mod l)
pub fn sub(a: &Scalar52, b: &Scalar52) -> Scalar52 {
let mut difference = Scalar52::zero();
// Optimization barrier to prevent compiler from inserting branch instructions
// TODO(tarcieri): find a better home (or abstraction) for this
fn black_box(value: u64) -> u64 {
// SAFETY: `u64` is a simple integer `Copy` type and `value` lives on the stack so
// a pointer to it will be valid.
unsafe { core::ptr::read_volatile(&value) }
}
let mut difference = Scalar52::ZERO;
let mask = (1u64 << 52) - 1;
// a - b
@ -184,7 +196,9 @@ impl Scalar52 {
let underflow_mask = ((borrow >> 63) ^ 1).wrapping_sub(1);
let mut carry: u64 = 0;
for i in 0..5 {
carry = (carry >> 52) + difference[i] + (constants::L[i] & underflow_mask);
// SECURITY: `black_box` prevents LLVM from inserting a `jns` conditional on x86(_64)
// which can be used to bypass this section when `underflow_mask` is zero.
carry = (carry >> 52) + difference[i] + (constants::L[i] & black_box(underflow_mask));
difference[i] = carry & mask;
}
@ -193,53 +207,56 @@ impl Scalar52 {
/// Compute `a * b`
#[inline(always)]
#[rustfmt::skip] // keep alignment of z[*] calculations
pub (crate) fn mul_internal(a: &Scalar52, b: &Scalar52) -> [u128; 9] {
let mut z = [0u128; 9];
z[0] = m(a[0],b[0]);
z[1] = m(a[0],b[1]) + m(a[1],b[0]);
z[2] = m(a[0],b[2]) + m(a[1],b[1]) + m(a[2],b[0]);
z[3] = m(a[0],b[3]) + m(a[1],b[2]) + m(a[2],b[1]) + m(a[3],b[0]);
z[4] = m(a[0],b[4]) + m(a[1],b[3]) + m(a[2],b[2]) + m(a[3],b[1]) + m(a[4],b[0]);
z[5] = m(a[1],b[4]) + m(a[2],b[3]) + m(a[3],b[2]) + m(a[4],b[1]);
z[6] = m(a[2],b[4]) + m(a[3],b[3]) + m(a[4],b[2]);
z[7] = m(a[3],b[4]) + m(a[4],b[3]);
z[8] = m(a[4],b[4]);
z[0] = m(a[0], b[0]);
z[1] = m(a[0], b[1]) + m(a[1], b[0]);
z[2] = m(a[0], b[2]) + m(a[1], b[1]) + m(a[2], b[0]);
z[3] = m(a[0], b[3]) + m(a[1], b[2]) + m(a[2], b[1]) + m(a[3], b[0]);
z[4] = m(a[0], b[4]) + m(a[1], b[3]) + m(a[2], b[2]) + m(a[3], b[1]) + m(a[4], b[0]);
z[5] = m(a[1], b[4]) + m(a[2], b[3]) + m(a[3], b[2]) + m(a[4], b[1]);
z[6] = m(a[2], b[4]) + m(a[3], b[3]) + m(a[4], b[2]);
z[7] = m(a[3], b[4]) + m(a[4], b[3]);
z[8] = m(a[4], b[4]);
z
}
/// Compute `a^2`
#[inline(always)]
#[rustfmt::skip] // keep alignment of return calculations
fn square_internal(a: &Scalar52) -> [u128; 9] {
let aa = [
a[0]*2,
a[1]*2,
a[2]*2,
a[3]*2,
a[0] * 2,
a[1] * 2,
a[2] * 2,
a[3] * 2,
];
[
m( a[0],a[0]),
m(aa[0],a[1]),
m(aa[0],a[2]) + m( a[1],a[1]),
m(aa[0],a[3]) + m(aa[1],a[2]),
m(aa[0],a[4]) + m(aa[1],a[3]) + m( a[2],a[2]),
m(aa[1],a[4]) + m(aa[2],a[3]),
m(aa[2],a[4]) + m( a[3],a[3]),
m(aa[3],a[4]),
m(a[4],a[4])
m( a[0], a[0]),
m(aa[0], a[1]),
m(aa[0], a[2]) + m( a[1], a[1]),
m(aa[0], a[3]) + m(aa[1], a[2]),
m(aa[0], a[4]) + m(aa[1], a[3]) + m( a[2], a[2]),
m(aa[1], a[4]) + m(aa[2], a[3]),
m(aa[2], a[4]) + m( a[3], a[3]),
m(aa[3], a[4]),
m(a[4], a[4])
]
}
/// Compute `limbs/R` (mod l), where R is the Montgomery modulus 2^260
#[inline(always)]
#[rustfmt::skip] // keep alignment of n* and r* calculations
pub (crate) fn montgomery_reduce(limbs: &[u128; 9]) -> Scalar52 {
#[inline(always)]
fn part1(sum: u128) -> (u128, u64) {
let p = (sum as u64).wrapping_mul(constants::LFACTOR) & ((1u64 << 52) - 1);
((sum + m(p,constants::L[0])) >> 52, p)
((sum + m(p, constants::L[0])) >> 52, p)
}
#[inline(always)]
@ -253,20 +270,20 @@ impl Scalar52 {
// the first half computes the Montgomery adjustment factor n, and begins adding n*l to make limbs divisible by R
let (carry, n0) = part1( limbs[0]);
let (carry, n1) = part1(carry + limbs[1] + m(n0,l[1]));
let (carry, n2) = part1(carry + limbs[2] + m(n0,l[2]) + m(n1,l[1]));
let (carry, n3) = part1(carry + limbs[3] + m(n1,l[2]) + m(n2,l[1]));
let (carry, n4) = part1(carry + limbs[4] + m(n0,l[4]) + m(n2,l[2]) + m(n3,l[1]));
let (carry, n1) = part1(carry + limbs[1] + m(n0, l[1]));
let (carry, n2) = part1(carry + limbs[2] + m(n0, l[2]) + m(n1, l[1]));
let (carry, n3) = part1(carry + limbs[3] + m(n1, l[2]) + m(n2, l[1]));
let (carry, n4) = part1(carry + limbs[4] + m(n0, l[4]) + m(n2, l[2]) + m(n3, l[1]));
// limbs is divisible by R now, so we can divide by R by simply storing the upper half as the result
let (carry, r0) = part2(carry + limbs[5] + m(n1,l[4]) + m(n3,l[2]) + m(n4,l[1]));
let (carry, r1) = part2(carry + limbs[6] + m(n2,l[4]) + m(n4,l[2]));
let (carry, r2) = part2(carry + limbs[7] + m(n3,l[4]) );
let (carry, r3) = part2(carry + limbs[8] + m(n4,l[4]));
let (carry, r0) = part2(carry + limbs[5] + m(n1, l[4]) + m(n3, l[2]) + m(n4, l[1]));
let (carry, r1) = part2(carry + limbs[6] + m(n2,l[4]) + m(n4, l[2]));
let (carry, r2) = part2(carry + limbs[7] + m(n3, l[4]) );
let (carry, r3) = part2(carry + limbs[8] + m(n4, l[4]));
let r4 = carry as u64;
// result may be >= l, so attempt to subtract l
Scalar52::sub(&Scalar52([r0,r1,r2,r3,r4]), l)
Scalar52::sub(&Scalar52([r0, r1, r2, r3, r4]), l)
}
/// Compute `a * b` (mod l)
@ -298,11 +315,12 @@ impl Scalar52 {
/// Puts a Scalar52 in to Montgomery form, i.e. computes `a*R (mod l)`
#[inline(never)]
pub fn to_montgomery(&self) -> Scalar52 {
pub fn as_montgomery(&self) -> Scalar52 {
Scalar52::montgomery_mul(self, &constants::RR)
}
/// Takes a Scalar52 out of Montgomery form, i.e. computes `a/R (mod l)`
#[allow(clippy::wrong_self_convention)]
#[inline(never)]
pub fn from_montgomery(&self) -> Scalar52 {
let mut limbs = [0u128; 9];
@ -313,7 +331,6 @@ impl Scalar52 {
}
}
#[cfg(test)]
mod test {
use super::*;
@ -324,55 +341,95 @@ mod test {
/// x = 14474011154664524427946373126085988481658748083205070504932198000989141204991
/// x = 7237005577332262213973186563042994240801631723825162898930247062703686954002 mod l
/// x = 3057150787695215392275360544382990118917283750546154083604586903220563173085*R mod l in Montgomery form
pub static X: Scalar52 = Scalar52(
[0x000fffffffffffff, 0x000fffffffffffff, 0x000fffffffffffff, 0x000fffffffffffff,
0x00001fffffffffff]);
pub static X: Scalar52 = Scalar52([
0x000fffffffffffff,
0x000fffffffffffff,
0x000fffffffffffff,
0x000fffffffffffff,
0x00001fffffffffff,
]);
/// x^2 = 3078544782642840487852506753550082162405942681916160040940637093560259278169 mod l
pub static XX: Scalar52 = Scalar52(
[0x0001668020217559, 0x000531640ffd0ec0, 0x00085fd6f9f38a31, 0x000c268f73bb1cf4,
0x000006ce65046df0]);
pub static XX: Scalar52 = Scalar52([
0x0001668020217559,
0x000531640ffd0ec0,
0x00085fd6f9f38a31,
0x000c268f73bb1cf4,
0x000006ce65046df0,
]);
/// x^2 = 4413052134910308800482070043710297189082115023966588301924965890668401540959*R mod l in Montgomery form
pub static XX_MONT: Scalar52 = Scalar52(
[0x000c754eea569a5c, 0x00063b6ed36cb215, 0x0008ffa36bf25886, 0x000e9183614e7543,
0x0000061db6c6f26f]);
pub static XX_MONT: Scalar52 = Scalar52([
0x000c754eea569a5c,
0x00063b6ed36cb215,
0x0008ffa36bf25886,
0x000e9183614e7543,
0x0000061db6c6f26f,
]);
/// y = 6145104759870991071742105800796537629880401874866217824609283457819451087098
pub static Y: Scalar52 = Scalar52(
[0x000b75071e1458fa, 0x000bf9d75e1ecdac, 0x000433d2baf0672b, 0x0005fffcc11fad13,
0x00000d96018bb825]);
pub static Y: Scalar52 = Scalar52([
0x000b75071e1458fa,
0x000bf9d75e1ecdac,
0x000433d2baf0672b,
0x0005fffcc11fad13,
0x00000d96018bb825,
]);
/// x*y = 36752150652102274958925982391442301741 mod l
pub static XY: Scalar52 = Scalar52(
[0x000ee6d76ba7632d, 0x000ed50d71d84e02, 0x00000000001ba634, 0x0000000000000000,
0x0000000000000000]);
pub static XY: Scalar52 = Scalar52([
0x000ee6d76ba7632d,
0x000ed50d71d84e02,
0x00000000001ba634,
0x0000000000000000,
0x0000000000000000,
]);
/// x*y = 658448296334113745583381664921721413881518248721417041768778176391714104386*R mod l in Montgomery form
pub static XY_MONT: Scalar52 = Scalar52(
[0x0006d52bf200cfd5, 0x00033fb1d7021570, 0x000f201bc07139d8, 0x0001267e3e49169e,
0x000007b839c00268]);
pub static XY_MONT: Scalar52 = Scalar52([
0x0006d52bf200cfd5,
0x00033fb1d7021570,
0x000f201bc07139d8,
0x0001267e3e49169e,
0x000007b839c00268,
]);
/// a = 2351415481556538453565687241199399922945659411799870114962672658845158063753
pub static A: Scalar52 = Scalar52(
[0x0005236c07b3be89, 0x0001bc3d2a67c0c4, 0x000a4aa782aae3ee, 0x0006b3f6e4fec4c4,
0x00000532da9fab8c]);
pub static A: Scalar52 = Scalar52([
0x0005236c07b3be89,
0x0001bc3d2a67c0c4,
0x000a4aa782aae3ee,
0x0006b3f6e4fec4c4,
0x00000532da9fab8c,
]);
/// b = 4885590095775723760407499321843594317911456947580037491039278279440296187236
pub static B: Scalar52 = Scalar52(
[0x000d3fae55421564, 0x000c2df24f65a4bc, 0x0005b5587d69fb0b, 0x00094c091b013b3b,
0x00000acd25605473]);
pub static B: Scalar52 = Scalar52([
0x000d3fae55421564,
0x000c2df24f65a4bc,
0x0005b5587d69fb0b,
0x00094c091b013b3b,
0x00000acd25605473,
]);
/// a+b = 0
/// a-b = 4702830963113076907131374482398799845891318823599740229925345317690316127506
pub static AB: Scalar52 = Scalar52(
[0x000a46d80f677d12, 0x0003787a54cf8188, 0x0004954f0555c7dc, 0x000d67edc9fd8989,
0x00000a65b53f5718]);
pub static AB: Scalar52 = Scalar52([
0x000a46d80f677d12,
0x0003787a54cf8188,
0x0004954f0555c7dc,
0x000d67edc9fd8989,
0x00000a65b53f5718,
]);
// c = (2^512 - 1) % l = 1627715501170711445284395025044413883736156588369414752970002579683115011840
pub static C: Scalar52 = Scalar52(
[0x000611e3449c0f00, 0x000a768859347a40, 0x0007f5be65d00e1b, 0x0009a3dceec73d21,
0x00000399411b7c30]);
pub static C: Scalar52 = Scalar52([
0x000611e3449c0f00,
0x000a768859347a40,
0x0007f5be65d00e1b,
0x0009a3dceec73d21,
0x00000399411b7c30,
]);
#[test]
fn mul_max() {
@ -425,7 +482,7 @@ mod test {
#[test]
fn add() {
let res = Scalar52::add(&A, &B);
let zero = Scalar52::zero();
let zero = Scalar52::ZERO;
for i in 0..5 {
assert!(res[i] == zero[i]);
}

File diff suppressed because it is too large Load Diff

View File

@ -35,16 +35,20 @@
#![allow(non_snake_case)]
use core::convert::From;
use core::ops::{Add, Neg, Sub};
use subtle::Choice;
use subtle::ConditionallySelectable;
use edwards;
use window::{LookupTable, NafLookupTable5, NafLookupTable8};
use curve25519_dalek_derive::unsafe_target_feature;
use traits::Identity;
use crate::edwards;
use crate::window::{LookupTable, NafLookupTable5};
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
use crate::window::NafLookupTable8;
use crate::traits::Identity;
use super::constants;
use super::field::{FieldElement2625x4, Lanes, Shuffle};
@ -59,12 +63,14 @@ use super::field::{FieldElement2625x4, Lanes, Shuffle};
#[derive(Copy, Clone, Debug)]
pub struct ExtendedPoint(pub(super) FieldElement2625x4);
#[unsafe_target_feature("avx2")]
impl From<edwards::EdwardsPoint> for ExtendedPoint {
fn from(P: edwards::EdwardsPoint) -> ExtendedPoint {
ExtendedPoint(FieldElement2625x4::new(&P.X, &P.Y, &P.Z, &P.T))
}
}
#[unsafe_target_feature("avx2")]
impl From<ExtendedPoint> for edwards::EdwardsPoint {
fn from(P: ExtendedPoint) -> edwards::EdwardsPoint {
let tmp = P.0.split();
@ -77,6 +83,7 @@ impl From<ExtendedPoint> for edwards::EdwardsPoint {
}
}
#[unsafe_target_feature("avx2")]
impl ConditionallySelectable for ExtendedPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ExtendedPoint(FieldElement2625x4::conditional_select(&a.0, &b.0, choice))
@ -87,18 +94,21 @@ impl ConditionallySelectable for ExtendedPoint {
}
}
#[unsafe_target_feature("avx2")]
impl Default for ExtendedPoint {
fn default() -> ExtendedPoint {
ExtendedPoint::identity()
}
}
#[unsafe_target_feature("avx2")]
impl Identity for ExtendedPoint {
fn identity() -> ExtendedPoint {
constants::EXTENDEDPOINT_IDENTITY
}
}
#[unsafe_target_feature("avx2")]
impl ExtendedPoint {
/// Compute the double of this point.
pub fn double(&self) -> ExtendedPoint {
@ -134,7 +144,7 @@ impl ExtendedPoint {
// =======================
// S5 S6 S8 S9
let zero = FieldElement2625x4::zero();
let zero = FieldElement2625x4::ZERO;
let S_1 = tmp1.shuffle(Shuffle::AAAA);
let S_2 = tmp1.shuffle(Shuffle::BBBB);
@ -184,6 +194,7 @@ impl ExtendedPoint {
#[derive(Copy, Clone, Debug)]
pub struct CachedPoint(pub(super) FieldElement2625x4);
#[unsafe_target_feature("avx2")]
impl From<ExtendedPoint> for CachedPoint {
fn from(P: ExtendedPoint) -> CachedPoint {
let mut x = P.0;
@ -202,18 +213,21 @@ impl From<ExtendedPoint> for CachedPoint {
}
}
#[unsafe_target_feature("avx2")]
impl Default for CachedPoint {
fn default() -> CachedPoint {
CachedPoint::identity()
}
}
#[unsafe_target_feature("avx2")]
impl Identity for CachedPoint {
fn identity() -> CachedPoint {
constants::CACHEDPOINT_IDENTITY
}
}
#[unsafe_target_feature("avx2")]
impl ConditionallySelectable for CachedPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
CachedPoint(FieldElement2625x4::conditional_select(&a.0, &b.0, choice))
@ -224,7 +238,8 @@ impl ConditionallySelectable for CachedPoint {
}
}
impl<'a> Neg for &'a CachedPoint {
#[unsafe_target_feature("avx2")]
impl Neg for &CachedPoint {
type Output = CachedPoint;
/// Lazily negate the point.
///
@ -238,11 +253,12 @@ impl<'a> Neg for &'a CachedPoint {
}
}
impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
#[unsafe_target_feature("avx2")]
impl Add<&CachedPoint> for &ExtendedPoint {
type Output = ExtendedPoint;
/// Add an `ExtendedPoint` and a `CachedPoint`.
fn add(self, other: &'b CachedPoint) -> ExtendedPoint {
fn add(self, other: &CachedPoint) -> ExtendedPoint {
// The coefficients of an `ExtendedPoint` are reduced after
// every operation. If the `CachedPoint` was negated, its
// coefficients grow by one bit. So on input, `self` is
@ -275,7 +291,8 @@ impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
}
}
impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
#[unsafe_target_feature("avx2")]
impl Sub<&CachedPoint> for &ExtendedPoint {
type Output = ExtendedPoint;
/// Implement subtraction by negating the point and adding.
@ -283,13 +300,14 @@ impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
/// Empirically, this seems about the same cost as a custom
/// subtraction impl (maybe because the benefit is cancelled by
/// increased code size?)
fn sub(self, other: &'b CachedPoint) -> ExtendedPoint {
fn sub(self, other: &CachedPoint) -> ExtendedPoint {
self + &(-other)
}
}
impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
#[unsafe_target_feature("avx2")]
impl From<&edwards::EdwardsPoint> for LookupTable<CachedPoint> {
fn from(point: &edwards::EdwardsPoint) -> Self {
let P = ExtendedPoint::from(*point);
let mut points = [CachedPoint::from(P); 8];
for i in 0..7 {
@ -299,8 +317,9 @@ impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
}
}
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
#[unsafe_target_feature("avx2")]
impl From<&edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
fn from(point: &edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
let mut Ai = [CachedPoint::from(A); 8];
let A2 = A.double();
@ -312,8 +331,10 @@ impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
}
}
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
#[unsafe_target_feature("avx2")]
impl From<&edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
fn from(point: &edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
let mut Ai = [CachedPoint::from(A); 64];
let A2 = A.double();
@ -325,19 +346,21 @@ impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
}
}
#[cfg(target_feature = "avx2")]
#[cfg(test)]
mod test {
use super::*;
#[rustfmt::skip] // keep alignment of some S* calculations
fn serial_add(P: edwards::EdwardsPoint, Q: edwards::EdwardsPoint) -> edwards::EdwardsPoint {
use backend::serial::u64::field::FieldElement51;
use crate::backend::serial::u64::field::FieldElement51;
let (X1, Y1, Z1, T1) = (P.X, P.Y, P.Z, P.T);
let (X2, Y2, Z2, T2) = (Q.X, Q.Y, Q.Z, Q.T);
macro_rules! print_var {
($x:ident) => {
println!("{} = {:?}", stringify!($x), $x.to_bytes());
println!("{} = {:?}", stringify!($x), $x.as_bytes());
};
}
@ -420,8 +443,8 @@ mod test {
#[test]
fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() {
use constants;
use scalar::Scalar;
use crate::constants;
use crate::scalar::Scalar;
println!("Testing id +- id");
let P = edwards::EdwardsPoint::identity();
@ -440,7 +463,7 @@ mod test {
println!("Testing B +- kB");
let P = constants::ED25519_BASEPOINT_POINT;
let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
let Q = constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
addition_test_helper(P, Q);
}
@ -449,7 +472,7 @@ mod test {
macro_rules! print_var {
($x:ident) => {
println!("{} = {:?}", stringify!($x), $x.to_bytes());
println!("{} = {:?}", stringify!($x), $x.as_bytes());
};
}
@ -507,8 +530,8 @@ mod test {
#[test]
fn vector_doubling_vs_serial_doubling_vs_edwards_extendedpoint() {
use constants;
use scalar::Scalar;
use crate::constants;
use crate::scalar::Scalar;
println!("Testing [2]id");
let P = edwards::EdwardsPoint::identity();
@ -519,16 +542,18 @@ mod test {
doubling_test_helper(P);
println!("Testing [2]([k]B)");
let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
let P = constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
doubling_test_helper(P);
}
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
#[test]
fn basepoint_odd_lookup_table_verify() {
use constants;
use backend::vector::avx2::constants::{BASEPOINT_ODD_LOOKUP_TABLE};
use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
use crate::constants;
let basepoint_odd_table = NafLookupTable8::<CachedPoint>::from(&constants::ED25519_BASEPOINT_POINT);
let basepoint_odd_table =
NafLookupTable8::<CachedPoint>::from(&constants::ED25519_BASEPOINT_POINT);
println!("basepoint_odd_lookup_table = {:?}", basepoint_odd_table);
let table_B = &BASEPOINT_ODD_LOOKUP_TABLE;

View File

@ -40,11 +40,15 @@ const C_LANES64: u8 = 0b00_11_00_00;
#[allow(unused)]
const D_LANES64: u8 = 0b11_00_00_00;
use crate::backend::vector::packed_simd::{u32x8, u64x4};
use core::ops::{Add, Mul, Neg};
use packed_simd::{i32x8, u32x8, u64x4, IntoBits};
use backend::vector::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO};
use backend::serial::u64::field::FieldElement51;
use crate::backend::serial::u64::field::FieldElement51;
use crate::backend::vector::avx2::constants::{
P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO,
};
use curve25519_dalek_derive::unsafe_target_feature;
/// Unpack 32-bit lanes into 64-bit lanes:
/// ```ascii,no_run
@ -55,16 +59,17 @@ use backend::serial::u64::field::FieldElement51;
/// (a0, 0, b0, 0, c0, 0, d0, 0)
/// (a1, 0, b1, 0, c1, 0, d1, 0)
/// ```
#[unsafe_target_feature("avx2")]
#[inline(always)]
fn unpack_pair(src: u32x8) -> (u32x8, u32x8) {
let a: u32x8;
let b: u32x8;
let zero = i32x8::new(0, 0, 0, 0, 0, 0, 0, 0);
let zero = u32x8::splat(0);
unsafe {
use core::arch::x86_64::_mm256_unpackhi_epi32;
use core::arch::x86_64::_mm256_unpacklo_epi32;
a = _mm256_unpacklo_epi32(src.into_bits(), zero.into_bits()).into_bits();
b = _mm256_unpackhi_epi32(src.into_bits(), zero.into_bits()).into_bits();
a = _mm256_unpacklo_epi32(src.into(), zero.into()).into();
b = _mm256_unpackhi_epi32(src.into(), zero.into()).into();
}
(a, b)
}
@ -78,6 +83,7 @@ fn unpack_pair(src: u32x8) -> (u32x8, u32x8) {
/// ```ascii,no_run
/// (a0, b0, a1, b1, c0, d0, c1, d1)
/// ```
#[unsafe_target_feature("avx2")]
#[inline(always)]
fn repack_pair(x: u32x8, y: u32x8) -> u32x8 {
unsafe {
@ -87,13 +93,13 @@ fn repack_pair(x: u32x8, y: u32x8) -> u32x8 {
// Input: x = (a0, 0, b0, 0, c0, 0, d0, 0)
// Input: y = (a1, 0, b1, 0, c1, 0, d1, 0)
let x_shuffled = _mm256_shuffle_epi32(x.into_bits(), 0b11_01_10_00);
let y_shuffled = _mm256_shuffle_epi32(y.into_bits(), 0b10_00_11_01);
let x_shuffled = _mm256_shuffle_epi32(x.into(), 0b11_01_10_00);
let y_shuffled = _mm256_shuffle_epi32(y.into(), 0b10_00_11_01);
// x' = (a0, b0, 0, 0, c0, d0, 0, 0)
// y' = ( 0, 0, a1, b1, 0, 0, c1, d1)
return _mm256_blend_epi32(x_shuffled, y_shuffled, 0b11001100).into_bits();
_mm256_blend_epi32(x_shuffled, y_shuffled, 0b11001100).into()
}
}
@ -103,6 +109,7 @@ fn repack_pair(x: u32x8, y: u32x8) -> u32x8 {
/// It's used to specify blend operations without
/// having to know details about the data layout of the
/// `FieldElement2625x4`.
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone, Debug)]
pub enum Lanes {
C,
@ -120,6 +127,7 @@ pub enum Lanes {
/// The enum variants are named by what they do to a vector \\(
/// (A,B,C,D) \\); for instance, `Shuffle::BADC` turns \\( (A, B, C,
/// D) \\) into \\( (B, A, D, C) \\).
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone, Debug)]
pub enum Shuffle {
AAAA,
@ -147,6 +155,7 @@ pub struct FieldElement2625x4(pub(crate) [u32x8; 5]);
use subtle::Choice;
use subtle::ConditionallySelectable;
#[unsafe_target_feature("avx2")]
impl ConditionallySelectable for FieldElement2625x4 {
fn conditional_select(
a: &FieldElement2625x4,
@ -164,11 +173,7 @@ impl ConditionallySelectable for FieldElement2625x4 {
])
}
fn conditional_assign(
&mut self,
other: &FieldElement2625x4,
choice: Choice,
) {
fn conditional_assign(&mut self, other: &FieldElement2625x4, choice: Choice) {
let mask = (-(choice.unwrap_u8() as i32)) as u32;
let mask_vec = u32x8::splat(mask);
self.0[0] ^= mask_vec & (self.0[0] ^ other.0[0]);
@ -179,20 +184,24 @@ impl ConditionallySelectable for FieldElement2625x4 {
}
}
#[unsafe_target_feature("avx2")]
impl FieldElement2625x4 {
pub const ZERO: FieldElement2625x4 = FieldElement2625x4([u32x8::splat_const::<0>(); 5]);
/// Split this vector into an array of four (serial) field
/// elements.
#[rustfmt::skip] // keep alignment of extracted lanes
pub fn split(&self) -> [FieldElement51; 4] {
let mut out = [FieldElement51::zero(); 4];
let mut out = [FieldElement51::ZERO; 4];
for i in 0..5 {
let a_2i = self.0[i].extract(0) as u64; //
let b_2i = self.0[i].extract(1) as u64; //
let a_2i_1 = self.0[i].extract(2) as u64; // `.
let b_2i_1 = self.0[i].extract(3) as u64; // | pre-swapped to avoid
let c_2i = self.0[i].extract(4) as u64; // | a cross lane shuffle
let d_2i = self.0[i].extract(5) as u64; // .'
let c_2i_1 = self.0[i].extract(6) as u64; //
let d_2i_1 = self.0[i].extract(7) as u64; //
let a_2i = self.0[i].extract::<0>() as u64; //
let b_2i = self.0[i].extract::<1>() as u64; //
let a_2i_1 = self.0[i].extract::<2>() as u64; // `.
let b_2i_1 = self.0[i].extract::<3>() as u64; // | pre-swapped to avoid
let c_2i = self.0[i].extract::<4>() as u64; // | a cross lane shuffle
let d_2i = self.0[i].extract::<5>() as u64; // .'
let c_2i_1 = self.0[i].extract::<6>() as u64; //
let d_2i_1 = self.0[i].extract::<7>() as u64; //
out[0].0[i] = a_2i + (a_2i_1 << 26);
out[1].0[i] = b_2i + (b_2i_1 << 26);
@ -230,7 +239,7 @@ impl FieldElement2625x4 {
// Note that this gets turned into a generic LLVM
// shuffle-by-constants, which can be lowered to a simpler
// instruction than a generic permute.
_mm256_permutevar8x32_epi32(x.into_bits(), c.into_bits()).into_bits()
_mm256_permutevar8x32_epi32(x.into(), c.into()).into()
}
}
@ -276,37 +285,29 @@ impl FieldElement2625x4 {
// which does not require a shuffle immediate but *is* lowered
// to immediate shuffles anyways).
match control {
Lanes::C => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), C_LANES as i32).into_bits()
}
Lanes::D => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), D_LANES as i32).into_bits()
}
Lanes::C => _mm256_blend_epi32(x.into(), y.into(), C_LANES as i32).into(),
Lanes::D => _mm256_blend_epi32(x.into(), y.into(), D_LANES as i32).into(),
Lanes::AD => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | D_LANES) as i32)
.into_bits()
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | D_LANES) as i32).into()
}
Lanes::AB => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | B_LANES) as i32)
.into_bits()
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | B_LANES) as i32).into()
}
Lanes::AC => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | C_LANES) as i32)
.into_bits()
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | C_LANES) as i32).into()
}
Lanes::CD => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (C_LANES | D_LANES) as i32)
.into_bits()
_mm256_blend_epi32(x.into(), y.into(), (C_LANES | D_LANES) as i32).into()
}
Lanes::BC => {
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (B_LANES | C_LANES) as i32)
.into_bits()
_mm256_blend_epi32(x.into(), y.into(), (B_LANES | C_LANES) as i32).into()
}
Lanes::ABCD => _mm256_blend_epi32(
x.into_bits(),
y.into_bits(),
x.into(),
y.into(),
(A_LANES | B_LANES | C_LANES | D_LANES) as i32,
).into_bits(),
)
.into(),
}
}
}
@ -320,11 +321,6 @@ impl FieldElement2625x4 {
])
}
/// Construct a vector of zeros.
pub fn zero() -> FieldElement2625x4 {
FieldElement2625x4([u32x8::splat(0); 5])
}
/// Convenience wrapper around `new(x,x,x,x)`.
pub fn splat(x: &FieldElement51) -> FieldElement2625x4 {
FieldElement2625x4::new(x, x, x, x)
@ -335,6 +331,7 @@ impl FieldElement2625x4 {
/// # Postconditions
///
/// The resulting `FieldElement2625x4` is bounded with \\( b < 0.0002 \\).
#[rustfmt::skip] // keep alignment of computed lanes
pub fn new(
x0: &FieldElement51,
x1: &FieldElement51,
@ -343,6 +340,7 @@ impl FieldElement2625x4 {
) -> FieldElement2625x4 {
let mut buf = [u32x8::splat(0); 5];
let low_26_bits = (1 << 26) - 1;
#[allow(clippy::needless_range_loop)]
for i in 0..5 {
let a_2i = (x0.0[i] & low_26_bits) as u32;
let a_2i_1 = (x0.0[i] >> 26) as u32;
@ -412,7 +410,7 @@ impl FieldElement2625x4 {
/// The coefficients of the result are bounded with \\( b < 0.0002 \\).
#[inline]
pub fn reduce(&self) -> FieldElement2625x4 {
let shifts = i32x8::new(26, 26, 25, 25, 26, 26, 25, 25);
let shifts = u32x8::new(26, 26, 25, 25, 26, 26, 25, 25);
let masks = u32x8::new(
(1 << 26) - 1,
(1 << 26) - 1,
@ -432,11 +430,11 @@ impl FieldElement2625x4 {
// The carryouts are bounded by 2^(32 - 25) = 2^7.
let rotated_carryout = |v: u32x8| -> u32x8 {
unsafe {
use core::arch::x86_64::_mm256_srlv_epi32;
use core::arch::x86_64::_mm256_shuffle_epi32;
use core::arch::x86_64::_mm256_srlv_epi32;
let c = _mm256_srlv_epi32(v.into_bits(), shifts.into_bits());
_mm256_shuffle_epi32(c, 0b01_00_11_10).into_bits()
let c = _mm256_srlv_epi32(v.into(), shifts.into());
_mm256_shuffle_epi32(c, 0b01_00_11_10).into()
}
};
@ -457,7 +455,7 @@ impl FieldElement2625x4 {
let combine = |v_lo: u32x8, v_hi: u32x8| -> u32x8 {
unsafe {
use core::arch::x86_64::_mm256_blend_epi32;
_mm256_blend_epi32(v_lo.into_bits(), v_hi.into_bits(), 0b11_00_11_00).into_bits()
_mm256_blend_epi32(v_lo.into(), v_hi.into(), 0b11_00_11_00).into()
}
};
@ -487,21 +485,21 @@ impl FieldElement2625x4 {
//
// c98 = (c(x9), c(y9), c(x8), c(y8), c(z9), c(w9), c(z8), c(w8));
// c9_spread = (c(x9), c(x8), c(y9), c(y8), c(z9), c(z8), c(w9), c(w8)).
let c9_spread = _mm256_shuffle_epi32(c98.into_bits(), 0b11_01_10_00);
let c9_spread = _mm256_shuffle_epi32(c98.into(), 0b11_01_10_00);
// Since the carryouts are bounded by 2^7, their products with 19
// are bounded by 2^11.25. This means that
//
// c9_19_spread = (19*c(x9), 0, 19*c(y9), 0, 19*c(z9), 0, 19*c(w9), 0).
let c9_19_spread = _mm256_mul_epu32(c9_spread, u64x4::splat(19).into_bits());
let c9_19_spread = _mm256_mul_epu32(c9_spread, u64x4::splat(19).into());
// Unshuffle:
// c9_19 = (19*c(x9), 19*c(y9), 0, 0, 19*c(z9), 19*c(w9), 0, 0).
_mm256_shuffle_epi32(c9_19_spread, 0b11_01_10_00).into_bits()
_mm256_shuffle_epi32(c9_19_spread, 0b11_01_10_00).into()
};
// Add the final carryin.
v[0] = v[0] + c9_19;
v[0] += c9_19;
// Each output coefficient has exactly one carryin, which is
// bounded by 2^11.25, so they are bounded as
@ -519,6 +517,7 @@ impl FieldElement2625x4 {
///
/// The coefficients of the result are bounded with \\( b < 0.007 \\).
#[inline]
#[rustfmt::skip] // keep alignment of carry chain
fn reduce64(mut z: [u64x4; 10]) -> FieldElement2625x4 {
// These aren't const because splat isn't a const fn
let LOW_25_BITS: u64x4 = u64x4::splat((1 << 25) - 1);
@ -529,12 +528,12 @@ impl FieldElement2625x4 {
debug_assert!(i < 9);
if i % 2 == 0 {
// Even limbs have 26 bits
z[i + 1] = z[i + 1] + (z[i] >> 26);
z[i] = z[i] & LOW_26_BITS;
z[i + 1] += z[i].shr::<26>();
z[i] &= LOW_26_BITS;
} else {
// Odd limbs have 25 bits
z[i + 1] = z[i + 1] + (z[i] >> 25);
z[i] = z[i] & LOW_25_BITS;
z[i + 1] += z[i].shr::<25>();
z[i] &= LOW_25_BITS;
}
};
@ -556,20 +555,17 @@ impl FieldElement2625x4 {
// big. To ensure c < 2^32, we would need z[9] < 2^57.
// Instead, we split the carry in two, with c = c_0 + c_1*2^26.
let c = z[9] >> 25;
z[9] = z[9] & LOW_25_BITS;
let c = z[9].shr::<25>();
z[9] &= LOW_25_BITS;
let mut c0: u64x4 = c & LOW_26_BITS; // c0 < 2^26;
let mut c1: u64x4 = c >> 26; // c1 < 2^(39-26) = 2^13;
let mut c1: u64x4 = c.shr::<26>(); // c1 < 2^(39-26) = 2^13;
unsafe {
use core::arch::x86_64::_mm256_mul_epu32;
let x19 = u64x4::splat(19);
c0 = _mm256_mul_epu32(c0.into_bits(), x19.into_bits()).into_bits(); // c0 < 2^30.25
c1 = _mm256_mul_epu32(c1.into_bits(), x19.into_bits()).into_bits(); // c1 < 2^17.25
}
let x19 = u64x4::splat(19);
c0 = u32x8::from(c0).mul32(u32x8::from(x19));
c1 = u32x8::from(c1).mul32(u32x8::from(x19));
z[0] = z[0] + c0; // z0 < 2^26 + 2^30.25 < 2^30.33
z[1] = z[1] + c1; // z1 < 2^25 + 2^17.25 < 2^25.0067
z[0] += c0; // z0 < 2^26 + 2^30.25 < 2^30.33
z[1] += c1; // z1 < 2^25 + 2^17.25 < 2^25.0067
carry(&mut z, 0); // z0 < 2^26, z1 < 2^25.0067 + 2^4.33 = 2^25.007
// The output coefficients are bounded with
@ -580,11 +576,11 @@ impl FieldElement2625x4 {
//
// So the packed result is bounded with b = 0.007.
FieldElement2625x4([
repack_pair(z[0].into_bits(), z[1].into_bits()),
repack_pair(z[2].into_bits(), z[3].into_bits()),
repack_pair(z[4].into_bits(), z[5].into_bits()),
repack_pair(z[6].into_bits(), z[7].into_bits()),
repack_pair(z[8].into_bits(), z[9].into_bits()),
repack_pair(z[0].into(), z[1].into()),
repack_pair(z[2].into(), z[3].into()),
repack_pair(z[4].into(), z[5].into()),
repack_pair(z[6].into(), z[7].into()),
repack_pair(z[8].into(), z[9].into()),
])
}
@ -597,17 +593,16 @@ impl FieldElement2625x4 {
/// # Postconditions
///
/// The coefficients of the result are bounded with \\( b < 0.007 \\).
#[rustfmt::skip] // keep alignment of z* calculations
pub fn square_and_negate_D(&self) -> FieldElement2625x4 {
#[inline(always)]
fn m(x: u32x8, y: u32x8) -> u64x4 {
use core::arch::x86_64::_mm256_mul_epu32;
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
x.mul32(y)
}
#[inline(always)]
fn m_lo(x: u32x8, y: u32x8) -> u32x8 {
use core::arch::x86_64::_mm256_mul_epu32;
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
x.mul32(y).into()
}
let v19 = u32x8::new(19, 0, 19, 0, 19, 0, 19, 0);
@ -618,31 +613,31 @@ impl FieldElement2625x4 {
let (x6, x7) = unpack_pair(self.0[3]);
let (x8, x9) = unpack_pair(self.0[4]);
let x0_2 = x0 << 1;
let x1_2 = x1 << 1;
let x2_2 = x2 << 1;
let x3_2 = x3 << 1;
let x4_2 = x4 << 1;
let x5_2 = x5 << 1;
let x6_2 = x6 << 1;
let x7_2 = x7 << 1;
let x0_2 = x0.shl::<1>();
let x1_2 = x1.shl::<1>();
let x2_2 = x2.shl::<1>();
let x3_2 = x3.shl::<1>();
let x4_2 = x4.shl::<1>();
let x5_2 = x5.shl::<1>();
let x6_2 = x6.shl::<1>();
let x7_2 = x7.shl::<1>();
let x5_19 = m_lo(v19, x5);
let x6_19 = m_lo(v19, x6);
let x7_19 = m_lo(v19, x7);
let x8_19 = m_lo(v19, x8);
let x9_19 = m_lo(v19, x9);
let x5_19 = m_lo(v19, x5);
let x6_19 = m_lo(v19, x6);
let x7_19 = m_lo(v19, x7);
let x8_19 = m_lo(v19, x8);
let x9_19 = m_lo(v19, x9);
let mut z0 = m(x0, x0) + m(x2_2,x8_19) + m(x4_2,x6_19) + ((m(x1_2,x9_19) + m(x3_2,x7_19) + m(x5,x5_19)) << 1);
let mut z1 = m(x0_2,x1) + m(x3_2,x8_19) + m(x5_2,x6_19) + ((m(x2,x9_19) + m(x4,x7_19)) << 1);
let mut z2 = m(x0_2,x2) + m(x1_2,x1) + m(x4_2,x8_19) + m(x6,x6_19) + ((m(x3_2,x9_19) + m(x5_2,x7_19)) << 1);
let mut z3 = m(x0_2,x3) + m(x1_2,x2) + m(x5_2,x8_19) + ((m(x4,x9_19) + m(x6,x7_19)) << 1);
let mut z4 = m(x0_2,x4) + m(x1_2,x3_2) + m(x2, x2) + m(x6_2,x8_19) + ((m(x5_2,x9_19) + m(x7,x7_19)) << 1);
let mut z5 = m(x0_2,x5) + m(x1_2,x4) + m(x2_2,x3) + m(x7_2,x8_19) + ((m(x6,x9_19)) << 1);
let mut z6 = m(x0_2,x6) + m(x1_2,x5_2) + m(x2_2,x4) + m(x3_2,x3) + m(x8,x8_19) + ((m(x7_2,x9_19)) << 1);
let mut z7 = m(x0_2,x7) + m(x1_2,x6) + m(x2_2,x5) + m(x3_2,x4) + ((m(x8,x9_19)) << 1);
let mut z8 = m(x0_2,x8) + m(x1_2,x7_2) + m(x2_2,x6) + m(x3_2,x5_2) + m(x4,x4) + ((m(x9,x9_19)) << 1);
let mut z9 = m(x0_2,x9) + m(x1_2,x8) + m(x2_2,x7) + m(x3_2,x6) + m(x4_2,x5);
let mut z0 = m(x0, x0) + m(x2_2, x8_19) + m(x4_2, x6_19) + ((m(x1_2, x9_19) + m(x3_2, x7_19) + m(x5, x5_19)).shl::<1>());
let mut z1 = m(x0_2, x1) + m(x3_2, x8_19) + m(x5_2, x6_19) + ((m(x2, x9_19) + m(x4, x7_19)).shl::<1>());
let mut z2 = m(x0_2, x2) + m(x1_2, x1) + m(x4_2, x8_19) + m(x6, x6_19) + ((m(x3_2, x9_19) + m(x5_2, x7_19)).shl::<1>());
let mut z3 = m(x0_2, x3) + m(x1_2, x2) + m(x5_2, x8_19) + ((m(x4, x9_19) + m(x6, x7_19)).shl::<1>());
let mut z4 = m(x0_2, x4) + m(x1_2, x3_2) + m(x2, x2) + m(x6_2, x8_19) + ((m(x5_2, x9_19) + m(x7, x7_19)).shl::<1>());
let mut z5 = m(x0_2, x5) + m(x1_2, x4) + m(x2_2, x3) + m(x7_2, x8_19) + ((m(x6, x9_19)).shl::<1>());
let mut z6 = m(x0_2, x6) + m(x1_2, x5_2) + m(x2_2, x4) + m(x3_2, x3) + m(x8, x8_19) + ((m(x7_2, x9_19)).shl::<1>());
let mut z7 = m(x0_2, x7) + m(x1_2, x6) + m(x2_2, x5) + m(x3_2, x4) + ((m(x8, x9_19)).shl::<1>());
let mut z8 = m(x0_2, x8) + m(x1_2, x7_2) + m(x2_2, x6) + m(x3_2, x5_2) + m(x4, x4) + ((m(x9, x9_19)).shl::<1>());
let mut z9 = m(x0_2, x9) + m(x1_2, x8) + m(x2_2, x7) + m(x3_2, x6) + m(x4_2, x5) ;
// The biggest z_i is bounded as z_i < 249*2^(51 + 2*b);
// if b < 1.5 we get z_i < 4485585228861014016.
@ -667,7 +662,7 @@ impl FieldElement2625x4 {
let negate_D = |x: u64x4, p: u64x4| -> u64x4 {
unsafe {
use core::arch::x86_64::_mm256_blend_epi32;
_mm256_blend_epi32(x.into_bits(), (p - x).into_bits(), D_LANES64 as i32).into_bits()
_mm256_blend_epi32(x.into(), (p - x).into(), D_LANES64 as i32).into()
}
};
@ -686,6 +681,7 @@ impl FieldElement2625x4 {
}
}
#[unsafe_target_feature("avx2")]
impl Neg for FieldElement2625x4 {
type Output = FieldElement2625x4;
@ -709,10 +705,12 @@ impl Neg for FieldElement2625x4 {
P_TIMES_16_HI - self.0[2],
P_TIMES_16_HI - self.0[3],
P_TIMES_16_HI - self.0[4],
]).reduce()
])
.reduce()
}
}
#[unsafe_target_feature("avx2")]
impl Add<FieldElement2625x4> for FieldElement2625x4 {
type Output = FieldElement2625x4;
/// Add two `FieldElement2625x4`s, without performing a reduction.
@ -728,6 +726,7 @@ impl Add<FieldElement2625x4> for FieldElement2625x4 {
}
}
#[unsafe_target_feature("avx2")]
impl Mul<(u32, u32, u32, u32)> for FieldElement2625x4 {
type Output = FieldElement2625x4;
/// Perform a multiplication by a vector of small constants.
@ -737,34 +736,31 @@ impl Mul<(u32, u32, u32, u32)> for FieldElement2625x4 {
/// The coefficients of the result are bounded with \\( b < 0.007 \\).
#[inline]
fn mul(self, scalars: (u32, u32, u32, u32)) -> FieldElement2625x4 {
unsafe {
use core::arch::x86_64::_mm256_mul_epu32;
let consts = u32x8::new(scalars.0, 0, scalars.1, 0, scalars.2, 0, scalars.3, 0);
let consts = u32x8::new(scalars.0, 0, scalars.1, 0, scalars.2, 0, scalars.3, 0);
let (b0, b1) = unpack_pair(self.0[0]);
let (b2, b3) = unpack_pair(self.0[1]);
let (b4, b5) = unpack_pair(self.0[2]);
let (b6, b7) = unpack_pair(self.0[3]);
let (b8, b9) = unpack_pair(self.0[4]);
let (b0, b1) = unpack_pair(self.0[0]);
let (b2, b3) = unpack_pair(self.0[1]);
let (b4, b5) = unpack_pair(self.0[2]);
let (b6, b7) = unpack_pair(self.0[3]);
let (b8, b9) = unpack_pair(self.0[4]);
FieldElement2625x4::reduce64([
_mm256_mul_epu32(b0.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b1.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b2.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b3.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b4.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b5.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b6.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b7.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b8.into_bits(), consts.into_bits()).into_bits(),
_mm256_mul_epu32(b9.into_bits(), consts.into_bits()).into_bits(),
])
}
FieldElement2625x4::reduce64([
b0.mul32(consts),
b1.mul32(consts),
b2.mul32(consts),
b3.mul32(consts),
b4.mul32(consts),
b5.mul32(consts),
b6.mul32(consts),
b7.mul32(consts),
b8.mul32(consts),
b9.mul32(consts),
])
}
}
impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
#[unsafe_target_feature("avx2")]
impl Mul<&FieldElement2625x4> for &FieldElement2625x4 {
type Output = FieldElement2625x4;
/// Multiply `self` by `rhs`.
///
@ -778,17 +774,17 @@ impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
///
/// The coefficients of the result are bounded with \\( b < 0.007 \\).
///
fn mul(self, rhs: &'b FieldElement2625x4) -> FieldElement2625x4 {
#[rustfmt::skip] // keep alignment of z* calculations
#[inline]
fn mul(self, rhs: &FieldElement2625x4) -> FieldElement2625x4 {
#[inline(always)]
fn m(x: u32x8, y: u32x8) -> u64x4 {
use core::arch::x86_64::_mm256_mul_epu32;
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
x.mul32(y)
}
#[inline(always)]
fn m_lo(x: u32x8, y: u32x8) -> u32x8 {
use core::arch::x86_64::_mm256_mul_epu32;
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
x.mul32(y).into()
}
let (x0, x1) = unpack_pair(self.0[0]);
@ -821,16 +817,16 @@ impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
let x7_2 = x7 + x7;
let x9_2 = x9 + x9;
let z0 = m(x0,y0) + m(x1_2,y9_19) + m(x2,y8_19) + m(x3_2,y7_19) + m(x4,y6_19) + m(x5_2,y5_19) + m(x6,y4_19) + m(x7_2,y3_19) + m(x8,y2_19) + m(x9_2,y1_19);
let z1 = m(x0,y1) + m(x1,y0) + m(x2,y9_19) + m(x3,y8_19) + m(x4,y7_19) + m(x5,y6_19) + m(x6,y5_19) + m(x7,y4_19) + m(x8,y3_19) + m(x9,y2_19);
let z2 = m(x0,y2) + m(x1_2,y1) + m(x2,y0) + m(x3_2,y9_19) + m(x4,y8_19) + m(x5_2,y7_19) + m(x6,y6_19) + m(x7_2,y5_19) + m(x8,y4_19) + m(x9_2,y3_19);
let z3 = m(x0,y3) + m(x1,y2) + m(x2,y1) + m(x3,y0) + m(x4,y9_19) + m(x5,y8_19) + m(x6,y7_19) + m(x7,y6_19) + m(x8,y5_19) + m(x9,y4_19);
let z4 = m(x0,y4) + m(x1_2,y3) + m(x2,y2) + m(x3_2,y1) + m(x4,y0) + m(x5_2,y9_19) + m(x6,y8_19) + m(x7_2,y7_19) + m(x8,y6_19) + m(x9_2,y5_19);
let z5 = m(x0,y5) + m(x1,y4) + m(x2,y3) + m(x3,y2) + m(x4,y1) + m(x5,y0) + m(x6,y9_19) + m(x7,y8_19) + m(x8,y7_19) + m(x9,y6_19);
let z6 = m(x0,y6) + m(x1_2,y5) + m(x2,y4) + m(x3_2,y3) + m(x4,y2) + m(x5_2,y1) + m(x6,y0) + m(x7_2,y9_19) + m(x8,y8_19) + m(x9_2,y7_19);
let z7 = m(x0,y7) + m(x1,y6) + m(x2,y5) + m(x3,y4) + m(x4,y3) + m(x5,y2) + m(x6,y1) + m(x7,y0) + m(x8,y9_19) + m(x9,y8_19);
let z8 = m(x0,y8) + m(x1_2,y7) + m(x2,y6) + m(x3_2,y5) + m(x4,y4) + m(x5_2,y3) + m(x6,y2) + m(x7_2,y1) + m(x8,y0) + m(x9_2,y9_19);
let z9 = m(x0,y9) + m(x1,y8) + m(x2,y7) + m(x3,y6) + m(x4,y5) + m(x5,y4) + m(x6,y3) + m(x7,y2) + m(x8,y1) + m(x9,y0);
let z0 = m(x0, y0) + m(x1_2, y9_19) + m(x2, y8_19) + m(x3_2, y7_19) + m(x4, y6_19) + m(x5_2, y5_19) + m(x6, y4_19) + m(x7_2, y3_19) + m(x8, y2_19) + m(x9_2, y1_19);
let z1 = m(x0, y1) + m(x1, y0) + m(x2, y9_19) + m(x3, y8_19) + m(x4, y7_19) + m(x5, y6_19) + m(x6, y5_19) + m(x7, y4_19) + m(x8, y3_19) + m(x9, y2_19);
let z2 = m(x0, y2) + m(x1_2, y1) + m(x2, y0) + m(x3_2, y9_19) + m(x4, y8_19) + m(x5_2, y7_19) + m(x6, y6_19) + m(x7_2, y5_19) + m(x8, y4_19) + m(x9_2, y3_19);
let z3 = m(x0, y3) + m(x1, y2) + m(x2, y1) + m(x3, y0) + m(x4, y9_19) + m(x5, y8_19) + m(x6, y7_19) + m(x7, y6_19) + m(x8, y5_19) + m(x9, y4_19);
let z4 = m(x0, y4) + m(x1_2, y3) + m(x2, y2) + m(x3_2, y1) + m(x4, y0) + m(x5_2, y9_19) + m(x6, y8_19) + m(x7_2, y7_19) + m(x8, y6_19) + m(x9_2, y5_19);
let z5 = m(x0, y5) + m(x1, y4) + m(x2, y3) + m(x3, y2) + m(x4, y1) + m(x5, y0) + m(x6, y9_19) + m(x7, y8_19) + m(x8, y7_19) + m(x9, y6_19);
let z6 = m(x0, y6) + m(x1_2, y5) + m(x2, y4) + m(x3_2, y3) + m(x4, y2) + m(x5_2, y1) + m(x6, y0) + m(x7_2, y9_19) + m(x8, y8_19) + m(x9_2, y7_19);
let z7 = m(x0, y7) + m(x1, y6) + m(x2, y5) + m(x3, y4) + m(x4, y3) + m(x5, y2) + m(x6, y1) + m(x7, y0) + m(x8, y9_19) + m(x9, y8_19);
let z8 = m(x0, y8) + m(x1_2, y7) + m(x2, y6) + m(x3_2, y5) + m(x4, y4) + m(x5_2, y3) + m(x6, y2) + m(x7_2, y1) + m(x8, y0) + m(x9_2, y9_19);
let z9 = m(x0, y9) + m(x1, y8) + m(x2, y7) + m(x3, y6) + m(x4, y5) + m(x5, y4) + m(x6, y3) + m(x7, y2) + m(x8, y1) + m(x9, y0);
// The bounds on z[i] are the same as in the serial 32-bit code
// and the comment below is copied from there:
@ -874,15 +870,16 @@ impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
}
}
#[cfg(target_feature = "avx2")]
#[cfg(test)]
mod test {
use super::*;
#[test]
fn scale_by_curve_constants() {
let mut x = FieldElement2625x4::splat(&FieldElement51::one());
let mut x = FieldElement2625x4::splat(&FieldElement51::ONE);
x = x * (121666, 121666, 2*121666, 2*121665);
x = x * (121666, 121666, 2 * 121666, 2 * 121665);
let xs = x.split();
assert_eq!(xs[0], FieldElement51([121666, 0, 0, 0, 0]));

View File

@ -9,13 +9,12 @@
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![cfg_attr(
feature = "nightly",
doc(include = "../../../../docs/avx2-notes.md")
)]
#![doc = include_str!("../../../../docs/avx2-notes.md")]
pub(crate) mod field;
pub(crate) mod edwards;
pub(crate) mod constants;
pub(crate) use self::edwards::{CachedPoint, ExtendedPoint};

View File

@ -9,15 +9,20 @@
#![allow(non_snake_case)]
use traits::Identity;
use crate::traits::Identity;
use std::ops::{Add, Neg, Sub};
use core::ops::{Add, Neg, Sub};
use subtle::Choice;
use subtle::ConditionallySelectable;
use edwards;
use window::{LookupTable, NafLookupTable5, NafLookupTable8};
use curve25519_dalek_derive::unsafe_target_feature;
use crate::edwards;
use crate::window::{LookupTable, NafLookupTable5};
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
use crate::window::NafLookupTable8;
use super::constants;
use super::field::{F51x4Reduced, F51x4Unreduced, Lanes, Shuffle};
@ -28,12 +33,14 @@ pub struct ExtendedPoint(pub(super) F51x4Unreduced);
#[derive(Copy, Clone, Debug)]
pub struct CachedPoint(pub(super) F51x4Reduced);
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl From<edwards::EdwardsPoint> for ExtendedPoint {
fn from(P: edwards::EdwardsPoint) -> ExtendedPoint {
ExtendedPoint(F51x4Unreduced::new(&P.X, &P.Y, &P.Z, &P.T))
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl From<ExtendedPoint> for edwards::EdwardsPoint {
fn from(P: ExtendedPoint) -> edwards::EdwardsPoint {
let reduced = F51x4Reduced::from(P.0);
@ -47,6 +54,7 @@ impl From<ExtendedPoint> for edwards::EdwardsPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl From<ExtendedPoint> for CachedPoint {
fn from(P: ExtendedPoint) -> CachedPoint {
let mut x = P.0;
@ -59,24 +67,27 @@ impl From<ExtendedPoint> for CachedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Default for ExtendedPoint {
fn default() -> ExtendedPoint {
ExtendedPoint::identity()
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Identity for ExtendedPoint {
fn identity() -> ExtendedPoint {
constants::EXTENDEDPOINT_IDENTITY
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl ExtendedPoint {
pub fn double(&self) -> ExtendedPoint {
// (Y1 X1 T1 Z1) -- uses vpshufd (1c latency @ 1/c)
let mut tmp0 = self.0.shuffle(Shuffle::BADC);
// (X1+Y1 X1+Y1 X1+Y1 X1+Y1) -- can use vpinserti128
// (X1+Y1 X1+Y1 X1+Y1 X1+Y1) -- can use vpinserti128
let mut tmp1 = (self.0 + tmp0).shuffle(Shuffle::ABAB);
// (X1 Y1 Z1 X1+Y1)
@ -97,7 +108,7 @@ impl ExtendedPoint {
// =======================
// S5 S6 S8 S9
let zero = F51x4Unreduced::zero();
let zero = F51x4Unreduced::ZERO;
let S1_S1_S1_S1 = tmp1.shuffle(Shuffle::AAAA);
let S2_S2_S2_S2 = tmp1.shuffle(Shuffle::BBBB);
@ -122,6 +133,7 @@ impl ExtendedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
type Output = ExtendedPoint;
@ -151,18 +163,21 @@ impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Default for CachedPoint {
fn default() -> CachedPoint {
CachedPoint::identity()
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Identity for CachedPoint {
fn identity() -> CachedPoint {
constants::CACHEDPOINT_IDENTITY
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl ConditionallySelectable for CachedPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
CachedPoint(F51x4Reduced::conditional_select(&a.0, &b.0, choice))
@ -173,6 +188,7 @@ impl ConditionallySelectable for CachedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a> Neg for &'a CachedPoint {
type Output = CachedPoint;
@ -182,6 +198,7 @@ impl<'a> Neg for &'a CachedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
type Output = ExtendedPoint;
@ -191,6 +208,7 @@ impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
let P = ExtendedPoint::from(*point);
@ -202,6 +220,7 @@ impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
@ -215,6 +234,8 @@ impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
}
}
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
fn from(point: &'a edwards::EdwardsPoint) -> Self {
let A = ExtendedPoint::from(*point);
@ -228,6 +249,7 @@ impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
}
}
#[cfg(target_feature = "avx512ifma,avx512vl")]
#[cfg(test)]
mod test {
use super::*;
@ -258,8 +280,8 @@ mod test {
#[test]
fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() {
use constants;
use scalar::Scalar;
use crate::constants;
use crate::scalar::Scalar;
println!("Testing id +- id");
let P = edwards::EdwardsPoint::identity();
@ -278,7 +300,7 @@ mod test {
println!("Testing B +- kB");
let P = constants::ED25519_BASEPOINT_POINT;
let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
let Q = constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
addition_test_helper(P, Q);
}
@ -297,8 +319,8 @@ mod test {
#[test]
fn vector_doubling_vs_serial_doubling_vs_edwards_extendedpoint() {
use constants;
use scalar::Scalar;
use crate::constants;
use crate::scalar::Scalar;
println!("Testing [2]id");
let P = edwards::EdwardsPoint::identity();
@ -309,7 +331,7 @@ mod test {
doubling_test_helper(P);
println!("Testing [2]([k]B)");
let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
let P = constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
doubling_test_helper(P);
}
}

View File

@ -11,23 +11,27 @@
#![allow(non_snake_case)]
use crate::backend::vector::packed_simd::u64x4;
use core::ops::{Add, Mul, Neg};
use packed_simd::{u64x4, IntoBits};
use backend::serial::u64::field::FieldElement51;
use crate::backend::serial::u64::field::FieldElement51;
use curve25519_dalek_derive::unsafe_target_feature;
/// A wrapper around `vpmadd52luq` that works on `u64x4`.
#[inline(always)]
#[unsafe_target_feature("avx512ifma,avx512vl")]
#[inline]
unsafe fn madd52lo(z: u64x4, x: u64x4, y: u64x4) -> u64x4 {
use core::arch::x86_64::_mm256_madd52lo_epu64;
_mm256_madd52lo_epu64(z.into_bits(), x.into_bits(), y.into_bits()).into_bits()
_mm256_madd52lo_epu64(z.into(), x.into(), y.into()).into()
}
/// A wrapper around `vpmadd52huq` that works on `u64x4`.
#[inline(always)]
#[unsafe_target_feature("avx512ifma,avx512vl")]
#[inline]
unsafe fn madd52hi(z: u64x4, x: u64x4, y: u64x4) -> u64x4 {
use core::arch::x86_64::_mm256_madd52hi_epu64;
_mm256_madd52hi_epu64(z.into_bits(), x.into_bits(), y.into_bits()).into_bits()
_mm256_madd52hi_epu64(z.into(), x.into(), y.into()).into()
}
/// A vector of four field elements in radix 2^51, with unreduced coefficients.
@ -38,6 +42,7 @@ pub struct F51x4Unreduced(pub(crate) [u64x4; 5]);
#[derive(Copy, Clone, Debug)]
pub struct F51x4Reduced(pub(crate) [u64x4; 5]);
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone)]
pub enum Shuffle {
AAAA,
@ -52,26 +57,28 @@ pub enum Shuffle {
CACA,
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
#[inline(always)]
fn shuffle_lanes(x: u64x4, control: Shuffle) -> u64x4 {
unsafe {
use core::arch::x86_64::_mm256_permute4x64_epi64 as perm;
match control {
Shuffle::AAAA => perm(x.into_bits(), 0b00_00_00_00).into_bits(),
Shuffle::BBBB => perm(x.into_bits(), 0b01_01_01_01).into_bits(),
Shuffle::BADC => perm(x.into_bits(), 0b10_11_00_01).into_bits(),
Shuffle::BACD => perm(x.into_bits(), 0b11_10_00_01).into_bits(),
Shuffle::ADDA => perm(x.into_bits(), 0b00_11_11_00).into_bits(),
Shuffle::CBCB => perm(x.into_bits(), 0b01_10_01_10).into_bits(),
Shuffle::ABDC => perm(x.into_bits(), 0b10_11_01_00).into_bits(),
Shuffle::ABAB => perm(x.into_bits(), 0b01_00_01_00).into_bits(),
Shuffle::DBBD => perm(x.into_bits(), 0b11_01_01_11).into_bits(),
Shuffle::CACA => perm(x.into_bits(), 0b00_10_00_10).into_bits(),
Shuffle::AAAA => perm(x.into(), 0b00_00_00_00).into(),
Shuffle::BBBB => perm(x.into(), 0b01_01_01_01).into(),
Shuffle::BADC => perm(x.into(), 0b10_11_00_01).into(),
Shuffle::BACD => perm(x.into(), 0b11_10_00_01).into(),
Shuffle::ADDA => perm(x.into(), 0b00_11_11_00).into(),
Shuffle::CBCB => perm(x.into(), 0b01_10_01_10).into(),
Shuffle::ABDC => perm(x.into(), 0b10_11_01_00).into(),
Shuffle::ABAB => perm(x.into(), 0b01_00_01_00).into(),
Shuffle::DBBD => perm(x.into(), 0b11_01_01_11).into(),
Shuffle::CACA => perm(x.into(), 0b00_10_00_10).into(),
}
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone)]
pub enum Lanes {
D,
@ -82,26 +89,26 @@ pub enum Lanes {
BCD,
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
#[inline]
fn blend_lanes(x: u64x4, y: u64x4, control: Lanes) -> u64x4 {
unsafe {
use core::arch::x86_64::_mm256_blend_epi32 as blend;
match control {
Lanes::D => blend(x.into_bits(), y.into_bits(), 0b11_00_00_00).into_bits(),
Lanes::C => blend(x.into_bits(), y.into_bits(), 0b00_11_00_00).into_bits(),
Lanes::AB => blend(x.into_bits(), y.into_bits(), 0b00_00_11_11).into_bits(),
Lanes::AC => blend(x.into_bits(), y.into_bits(), 0b00_11_00_11).into_bits(),
Lanes::AD => blend(x.into_bits(), y.into_bits(), 0b11_00_00_11).into_bits(),
Lanes::BCD => blend(x.into_bits(), y.into_bits(), 0b11_11_11_00).into_bits(),
Lanes::D => blend(x.into(), y.into(), 0b11_00_00_00).into(),
Lanes::C => blend(x.into(), y.into(), 0b00_11_00_00).into(),
Lanes::AB => blend(x.into(), y.into(), 0b00_00_11_11).into(),
Lanes::AC => blend(x.into(), y.into(), 0b00_11_00_11).into(),
Lanes::AD => blend(x.into(), y.into(), 0b11_00_00_11).into(),
Lanes::BCD => blend(x.into(), y.into(), 0b11_11_11_00).into(),
}
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl F51x4Unreduced {
pub fn zero() -> F51x4Unreduced {
F51x4Unreduced([u64x4::splat(0); 5])
}
pub const ZERO: F51x4Unreduced = F51x4Unreduced([u64x4::splat_const::<0>(); 5]);
pub fn new(
x0: &FieldElement51,
@ -122,32 +129,32 @@ impl F51x4Unreduced {
let x = &self.0;
[
FieldElement51([
x[0].extract(0),
x[1].extract(0),
x[2].extract(0),
x[3].extract(0),
x[4].extract(0),
x[0].extract::<0>(),
x[1].extract::<0>(),
x[2].extract::<0>(),
x[3].extract::<0>(),
x[4].extract::<0>(),
]),
FieldElement51([
x[0].extract(1),
x[1].extract(1),
x[2].extract(1),
x[3].extract(1),
x[4].extract(1),
x[0].extract::<1>(),
x[1].extract::<1>(),
x[2].extract::<1>(),
x[3].extract::<1>(),
x[4].extract::<1>(),
]),
FieldElement51([
x[0].extract(2),
x[1].extract(2),
x[2].extract(2),
x[3].extract(2),
x[4].extract(2),
x[0].extract::<2>(),
x[1].extract::<2>(),
x[2].extract::<2>(),
x[3].extract::<2>(),
x[4].extract::<2>(),
]),
FieldElement51([
x[0].extract(3),
x[1].extract(3),
x[2].extract(3),
x[3].extract(3),
x[4].extract(3),
x[0].extract::<3>(),
x[1].extract::<3>(),
x[2].extract::<3>(),
x[3].extract::<3>(),
x[4].extract::<3>(),
]),
]
}
@ -198,6 +205,7 @@ impl F51x4Unreduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Neg for F51x4Reduced {
type Output = F51x4Reduced;
@ -209,6 +217,7 @@ impl Neg for F51x4Reduced {
use subtle::Choice;
use subtle::ConditionallySelectable;
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl ConditionallySelectable for F51x4Reduced {
#[inline]
fn conditional_select(a: &F51x4Reduced, b: &F51x4Reduced, choice: Choice) -> F51x4Reduced {
@ -235,6 +244,7 @@ impl ConditionallySelectable for F51x4Reduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl F51x4Reduced {
#[inline]
pub fn shuffle(&self, control: Shuffle) -> F51x4Reduced {
@ -291,64 +301,64 @@ impl F51x4Reduced {
z1_2 = madd52hi(z1_2, x[0], x[0]);
z2_4 = madd52hi(z2_4, x[0], x[1]);
let mut z2_1 = z2_4 << 2;
let mut z2_1 = z2_4.shl::<2>();
z2_2 = madd52lo(z2_2, x[0], x[2]);
z2_1 = madd52lo(z2_1, x[1], x[1]);
z3_4 = madd52hi(z3_4, x[0], x[2]);
let mut z3_1 = z3_4 << 2;
let mut z3_1 = z3_4.shl::<2>();
z3_2 = madd52lo(z3_2, x[1], x[2]);
z3_2 = madd52lo(z3_2, x[0], x[3]);
z3_2 = madd52hi(z3_2, x[1], x[1]);
z4_4 = madd52hi(z4_4, x[1], x[2]);
z4_4 = madd52hi(z4_4, x[0], x[3]);
let mut z4_1 = z4_4 << 2;
let mut z4_1 = z4_4.shl::<2>();
z4_2 = madd52lo(z4_2, x[1], x[3]);
z4_2 = madd52lo(z4_2, x[0], x[4]);
z4_1 = madd52lo(z4_1, x[2], x[2]);
z5_4 = madd52hi(z5_4, x[1], x[3]);
z5_4 = madd52hi(z5_4, x[0], x[4]);
let mut z5_1 = z5_4 << 2;
let mut z5_1 = z5_4.shl::<2>();
z5_2 = madd52lo(z5_2, x[2], x[3]);
z5_2 = madd52lo(z5_2, x[1], x[4]);
z5_2 = madd52hi(z5_2, x[2], x[2]);
z6_4 = madd52hi(z6_4, x[2], x[3]);
z6_4 = madd52hi(z6_4, x[1], x[4]);
let mut z6_1 = z6_4 << 2;
let mut z6_1 = z6_4.shl::<2>();
z6_2 = madd52lo(z6_2, x[2], x[4]);
z6_1 = madd52lo(z6_1, x[3], x[3]);
z7_4 = madd52hi(z7_4, x[2], x[4]);
let mut z7_1 = z7_4 << 2;
let mut z7_1 = z7_4.shl::<2>();
z7_2 = madd52lo(z7_2, x[3], x[4]);
z7_2 = madd52hi(z7_2, x[3], x[3]);
z8_4 = madd52hi(z8_4, x[3], x[4]);
let mut z8_1 = z8_4 << 2;
let mut z8_1 = z8_4.shl::<2>();
z8_1 = madd52lo(z8_1, x[4], x[4]);
let mut z9_1 = u64x4::splat(0);
z9_2 = madd52hi(z9_2, x[4], x[4]);
z5_1 += z5_2 << 1;
z6_1 += z6_2 << 1;
z7_1 += z7_2 << 1;
z9_1 += z9_2 << 1;
z5_1 += z5_2.shl::<1>();
z6_1 += z6_2.shl::<1>();
z7_1 += z7_2.shl::<1>();
z9_1 += z9_2.shl::<1>();
let mut t0 = u64x4::splat(0);
let mut t1 = u64x4::splat(0);
let r19 = u64x4::splat(19);
t0 = madd52hi(t0, r19, z9_1);
t1 = madd52lo(t1, r19, z9_1 >> 52);
t1 = madd52lo(t1, r19, z9_1.shr::<52>());
z4_2 = madd52lo(z4_2, r19, z8_1 >> 52);
z3_2 = madd52lo(z3_2, r19, z7_1 >> 52);
z2_2 = madd52lo(z2_2, r19, z6_1 >> 52);
z1_2 = madd52lo(z1_2, r19, z5_1 >> 52);
z4_2 = madd52lo(z4_2, r19, z8_1.shr::<52>());
z3_2 = madd52lo(z3_2, r19, z7_1.shr::<52>());
z2_2 = madd52lo(z2_2, r19, z6_1.shr::<52>());
z1_2 = madd52lo(z1_2, r19, z5_1.shr::<52>());
z0_2 = madd52lo(z0_2, r19, t0 + t1);
z1_2 = madd52hi(z1_2, r19, z5_1);
@ -373,6 +383,7 @@ impl F51x4Reduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl From<F51x4Reduced> for F51x4Unreduced {
#[inline]
fn from(x: F51x4Reduced) -> F51x4Unreduced {
@ -380,6 +391,7 @@ impl From<F51x4Reduced> for F51x4Unreduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl From<F51x4Unreduced> for F51x4Reduced {
#[inline]
fn from(x: F51x4Unreduced) -> F51x4Reduced {
@ -387,11 +399,11 @@ impl From<F51x4Unreduced> for F51x4Reduced {
let r19 = u64x4::splat(19);
// Compute carryouts in parallel
let c0 = x.0[0] >> 51;
let c1 = x.0[1] >> 51;
let c2 = x.0[2] >> 51;
let c3 = x.0[3] >> 51;
let c4 = x.0[4] >> 51;
let c0 = x.0[0].shr::<51>();
let c1 = x.0[1].shr::<51>();
let c2 = x.0[2].shr::<51>();
let c3 = x.0[3].shr::<51>();
let c4 = x.0[4].shr::<51>();
unsafe {
F51x4Reduced([
@ -405,6 +417,7 @@ impl From<F51x4Unreduced> for F51x4Reduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl Add<F51x4Unreduced> for F51x4Unreduced {
type Output = F51x4Unreduced;
#[inline]
@ -419,6 +432,7 @@ impl Add<F51x4Unreduced> for F51x4Unreduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a> Mul<(u32, u32, u32, u32)> for &'a F51x4Reduced {
type Output = F51x4Unreduced;
#[inline]
@ -470,6 +484,7 @@ impl<'a> Mul<(u32, u32, u32, u32)> for &'a F51x4Reduced {
}
}
#[unsafe_target_feature("avx512ifma,avx512vl")]
impl<'a, 'b> Mul<&'b F51x4Reduced> for &'a F51x4Reduced {
type Output = F51x4Unreduced;
#[inline]
@ -581,12 +596,12 @@ impl<'a, 'b> Mul<&'b F51x4Reduced> for &'a F51x4Reduced {
// Wave 6
t0 = madd52hi(t0, r19, z9);
t1 = madd52lo(t1, r19, z9 >> 52);
t1 = madd52lo(t1, r19, z9.shr::<52>());
z3_1 = madd52lo(z3_1, x[0], y[3]);
z4_2 = madd52hi(z4_2, x[0], y[3]);
z1_2 = madd52lo(z1_2, r19, z5 >> 52);
z2_2 = madd52lo(z2_2, r19, z6 >> 52);
z3_2 = madd52lo(z3_2, r19, z7 >> 52);
z1_2 = madd52lo(z1_2, r19, z5.shr::<52>());
z2_2 = madd52lo(z2_2, r19, z6.shr::<52>());
z3_2 = madd52lo(z3_2, r19, z7.shr::<52>());
z0_1 = madd52lo(z0_1, r19, z5);
// Wave 7
@ -601,7 +616,7 @@ impl<'a, 'b> Mul<&'b F51x4Reduced> for &'a F51x4Reduced {
// Wave 8
z3_1 = madd52lo(z3_1, r19, z8);
z4_2 = madd52lo(z4_2, r19, z8 >> 52);
z4_2 = madd52lo(z4_2, r19, z8.shr::<52>());
F51x4Unreduced([
z0_1 + z0_2 + z0_2,
@ -614,6 +629,7 @@ impl<'a, 'b> Mul<&'b F51x4Reduced> for &'a F51x4Reduced {
}
}
#[cfg(target_feature = "avx512ifma,avx512vl")]
#[cfg(test)]
mod test {
use super::*;

View File

@ -7,13 +7,14 @@
// Authors:
// - Henry de Valence <hdevalence@hdevalence.ca>
#![cfg_attr(
feature = "nightly",
doc(include = "../../../../docs/ifma-notes.md")
)]
#![doc = include_str!("../../../../docs/ifma-notes.md")]
#[allow(missing_docs)]
pub mod field;
#[allow(missing_docs)]
pub mod edwards;
pub mod constants;
pub(crate) use self::edwards::{CachedPoint, ExtendedPoint};

View File

@ -9,11 +9,14 @@
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Crate-local prelude (for alloc-dependent features like `Vec`)
#![doc = include_str!("../../../docs/parallel-formulas.md")]
// TODO: switch to alloc::prelude
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub use alloc::vec::Vec;
#[allow(missing_docs)]
pub mod packed_simd;
#[cfg(feature = "std")]
pub use std::vec::Vec;
pub mod avx2;
#[cfg(nightly)]
pub mod ifma;
pub mod scalar_mul;

View File

@ -0,0 +1,337 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// See LICENSE for licensing information.
//! This module defines wrappers over platform-specific SIMD types to make them
//! more convenient to use.
//!
//! UNSAFETY: Everything in this module assumes that we're running on hardware
//! which supports at least AVX2. This invariant *must* be enforced
//! by the callers of this code.
use core::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Sub};
use curve25519_dalek_derive::unsafe_target_feature;
macro_rules! impl_shared {
(
$ty:ident,
$lane_ty:ident,
$add_intrinsic:ident,
$sub_intrinsic:ident,
$shl_intrinsic:ident,
$shr_intrinsic:ident,
$extract_intrinsic:ident
) => {
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct $ty(core::arch::x86_64::__m256i);
#[unsafe_target_feature("avx2")]
impl From<$ty> for core::arch::x86_64::__m256i {
#[inline]
fn from(value: $ty) -> core::arch::x86_64::__m256i {
value.0
}
}
#[unsafe_target_feature("avx2")]
impl From<core::arch::x86_64::__m256i> for $ty {
#[inline]
fn from(value: core::arch::x86_64::__m256i) -> $ty {
$ty(value)
}
}
#[unsafe_target_feature("avx2")]
impl PartialEq for $ty {
#[inline]
fn eq(&self, rhs: &$ty) -> bool {
unsafe {
// This compares each pair of 8-bit packed integers and returns either 0xFF or
// 0x00 depending on whether they're equal.
//
// So the values are equal if (and only if) this returns a value that's filled
// with only 0xFF.
//
// Pseudocode of what this does:
// self.0
// .bytes()
// .zip(rhs.0.bytes())
// .map(|a, b| if a == b { 0xFF } else { 0x00 })
// .join();
let m = core::arch::x86_64::_mm256_cmpeq_epi8(self.0, rhs.0);
// Now we need to reduce the 256-bit value to something on which we can branch.
//
// This will just take the most significant bit of every 8-bit packed integer
// and build an `i32` out of it. If the values we previously compared were
// equal then all off the most significant bits will be equal to 1, which means
// that this will return 0xFFFFFFFF, which is equal to -1 when represented as
// an `i32`.
core::arch::x86_64::_mm256_movemask_epi8(m) == -1
}
}
}
impl Eq for $ty {}
#[unsafe_target_feature("avx2")]
impl Add for $ty {
type Output = Self;
#[inline]
fn add(self, rhs: $ty) -> Self {
unsafe { core::arch::x86_64::$add_intrinsic(self.0, rhs.0).into() }
}
}
#[allow(clippy::assign_op_pattern)]
#[unsafe_target_feature("avx2")]
impl AddAssign for $ty {
#[inline]
fn add_assign(&mut self, rhs: $ty) {
*self = *self + rhs
}
}
#[unsafe_target_feature("avx2")]
impl Sub for $ty {
type Output = Self;
#[inline]
fn sub(self, rhs: $ty) -> Self {
unsafe { core::arch::x86_64::$sub_intrinsic(self.0, rhs.0).into() }
}
}
#[unsafe_target_feature("avx2")]
impl BitAnd for $ty {
type Output = Self;
#[inline]
fn bitand(self, rhs: $ty) -> Self {
unsafe { core::arch::x86_64::_mm256_and_si256(self.0, rhs.0).into() }
}
}
#[unsafe_target_feature("avx2")]
impl BitXor for $ty {
type Output = Self;
#[inline]
fn bitxor(self, rhs: $ty) -> Self {
unsafe { core::arch::x86_64::_mm256_xor_si256(self.0, rhs.0).into() }
}
}
#[allow(clippy::assign_op_pattern)]
#[unsafe_target_feature("avx2")]
impl BitAndAssign for $ty {
#[inline]
fn bitand_assign(&mut self, rhs: $ty) {
*self = *self & rhs;
}
}
#[allow(clippy::assign_op_pattern)]
#[unsafe_target_feature("avx2")]
impl BitXorAssign for $ty {
#[inline]
fn bitxor_assign(&mut self, rhs: $ty) {
*self = *self ^ rhs;
}
}
#[unsafe_target_feature("avx2")]
#[allow(dead_code)]
impl $ty {
#[inline]
pub fn shl<const N: i32>(self) -> Self {
unsafe { core::arch::x86_64::$shl_intrinsic(self.0, N).into() }
}
#[inline]
pub fn shr<const N: i32>(self) -> Self {
unsafe { core::arch::x86_64::$shr_intrinsic(self.0, N).into() }
}
#[inline]
pub fn extract<const N: i32>(self) -> $lane_ty {
unsafe { core::arch::x86_64::$extract_intrinsic(self.0, N) as $lane_ty }
}
}
};
}
macro_rules! impl_conv {
($src:ident => $($dst:ident),+) => {
$(
#[unsafe_target_feature("avx2")]
impl From<$src> for $dst {
#[inline]
fn from(value: $src) -> $dst {
$dst(value.0)
}
}
)+
}
}
// We define SIMD functionality over packed unsigned integer types. However, all the integer
// intrinsics deal with signed integers. So we cast unsigned to signed, pack it into SIMD, do
// add/sub/shl/shr arithmetic, and finally cast back to unsigned at the end. Why is this equivalent
// to doing the same thing on unsigned integers? Shl/shr is clear, because casting does not change
// the bits of the integer. But what about add/sub? This is due to the following:
//
// 1) Rust uses two's complement to represent signed integers. So we're assured that the values
// we cast into SIMD and extract out at the end are two's complement.
//
// https://doc.rust-lang.org/reference/types/numeric.html
//
// 2) Wrapping add/sub is compatible between two's complement signed and unsigned integers.
// That is, for all x,y: u64 (or any unsigned integer type),
//
// x.wrapping_add(y) == (x as i64).wrapping_add(y as i64) as u64, and
// x.wrapping_sub(y) == (x as i64).wrapping_sub(y as i64) as u64
//
// https://julesjacobs.com/2019/03/20/why-twos-complement-works.html
//
// 3) The add/sub functions we use for SIMD are indeed wrapping. The docs indicate that
// __mm256_add/sub compile to vpaddX/vpsubX instructions where X = w, d, or q depending on
// the bitwidth. From x86 docs:
//
// When an individual result is too large to be represented in X bits (overflow), the
// result is wrapped around and the low X bits are written to the destination operand
// (that is, the carry is ignored).
//
// https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq
// https://www.felixcloutier.com/x86/psubb:psubw:psubd
// https://www.felixcloutier.com/x86/psubq
impl_shared!(
u64x4,
u64,
_mm256_add_epi64,
_mm256_sub_epi64,
_mm256_slli_epi64,
_mm256_srli_epi64,
_mm256_extract_epi64
);
impl_shared!(
u32x8,
u32,
_mm256_add_epi32,
_mm256_sub_epi32,
_mm256_slli_epi32,
_mm256_srli_epi32,
_mm256_extract_epi32
);
impl_conv!(u64x4 => u32x8);
#[allow(dead_code)]
impl u64x4 {
/// A constified variant of `new`.
///
/// Should only be called from `const` contexts. At runtime `new` is going to be faster.
#[inline]
pub const fn new_const(x0: u64, x1: u64, x2: u64, x3: u64) -> Self {
// SAFETY: Transmuting between an array and a SIMD type is safe
// https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
unsafe { Self(core::mem::transmute([x0, x1, x2, x3])) }
}
/// A constified variant of `splat`.
///
/// Should only be called from `const` contexts. At runtime `splat` is going to be faster.
#[inline]
pub const fn splat_const<const N: u64>() -> Self {
Self::new_const(N, N, N, N)
}
/// Constructs a new instance.
#[unsafe_target_feature("avx2")]
#[inline]
pub fn new(x0: u64, x1: u64, x2: u64, x3: u64) -> u64x4 {
unsafe {
// _mm256_set_epi64 sets the underlying vector in reverse order of the args
u64x4(core::arch::x86_64::_mm256_set_epi64x(
x3 as i64, x2 as i64, x1 as i64, x0 as i64,
))
}
}
/// Constructs a new instance with all of the elements initialized to the given value.
#[unsafe_target_feature("avx2")]
#[inline]
pub fn splat(x: u64) -> u64x4 {
unsafe { u64x4(core::arch::x86_64::_mm256_set1_epi64x(x as i64)) }
}
}
#[allow(dead_code)]
impl u32x8 {
/// A constified variant of `new`.
///
/// Should only be called from `const` contexts. At runtime `new` is going to be faster.
#[allow(clippy::too_many_arguments)]
#[inline]
pub const fn new_const(
x0: u32,
x1: u32,
x2: u32,
x3: u32,
x4: u32,
x5: u32,
x6: u32,
x7: u32,
) -> Self {
// SAFETY: Transmuting between an array and a SIMD type is safe
// https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html
unsafe { Self(core::mem::transmute([x0, x1, x2, x3, x4, x5, x6, x7])) }
}
/// A constified variant of `splat`.
///
/// Should only be called from `const` contexts. At runtime `splat` is going to be faster.
#[inline]
pub const fn splat_const<const N: u32>() -> Self {
Self::new_const(N, N, N, N, N, N, N, N)
}
/// Constructs a new instance.
#[allow(clippy::too_many_arguments)]
#[unsafe_target_feature("avx2")]
#[inline]
pub fn new(x0: u32, x1: u32, x2: u32, x3: u32, x4: u32, x5: u32, x6: u32, x7: u32) -> u32x8 {
unsafe {
// _mm256_set_epi32 sets the underlying vector in reverse order of the args
u32x8(core::arch::x86_64::_mm256_set_epi32(
x7 as i32, x6 as i32, x5 as i32, x4 as i32, x3 as i32, x2 as i32, x1 as i32,
x0 as i32,
))
}
}
/// Constructs a new instance with all of the elements initialized to the given value.
#[unsafe_target_feature("avx2")]
#[inline]
pub fn splat(x: u32) -> u32x8 {
unsafe { u32x8(core::arch::x86_64::_mm256_set1_epi32(x as i32)) }
}
}
#[unsafe_target_feature("avx2")]
impl u32x8 {
/// Multiplies the low unsigned 32-bits from each packed 64-bit element
/// and returns the unsigned 64-bit results.
///
/// (This ignores the upper 32-bits from each packed 64-bits!)
#[inline]
pub fn mul32(self, rhs: u32x8) -> u64x4 {
// NOTE: This ignores the upper 32-bits from each packed 64-bits.
unsafe { core::arch::x86_64::_mm256_mul_epu32(self.0, rhs.0).into() }
}
}

View File

@ -9,15 +9,22 @@
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Implementations of various multiplication algorithms for the SIMD backends.
#[allow(missing_docs)]
pub mod variable_base;
#[allow(missing_docs)]
pub mod vartime_double_base;
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub mod straus;
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub mod precomputed_straus;
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub mod pippenger;

View File

@ -0,0 +1,176 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2019 Oleg Andreev
// See LICENSE for licensing information.
//
// Authors:
// - Oleg Andreev <oleganza@gmail.com>
#![allow(non_snake_case)]
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
"avx2",
conditional("avx512ifma,avx512vl", nightly)
)]
pub mod spec {
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::{Identity, VartimeMultiscalarMul};
/// Implements a version of Pippenger's algorithm.
///
/// See the documentation in the serial `scalar_mul::pippenger` module for details.
pub struct Pippenger;
impl VartimeMultiscalarMul for Pippenger {
type Point = EdwardsPoint;
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
let mut scalars = scalars.into_iter();
let size = scalars.by_ref().size_hint().0;
let w = if size < 500 {
6
} else if size < 800 {
7
} else {
8
};
let max_digit: usize = 1 << w;
let digits_count: usize = Scalar::to_radix_2w_size_hint(w);
let buckets_count: usize = max_digit / 2; // digits are signed+centered hence 2^w/2, excluding 0-th bucket
// Collect optimized scalars and points in a buffer for repeated access
// (scanning the whole collection per each digit position).
let scalars = scalars.map(|s| s.borrow().as_radix_2w(w));
let points = points
.into_iter()
.map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P))));
let scalars_points = scalars
.zip(points)
.map(|(s, maybe_p)| maybe_p.map(|p| (s, p)))
.collect::<Option<Vec<_>>>()?;
// Prepare 2^w/2 buckets.
// buckets[i] corresponds to a multiplication factor (i+1).
let mut buckets: Vec<ExtendedPoint> = (0..buckets_count)
.map(|_| ExtendedPoint::identity())
.collect();
let mut columns = (0..digits_count).rev().map(|digit_index| {
// Clear the buckets when processing another digit.
for bucket in &mut buckets {
*bucket = ExtendedPoint::identity();
}
// Iterate over pairs of (point, scalar)
// and add/sub the point to the corresponding bucket.
// Note: if we add support for precomputed lookup tables,
// we'll be adding/subtractiong point premultiplied by `digits[i]` to buckets[0].
for (digits, pt) in scalars_points.iter() {
// Widen digit so that we don't run into edge cases when w=8.
let digit = digits[digit_index] as i16;
match digit.cmp(&0) {
Ordering::Greater => {
let b = (digit - 1) as usize;
buckets[b] = &buckets[b] + pt;
}
Ordering::Less => {
let b = (-digit - 1) as usize;
buckets[b] = &buckets[b] - pt;
}
Ordering::Equal => {}
}
}
// Add the buckets applying the multiplication factor to each bucket.
// The most efficient way to do that is to have a single sum with two running sums:
// an intermediate sum from last bucket to the first, and a sum of intermediate sums.
//
// For example, to add buckets 1*A, 2*B, 3*C we need to add these points:
// C
// C B
// C B A Sum = C + (C+B) + (C+B+A)
let mut buckets_intermediate_sum = buckets[buckets_count - 1];
let mut buckets_sum = buckets[buckets_count - 1];
for i in (0..(buckets_count - 1)).rev() {
buckets_intermediate_sum =
&buckets_intermediate_sum + &CachedPoint::from(buckets[i]);
buckets_sum = &buckets_sum + &CachedPoint::from(buckets_intermediate_sum);
}
buckets_sum
});
// Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`.
let hi_column = columns.next().expect("should have more than zero digits");
Some(
columns
.fold(hi_column, |total, p| {
&total.mul_by_pow_2(w as u32) + &CachedPoint::from(p)
})
.into(),
)
}
}
#[cfg(test)]
mod test {
#[test]
fn test_vartime_pippenger() {
use super::*;
use crate::constants;
use crate::scalar::Scalar;
// Reuse points across different tests
let mut n = 512;
let x = Scalar::from(2128506u64).invert();
let y = Scalar::from(4443282u64).invert();
let points: Vec<_> = (0..n)
.map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64))
.collect();
let scalars: Vec<_> = (0..n)
.map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars
.collect();
let premultiplied: Vec<EdwardsPoint> = scalars
.iter()
.zip(points.iter())
.map(|(sc, pt)| sc * pt)
.collect();
while n > 0 {
let scalars = &scalars[0..n].to_vec();
let points = &points[0..n].to_vec();
let control: EdwardsPoint = premultiplied[0..n].iter().sum();
let subject = Pippenger::vartime_multiscalar_mul(scalars.clone(), points.clone());
assert_eq!(subject.compress(), control.compress());
n = n / 2;
}
}
}
}

View File

@ -0,0 +1,127 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2019 Henry de Valence.
// See LICENSE for licensing information.
//
// Authors:
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Precomputation for Straus's method.
#![allow(non_snake_case)]
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
"avx2",
conditional("avx512ifma,avx512vl", nightly)
)]
pub mod spec {
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::traits::VartimePrecomputedMultiscalarMul;
use crate::window::{NafLookupTable5, NafLookupTable8};
pub struct VartimePrecomputedStraus {
static_lookup_tables: Vec<NafLookupTable8<CachedPoint>>,
}
impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
type Point = EdwardsPoint;
fn new<I>(static_points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<EdwardsPoint>,
{
Self {
static_lookup_tables: static_points
.into_iter()
.map(|P| NafLookupTable8::<CachedPoint>::from(P.borrow()))
.collect(),
}
}
fn optional_mixed_multiscalar_mul<I, J, K>(
&self,
static_scalars: I,
dynamic_scalars: J,
dynamic_points: K,
) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Scalar>,
K: IntoIterator<Item = Option<EdwardsPoint>>,
{
let static_nafs = static_scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect::<Vec<_>>();
let dynamic_nafs: Vec<_> = dynamic_scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect::<Vec<_>>();
let dynamic_lookup_tables = dynamic_points
.into_iter()
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
.collect::<Option<Vec<_>>>()?;
let sp = self.static_lookup_tables.len();
let dp = dynamic_lookup_tables.len();
assert_eq!(sp, static_nafs.len());
assert_eq!(dp, dynamic_nafs.len());
// We could save some doublings by looking for the highest
// nonzero NAF coefficient, but since we might have a lot of
// them to search, it's not clear it's worthwhile to check.
let mut R = ExtendedPoint::identity();
for j in (0..256).rev() {
R = R.double();
for i in 0..dp {
let t_ij = dynamic_nafs[i][j];
match t_ij.cmp(&0) {
Ordering::Greater => {
R = &R + &dynamic_lookup_tables[i].select(t_ij as usize);
}
Ordering::Less => {
R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize);
}
Ordering::Equal => {}
}
}
#[allow(clippy::needless_range_loop)]
for i in 0..sp {
let t_ij = static_nafs[i][j];
match t_ij.cmp(&0) {
Ordering::Greater => {
R = &R + &self.static_lookup_tables[i].select(t_ij as usize);
}
Ordering::Less => {
R = &R - &self.static_lookup_tables[i].select(-t_ij as usize);
}
Ordering::Equal => {}
}
}
}
Some(R.into())
}
}
}

View File

@ -0,0 +1,126 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![allow(non_snake_case)]
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
"avx2",
conditional("avx512ifma,avx512vl", nightly)
)]
pub mod spec {
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
#[cfg(feature = "zeroize")]
use zeroize::Zeroizing;
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
use crate::window::{LookupTable, NafLookupTable5};
/// Multiscalar multiplication using interleaved window / Straus'
/// method. See the `Straus` struct in the serial backend for more
/// details.
///
/// This exists as a seperate implementation from that one because the
/// AVX2 code uses different curve models (it does not pass between
/// multiple models during scalar mul), and it has to convert the
/// point representation on the fly.
pub struct Straus {}
impl MultiscalarMul for Straus {
type Point = EdwardsPoint;
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<EdwardsPoint>,
{
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
// for each input point P
let lookup_tables: Vec<_> = points
.into_iter()
.map(|point| LookupTable::<CachedPoint>::from(point.borrow()))
.collect();
let scalar_digits_vec: Vec<_> = scalars
.into_iter()
.map(|s| s.borrow().as_radix_16())
.collect();
// Pass ownership to a `Zeroizing` wrapper
#[cfg(feature = "zeroize")]
let scalar_digits_vec = Zeroizing::new(scalar_digits_vec);
let mut Q = ExtendedPoint::identity();
for j in (0..64).rev() {
Q = Q.mul_by_pow_2(4);
let it = scalar_digits_vec.iter().zip(lookup_tables.iter());
for (s_i, lookup_table_i) in it {
// Q = Q + s_{i,j} * P_i
Q = &Q + &lookup_table_i.select(s_i[j]);
}
}
Q.into()
}
}
impl VartimeMultiscalarMul for Straus {
type Point = EdwardsPoint;
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator<Item = Option<EdwardsPoint>>,
{
let nafs: Vec<_> = scalars
.into_iter()
.map(|c| c.borrow().non_adjacent_form(5))
.collect();
let lookup_tables: Vec<_> = points
.into_iter()
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
.collect::<Option<Vec<_>>>()?;
let mut Q = ExtendedPoint::identity();
for i in (0..256).rev() {
Q = Q.double();
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
match naf[i].cmp(&0) {
Ordering::Greater => {
Q = &Q + &lookup_table.select(naf[i] as usize);
}
Ordering::Less => {
Q = &Q - &lookup_table.select(-naf[i] as usize);
}
Ordering::Equal => {}
}
}
}
Some(Q.into())
}
}
}

View File

@ -0,0 +1,44 @@
#![allow(non_snake_case)]
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
"avx2",
conditional("avx512ifma,avx512vl", nightly)
)]
pub mod spec {
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::window::LookupTable;
/// Perform constant-time, variable-base scalar multiplication.
pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
// Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P]
let lookup_table = LookupTable::<CachedPoint>::from(point);
// Setting s = scalar, compute
//
// s = s_0 + s_1*16^1 + ... + s_63*16^63,
//
// with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`.
let scalar_digits = scalar.as_radix_16();
// Compute s*P as
//
// s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63)
// s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63
// s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...))
//
// We sum right-to-left.
let mut Q = ExtendedPoint::identity();
for i in (0..64).rev() {
Q = Q.mul_by_pow_2(4);
Q = &Q + &lookup_table.select(scalar_digits[i]);
}
Q.into()
}
}

View File

@ -0,0 +1,101 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![allow(non_snake_case)]
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
"avx2",
conditional("avx512ifma,avx512vl", nightly)
)]
pub mod spec {
use core::cmp::Ordering;
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint};
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint};
#[cfg(feature = "precomputed-tables")]
#[for_target_feature("avx2")]
use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
#[cfg(feature = "precomputed-tables")]
#[for_target_feature("avx512ifma")]
use crate::backend::vector::ifma::constants::BASEPOINT_ODD_LOOKUP_TABLE;
use crate::edwards::EdwardsPoint;
use crate::scalar::Scalar;
use crate::traits::Identity;
use crate::window::NafLookupTable5;
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
let a_naf = a.non_adjacent_form(5);
#[cfg(feature = "precomputed-tables")]
let b_naf = b.non_adjacent_form(8);
#[cfg(not(feature = "precomputed-tables"))]
let b_naf = b.non_adjacent_form(5);
// Find starting index
let mut i: usize = 255;
for j in (0..256).rev() {
i = j;
if a_naf[i] != 0 || b_naf[i] != 0 {
break;
}
}
let table_A = NafLookupTable5::<CachedPoint>::from(A);
#[cfg(feature = "precomputed-tables")]
let table_B = &BASEPOINT_ODD_LOOKUP_TABLE;
#[cfg(not(feature = "precomputed-tables"))]
let table_B =
&NafLookupTable5::<CachedPoint>::from(&crate::constants::ED25519_BASEPOINT_POINT);
let mut Q = ExtendedPoint::identity();
loop {
Q = Q.double();
match a_naf[i].cmp(&0) {
Ordering::Greater => {
Q = &Q + &table_A.select(a_naf[i] as usize);
}
Ordering::Less => {
Q = &Q - &table_A.select(-a_naf[i] as usize);
}
Ordering::Equal => {}
}
match b_naf[i].cmp(&0) {
Ordering::Greater => {
Q = &Q + &table_B.select(b_naf[i] as usize);
}
Ordering::Less => {
Q = &Q - &table_B.select(-b_naf[i] as usize);
}
Ordering::Equal => {}
}
if i == 0 {
break;
}
i -= 1;
}
Q.into()
}
}

View File

@ -0,0 +1,178 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Various constants, such as the Ristretto and Ed25519 basepoints.
#![allow(non_snake_case)]
use cfg_if::cfg_if;
use crate::edwards::CompressedEdwardsY;
use crate::montgomery::MontgomeryPoint;
use crate::ristretto::{CompressedRistretto, RistrettoPoint};
use crate::scalar::Scalar;
#[cfg(feature = "precomputed-tables")]
use crate::edwards::EdwardsBasepointTable;
cfg_if! {
if #[cfg(curve25519_dalek_backend = "fiat")] {
#[cfg(curve25519_dalek_bits = "32")]
pub use crate::backend::serial::fiat_u32::constants::*;
#[cfg(curve25519_dalek_bits = "64")]
pub use crate::backend::serial::fiat_u64::constants::*;
} else {
#[cfg(curve25519_dalek_bits = "32")]
pub use crate::backend::serial::u32::constants::*;
#[cfg(curve25519_dalek_bits = "64")]
pub use crate::backend::serial::u64::constants::*;
}
}
/// The Ed25519 basepoint, in `CompressedEdwardsY` format.
///
/// This is the little-endian byte encoding of \\( 4/5 \pmod p \\),
/// which is the \\(y\\)-coordinate of the Ed25519 basepoint.
///
/// The sign bit is 0 since the basepoint has \\(x\\) chosen to be positive.
pub const ED25519_BASEPOINT_COMPRESSED: CompressedEdwardsY = CompressedEdwardsY([
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
]);
/// The X25519 basepoint, in `MontgomeryPoint` format.
pub const X25519_BASEPOINT: MontgomeryPoint = MontgomeryPoint([
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
/// The Ristretto basepoint, in `CompressedRistretto` format.
pub const RISTRETTO_BASEPOINT_COMPRESSED: CompressedRistretto = CompressedRistretto([
0xe2, 0xf2, 0xae, 0x0a, 0x6a, 0xbc, 0x4e, 0x71, 0xa8, 0x84, 0xa9, 0x61, 0xc5, 0x00, 0x51, 0x5f,
0x58, 0xe3, 0x0b, 0x6a, 0xa5, 0x82, 0xdd, 0x8d, 0xb6, 0xa6, 0x59, 0x45, 0xe0, 0x8d, 0x2d, 0x76,
]);
/// The Ristretto basepoint, as a `RistrettoPoint`.
///
/// This is called `_POINT` to distinguish it from `_TABLE`, which
/// provides fast scalar multiplication.
pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT);
/// `BASEPOINT_ORDER` is the order of the Ristretto group and of the Ed25519 basepoint, i.e.,
/// $$
/// \ell = 2^\{252\} + 27742317777372353535851937790883648493.
/// $$
#[deprecated(since = "4.1.1", note = "Should not have been in public API")]
pub const BASEPOINT_ORDER: Scalar = BASEPOINT_ORDER_PRIVATE;
pub(crate) const BASEPOINT_ORDER_PRIVATE: Scalar = Scalar {
bytes: [
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde,
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10,
],
};
#[cfg(feature = "precomputed-tables")]
use crate::ristretto::RistrettoBasepointTable;
/// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication.
#[cfg(feature = "precomputed-tables")]
pub static RISTRETTO_BASEPOINT_TABLE: &RistrettoBasepointTable = unsafe {
// SAFETY: `RistrettoBasepointTable` is a `#[repr(transparent)]` newtype of
// `EdwardsBasepointTable`
&*(ED25519_BASEPOINT_TABLE as *const EdwardsBasepointTable as *const RistrettoBasepointTable)
};
#[cfg(test)]
mod test {
use crate::constants;
use crate::field::FieldElement;
use crate::traits::{IsIdentity, ValidityCheck};
#[test]
fn test_eight_torsion() {
for i in 0..8 {
let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(3);
assert!(Q.is_valid());
assert!(Q.is_identity());
}
}
#[test]
fn test_four_torsion() {
for i in (0..8).filter(|i| i % 2 == 0) {
let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(2);
assert!(Q.is_valid());
assert!(Q.is_identity());
}
}
#[test]
fn test_two_torsion() {
for i in (0..8).filter(|i| i % 4 == 0) {
let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(1);
assert!(Q.is_valid());
assert!(Q.is_identity());
}
}
/// Test that SQRT_M1 is the positive square root of -1
#[test]
fn test_sqrt_minus_one() {
let minus_one = FieldElement::MINUS_ONE;
let sqrt_m1_sq = &constants::SQRT_M1 * &constants::SQRT_M1;
assert_eq!(minus_one, sqrt_m1_sq);
assert!(bool::from(!constants::SQRT_M1.is_negative()));
}
#[test]
fn test_sqrt_constants_sign() {
let minus_one = FieldElement::MINUS_ONE;
let (was_nonzero_square, invsqrt_m1) = minus_one.invsqrt();
assert!(bool::from(was_nonzero_square));
let sign_test_sqrt = &invsqrt_m1 * &constants::SQRT_M1;
assert_eq!(sign_test_sqrt, minus_one);
}
/// Test that d = -121665/121666
#[test]
#[cfg(all(curve25519_dalek_bits = "32", not(curve25519_dalek_backend = "fiat")))]
fn test_d_vs_ratio() {
use crate::backend::serial::u32::field::FieldElement2625;
let a = -&FieldElement2625([121665, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let b = FieldElement2625([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let d = &a * &b.invert();
let d2 = &d + &d;
assert_eq!(d, constants::EDWARDS_D);
assert_eq!(d2, constants::EDWARDS_D2);
}
/// Test that d = -121665/121666
#[test]
#[cfg(all(curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))]
fn test_d_vs_ratio() {
use crate::backend::serial::u64::field::FieldElement51;
let a = -&FieldElement51([121665, 0, 0, 0, 0]);
let b = FieldElement51([121666, 0, 0, 0, 0]);
let d = &a * &b.invert();
let d2 = &d + &d;
assert_eq!(d, constants::EDWARDS_D);
assert_eq!(d2, constants::EDWARDS_D2);
}
#[test]
fn test_sqrt_ad_minus_one() {
let a = FieldElement::MINUS_ONE;
let ad_minus_one = &(&a * &constants::EDWARDS_D) + &a;
let should_be_ad_minus_one = constants::SQRT_AD_MINUS_ONE.square();
assert_eq!(should_be_ad_minus_one, ad_minus_one);
}
}

View File

@ -0,0 +1,25 @@
//! Build time diagnostics
// auto is assumed or selected
#[cfg(curve25519_dalek_backend = "auto")]
compile_error!("curve25519_dalek_backend is 'auto'");
// fiat was overriden
#[cfg(curve25519_dalek_backend = "fiat")]
compile_error!("curve25519_dalek_backend is 'fiat'");
// serial was assumed or overriden
#[cfg(curve25519_dalek_backend = "serial")]
compile_error!("curve25519_dalek_backend is 'serial'");
// simd was assumed over overriden
#[cfg(curve25519_dalek_backend = "simd")]
compile_error!("curve25519_dalek_backend is 'simd'");
// 32 bits target_pointer_width was assumed or overriden
#[cfg(curve25519_dalek_bits = "32")]
compile_error!("curve25519_dalek_bits is '32'");
// 64 bits target_pointer_width was assumed or overriden
#[cfg(curve25519_dalek_bits = "64")]
compile_error!("curve25519_dalek_bits is '64'");

File diff suppressed because it is too large Load Diff

View File

@ -23,56 +23,61 @@
//! Field operations defined in terms of other field operations, such as
//! field inversion or square roots, are defined here.
use core::cmp::{Eq, PartialEq};
#![allow(unused_qualifications)]
use cfg_if::cfg_if;
use subtle::ConditionallySelectable;
use subtle::ConditionallyNegatable;
use subtle::Choice;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use constants;
use backend;
use crate::backend;
use crate::constants;
#[cfg(feature = "fiat_u32_backend")]
pub use backend::serial::fiat_u32::field::*;
#[cfg(feature = "fiat_u64_backend")]
pub use backend::serial::fiat_u64::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
/// Using formally-verified field arithmetic from fiat-crypto
#[cfg(feature = "fiat_u32_backend")]
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
#[cfg(feature = "fiat_u64_backend")]
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
cfg_if! {
if #[cfg(curve25519_dalek_backend = "fiat")] {
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
///
/// Using formally-verified field arithmetic from fiat-crypto.
#[cfg(curve25519_dalek_bits = "32")]
pub(crate) type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
#[cfg(feature = "u64_backend")]
pub use backend::serial::u64::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
#[cfg(feature = "u64_backend")]
pub type FieldElement = backend::serial::u64::field::FieldElement51;
#[cfg(feature = "u32_backend")]
pub use backend::serial::u32::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
#[cfg(feature = "u32_backend")]
pub type FieldElement = backend::serial::u32::field::FieldElement2625;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
///
/// Using formally-verified field arithmetic from fiat-crypto.
#[cfg(curve25519_dalek_bits = "64")]
pub(crate) type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
} else if #[cfg(curve25519_dalek_bits = "64")] {
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
pub(crate) type FieldElement = backend::serial::u64::field::FieldElement51;
} else {
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
pub(crate) type FieldElement = backend::serial::u32::field::FieldElement2625;
}
}
impl Eq for FieldElement {}
impl PartialEq for FieldElement {
fn eq(&self, other: &FieldElement) -> bool {
self.ct_eq(other).unwrap_u8() == 1u8
self.ct_eq(other).into()
}
}
@ -81,7 +86,7 @@ impl ConstantTimeEq for FieldElement {
/// internal representation is not canonical, the field elements
/// are normalized to wire format before comparison.
fn ct_eq(&self, other: &FieldElement) -> Choice {
self.to_bytes().ct_eq(&other.to_bytes())
self.as_bytes().ct_eq(&other.as_bytes())
}
}
@ -93,8 +98,8 @@ impl FieldElement {
/// # Return
///
/// If negative, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_negative(&self) -> Choice {
let bytes = self.to_bytes();
pub(crate) fn is_negative(&self) -> Choice {
let bytes = self.as_bytes();
(bytes[0] & 1).into()
}
@ -103,15 +108,16 @@ impl FieldElement {
/// # Return
///
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_zero(&self) -> Choice {
pub(crate) fn is_zero(&self) -> Choice {
let zero = [0u8; 32];
let bytes = self.to_bytes();
let bytes = self.as_bytes();
bytes.ct_eq(&zero)
}
/// Compute (self^(2^250-1), self^11), used as a helper function
/// within invert() and pow22523().
#[rustfmt::skip] // keep alignment of explanatory comments
fn pow22501(&self) -> (FieldElement, FieldElement) {
// Instead of managing which temporary variables are used
// for what, we define as many as we need and leave stack
@ -148,30 +154,31 @@ impl FieldElement {
(t19, t3)
}
/// Given a slice of public `FieldElements`, replace each with its inverse.
/// Given a slice of pub(crate)lic `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]) {
pub(crate) fn batch_invert(inputs: &mut [FieldElement]) {
// Montgomerys Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater
// Section 3.2
let n = inputs.len();
let mut scratch = vec![FieldElement::one(); n];
let mut scratch = vec![FieldElement::ONE; n];
// Keep an accumulator of all of the previous products
let mut acc = FieldElement::one();
let mut acc = FieldElement::ONE;
// Pass through the input vector, recording the previous
// products in the scratch space
for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) {
*scratch = acc;
acc = &acc * input;
// acc <- acc * input, but skipping zeros (constant-time)
acc.conditional_assign(&(&acc * input), !input.is_zero());
}
// acc is nonzero iff all inputs are nonzero
assert_eq!(acc.is_zero().unwrap_u8(), 0);
// acc is nonzero because we skipped zeros in inputs
assert!(bool::from(!acc.is_zero()));
// Compute the inverse of all products
acc = acc.invert();
@ -180,8 +187,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);
}
}
@ -191,7 +201,9 @@ impl FieldElement {
/// x^(p-2)x = x^(p-1) = 1 (mod p).
///
/// This function returns zero on input zero.
pub fn invert(&self) -> FieldElement {
#[rustfmt::skip] // keep alignment of explanatory comments
#[allow(clippy::let_and_return)]
pub(crate) fn invert(&self) -> FieldElement {
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
//
// nonzero bits of exponent
@ -203,6 +215,8 @@ impl FieldElement {
}
/// Raise this field element to the power (p-5)/8 = 2^252 -3.
#[rustfmt::skip] // keep alignment of explanatory comments
#[allow(clippy::let_and_return)]
fn pow_p58(&self) -> FieldElement {
// The bits of (p-5)/8 are 101111.....11.
//
@ -226,7 +240,7 @@ impl FieldElement {
/// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero;
/// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square).
///
pub fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
pub(crate) fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
// Using the same trick as in ed25519 decoding, we merge the
// inversion, the square root, and the square test as follows.
//
@ -251,16 +265,16 @@ impl FieldElement {
//
// If v is zero, r is also zero.
let v3 = &v.square() * v;
let v3 = &v.square() * v;
let v7 = &v3.square() * v;
let mut r = &(u * &v3) * &(u * &v7).pow_p58();
let check = v * &r.square();
let i = &constants::SQRT_M1;
let correct_sign_sqrt = check.ct_eq( u);
let flipped_sign_sqrt = check.ct_eq( &(-u));
let flipped_sign_sqrt_i = check.ct_eq(&(&(-u)*i));
let correct_sign_sqrt = check.ct_eq(u);
let flipped_sign_sqrt = check.ct_eq(&(-u));
let flipped_sign_sqrt_i = check.ct_eq(&(&(-u) * i));
let r_prime = &constants::SQRT_M1 * &r;
r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i);
@ -286,45 +300,44 @@ impl FieldElement {
/// - `(Choice(0), zero) ` if `self` is zero;
/// - `(Choice(0), +sqrt(i/self)) ` if `self` is a nonzero nonsquare;
///
pub fn invsqrt(&self) -> (Choice, FieldElement) {
FieldElement::sqrt_ratio_i(&FieldElement::one(), self)
pub(crate) fn invsqrt(&self) -> (Choice, FieldElement) {
FieldElement::sqrt_ratio_i(&FieldElement::ONE, self)
}
}
#[cfg(test)]
mod test {
use field::*;
use subtle::ConditionallyNegatable;
use crate::field::*;
/// Random element a of GF(2^255-19), from Sage
/// a = 1070314506888354081329385823235218444233221\
/// 2228051251926706380353716438957572
static A_BYTES: [u8; 32] =
[ 0x04, 0xfe, 0xdf, 0x98, 0xa7, 0xfa, 0x0a, 0x68,
0x84, 0x92, 0xbd, 0x59, 0x08, 0x07, 0xa7, 0x03,
0x9e, 0xd1, 0xf6, 0xf2, 0xe1, 0xd9, 0xe2, 0xa4,
0xa4, 0x51, 0x47, 0x36, 0xf3, 0xc3, 0xa9, 0x17];
static A_BYTES: [u8; 32] = [
0x04, 0xfe, 0xdf, 0x98, 0xa7, 0xfa, 0x0a, 0x68, 0x84, 0x92, 0xbd, 0x59, 0x08, 0x07, 0xa7,
0x03, 0x9e, 0xd1, 0xf6, 0xf2, 0xe1, 0xd9, 0xe2, 0xa4, 0xa4, 0x51, 0x47, 0x36, 0xf3, 0xc3,
0xa9, 0x17,
];
/// Byte representation of a**2
static ASQ_BYTES: [u8; 32] =
[ 0x75, 0x97, 0x24, 0x9e, 0xe6, 0x06, 0xfe, 0xab,
0x24, 0x04, 0x56, 0x68, 0x07, 0x91, 0x2d, 0x5d,
0x0b, 0x0f, 0x3f, 0x1c, 0xb2, 0x6e, 0xf2, 0xe2,
0x63, 0x9c, 0x12, 0xba, 0x73, 0x0b, 0xe3, 0x62];
static ASQ_BYTES: [u8; 32] = [
0x75, 0x97, 0x24, 0x9e, 0xe6, 0x06, 0xfe, 0xab, 0x24, 0x04, 0x56, 0x68, 0x07, 0x91, 0x2d,
0x5d, 0x0b, 0x0f, 0x3f, 0x1c, 0xb2, 0x6e, 0xf2, 0xe2, 0x63, 0x9c, 0x12, 0xba, 0x73, 0x0b,
0xe3, 0x62,
];
/// Byte representation of 1/a
static AINV_BYTES: [u8; 32] =
[0x96, 0x1b, 0xcd, 0x8d, 0x4d, 0x5e, 0xa2, 0x3a,
0xe9, 0x36, 0x37, 0x93, 0xdb, 0x7b, 0x4d, 0x70,
0xb8, 0x0d, 0xc0, 0x55, 0xd0, 0x4c, 0x1d, 0x7b,
0x90, 0x71, 0xd8, 0xe9, 0xb6, 0x18, 0xe6, 0x30];
static AINV_BYTES: [u8; 32] = [
0x96, 0x1b, 0xcd, 0x8d, 0x4d, 0x5e, 0xa2, 0x3a, 0xe9, 0x36, 0x37, 0x93, 0xdb, 0x7b, 0x4d,
0x70, 0xb8, 0x0d, 0xc0, 0x55, 0xd0, 0x4c, 0x1d, 0x7b, 0x90, 0x71, 0xd8, 0xe9, 0xb6, 0x18,
0xe6, 0x30,
];
/// Byte representation of a^((p-5)/8)
static AP58_BYTES: [u8; 32] =
[0x6a, 0x4f, 0x24, 0x89, 0x1f, 0x57, 0x60, 0x36,
0xd0, 0xbe, 0x12, 0x3c, 0x8f, 0xf5, 0xb1, 0x59,
0xe0, 0xf0, 0xb8, 0x1b, 0x20, 0xd2, 0xb5, 0x1f,
0x15, 0x21, 0xf9, 0xe3, 0xe1, 0x61, 0x21, 0x55];
static AP58_BYTES: [u8; 32] = [
0x6a, 0x4f, 0x24, 0x89, 0x1f, 0x57, 0x60, 0x36, 0xd0, 0xbe, 0x12, 0x3c, 0x8f, 0xf5, 0xb1,
0x59, 0xe0, 0xf0, 0xb8, 0x1b, 0x20, 0xd2, 0xb5, 0x1f, 0x15, 0x21, 0xf9, 0xe3, 0xe1, 0x61,
0x21, 0x55,
];
#[test]
fn a_mul_a_vs_a_squared_constant() {
@ -344,82 +357,84 @@ mod test {
fn a_square2_vs_a_squared_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
assert_eq!(a.square2(), &asq+&asq);
assert_eq!(a.square2(), &asq + &asq);
}
#[test]
fn a_invert_vs_inverse_of_a_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let a = FieldElement::from_bytes(&A_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
let should_be_inverse = a.invert();
assert_eq!(ainv, should_be_inverse);
assert_eq!(FieldElement::one(), &a * &should_be_inverse);
assert_eq!(FieldElement::ONE, &a * &should_be_inverse);
}
#[test]
#[cfg(feature = "alloc")]
fn batch_invert_a_matches_nonbatched() {
let a = FieldElement::from_bytes(&A_BYTES);
let a = FieldElement::from_bytes(&A_BYTES);
let ap58 = FieldElement::from_bytes(&AP58_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
let a2 = &a + &a;
let a_list = vec![a, ap58, asq, ainv, a2];
let a0 = &a - &a;
let a2 = &a + &a;
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]);
}
}
#[test]
fn sqrt_ratio_behavior() {
let zero = FieldElement::zero();
let one = FieldElement::one();
let zero = FieldElement::ZERO;
let one = FieldElement::ONE;
let i = constants::SQRT_M1;
let two = &one + &one; // 2 is nonsquare mod p.
let four = &two + &two; // 4 is square mod p.
// 0/0 should return (1, 0) since u is 0
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&zero, &zero);
assert_eq!(choice.unwrap_u8(), 1);
assert!(bool::from(choice));
assert_eq!(sqrt, zero);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
assert!(bool::from(!sqrt.is_negative()));
// 1/0 should return (0, 0) since v is 0, u is nonzero
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &zero);
assert_eq!(choice.unwrap_u8(), 0);
assert!(bool::from(!choice));
assert_eq!(sqrt, zero);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
assert!(bool::from(!sqrt.is_negative()));
// 2/1 is nonsquare, so we expect (0, sqrt(i*2))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&two, &one);
assert_eq!(choice.unwrap_u8(), 0);
assert!(bool::from(!choice));
assert_eq!(sqrt.square(), &two * &i);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
assert!(bool::from(!sqrt.is_negative()));
// 4/1 is square, so we expect (1, sqrt(4))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&four, &one);
assert_eq!(choice.unwrap_u8(), 1);
assert!(bool::from(choice));
assert_eq!(sqrt.square(), four);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
assert!(bool::from(!sqrt.is_negative()));
// 1/4 is square, so we expect (1, 1/sqrt(4))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &four);
assert_eq!(choice.unwrap_u8(), 1);
assert!(bool::from(choice));
assert_eq!(&sqrt.square() * &four, one);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
assert!(bool::from(!sqrt.is_negative()));
}
#[test]
fn a_p58_vs_ap58_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let a = FieldElement::from_bytes(&A_BYTES);
let ap58 = FieldElement::from_bytes(&AP58_BYTES);
assert_eq!(ap58, a.pow_p58());
}
#[test]
fn equality() {
let a = FieldElement::from_bytes(&A_BYTES);
let a = FieldElement::from_bytes(&A_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
assert!(a == a);
assert!(a != ainv);
@ -427,25 +442,24 @@ mod test {
/// Notice that the last element has the high bit set, which
/// should be ignored
static B_BYTES: [u8;32] =
[113, 191, 169, 143, 91, 234, 121, 15,
241, 131, 217, 36, 230, 101, 92, 234,
8, 208, 170, 251, 97, 127, 70, 210,
58, 23, 166, 87, 240, 169, 184, 178];
static B_BYTES: [u8; 32] = [
113, 191, 169, 143, 91, 234, 121, 15, 241, 131, 217, 36, 230, 101, 92, 234, 8, 208, 170,
251, 97, 127, 70, 210, 58, 23, 166, 87, 240, 169, 184, 178,
];
#[test]
fn from_bytes_highbit_is_ignored() {
let mut cleared_bytes = B_BYTES;
cleared_bytes[31] &= 127u8;
let with_highbit_set = FieldElement::from_bytes(&B_BYTES);
let with_highbit_set = FieldElement::from_bytes(&B_BYTES);
let without_highbit_set = FieldElement::from_bytes(&cleared_bytes);
assert_eq!(without_highbit_set, with_highbit_set);
}
#[test]
fn conditional_negate() {
let one = FieldElement::one();
let minus_one = FieldElement::minus_one();
let one = FieldElement::ONE;
let minus_one = FieldElement::MINUS_ONE;
let mut x = one;
x.conditional_negate(Choice::from(1));
assert_eq!(x, minus_one);
@ -458,18 +472,23 @@ mod test {
#[test]
fn encoding_is_canonical() {
// Encode 1 wrongly as 1 + (2^255 - 19) = 2^255 - 18
let one_encoded_wrongly_bytes: [u8;32] = [0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f];
let one_encoded_wrongly_bytes: [u8; 32] = [
0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x7f,
];
// Decode to a field element
let one = FieldElement::from_bytes(&one_encoded_wrongly_bytes);
// .. then check that the encoding is correct
let one_bytes = one.to_bytes();
let one_bytes = one.as_bytes();
assert_eq!(one_bytes[0], 1);
for i in 1..32 {
assert_eq!(one_bytes[i], 0);
for byte in &one_bytes[1..] {
assert_eq!(*byte, 0);
}
}
#[test]
#[cfg(feature = "alloc")]
fn batch_invert_empty() {
FieldElement::batch_invert(&mut []);
}

117
curve25519-dalek/src/lib.rs Normal file
View File

@ -0,0 +1,117 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
#![no_std]
#![cfg_attr(
all(
curve25519_dalek_backend = "simd",
nightly,
any(target_arch = "x86", target_arch = "x86_64")
),
feature(stdarch_x86_avx512)
)]
#![cfg_attr(
all(curve25519_dalek_backend = "simd", nightly),
feature(avx512_target_feature)
)]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
//------------------------------------------------------------------------
// Documentation:
//------------------------------------------------------------------------
#![doc(
html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png"
)]
#![doc = include_str!("../README.md")]
//------------------------------------------------------------------------
// Linting:
//------------------------------------------------------------------------
#![cfg_attr(allow_unused_unsafe, allow(unused_unsafe))]
#![warn(
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]
// Requires MSRV 1.77 as it does not allow build.rs gating
#![allow(unexpected_cfgs)]
//------------------------------------------------------------------------
// External dependencies:
//------------------------------------------------------------------------
#[cfg(feature = "alloc")]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
// TODO: move std-dependent tests to `tests/`
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(feature = "digest")]
pub use digest;
// Internal macros. Must come first!
#[macro_use]
pub(crate) mod macros;
//------------------------------------------------------------------------
// curve25519-dalek public modules
//------------------------------------------------------------------------
// Scalar arithmetic mod l = 2^252 + ..., the order of the Ristretto group
pub mod scalar;
// Point operations on the Montgomery form of Curve25519
pub mod montgomery;
// Point operations on the Edwards form of Curve25519
pub mod edwards;
// Group operations on the Ristretto group
pub mod ristretto;
// Useful constants, like the Ed25519 basepoint
pub mod constants;
// External (and internal) traits.
pub mod traits;
// All the lizard code is here, for now
#[cfg(feature = "lizard")]
pub mod lizard;
//------------------------------------------------------------------------
// curve25519-dalek internal modules
//------------------------------------------------------------------------
// Finite field arithmetic mod p = 2^255 - 19
pub mod field;
// Arithmetic backends (using u32, u64, etc) live here
#[cfg(docsrs)]
pub mod backend;
#[cfg(not(docsrs))]
pub(crate) mod backend;
// Generic code for window lookups
pub(crate) mod window;
pub use crate::{
edwards::EdwardsPoint, montgomery::MontgomeryPoint, ristretto::RistrettoPoint, scalar::Scalar,
};
// Build time diagnostics for validation
#[cfg(curve25519_dalek_diagnostics = "build")]
mod diagnostics;

View File

@ -3,15 +3,14 @@
#![allow(non_snake_case)]
use subtle::Choice;
use subtle::ConstantTimeEq;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use constants;
use lizard::lizard_constants;
use field::FieldElement;
use super::lizard_constants;
use crate::constants;
use crate::field::FieldElement;
/// Represents a point (s,t) on the the Jacobi quartic associated
/// to the Edwards curve.
@ -29,18 +28,18 @@ impl JacobiPoint {
/// This function computes a field element that is mapped to a given (s,t)
/// with Elligator2 if it exists.
pub(crate) fn elligator_inv(&self) -> (Choice, FieldElement) {
let mut out = FieldElement::zero();
let mut out = FieldElement::ZERO;
// Special case: s = 0. If s is zero, either t = 1 or t = -1.
// If t=1, then sqrt(i*d) is the preimage. Otherwise it's 0.
let s_is_zero = self.S.is_zero();
let t_equals_one = self.T.ct_eq(&FieldElement::one());
let t_equals_one = self.T.ct_eq(&FieldElement::ONE);
out.conditional_assign(&lizard_constants::SQRT_ID, t_equals_one);
let mut ret = s_is_zero;
let mut done = s_is_zero;
// a := (t+1) (d+1)/(d-1)
let a = &(&self.T + &FieldElement::one()) * &lizard_constants::DP1_OVER_DM1;
let a = &(&self.T + &FieldElement::ONE) * &lizard_constants::DP1_OVER_DM1;
let a2 = a.square();
// y := 1/sqrt(i (s^4 - a^2)).
@ -71,4 +70,3 @@ impl JacobiPoint {
}
}
}

View File

@ -2,35 +2,34 @@
//!
//! Could be moved into backend/serial/u??/constants.rs
#[cfg(feature = "u64_backend")]
pub(crate) use lizard::u64_constants::*;
#[cfg(feature = "u32_backend")]
pub(crate) use lizard::u32_constants::*;
#[cfg(curve25519_dalek_bits = "64")]
pub(crate) use super::u64_constants::*;
#[cfg(curve25519_dalek_bits = "32")]
pub(crate) use super::u32_constants::*;
// ------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------
#[cfg(all(test, feature = "stage2_build"))]
#[cfg(test)]
mod test {
use super::*;
use constants;
use field::FieldElement;
use crate::constants;
use crate::field::FieldElement;
#[test]
fn test_lizard_constants() {
let (_, sqrt_id) = FieldElement::sqrt_ratio_i(
let (_, sqrt_id) = FieldElement::sqrt_ratio_i(
&(&constants::SQRT_M1 * &constants::EDWARDS_D),
&FieldElement::one()
&FieldElement::ONE,
);
assert_eq!(sqrt_id, SQRT_ID);
assert_eq!(
&(&constants::EDWARDS_D + &FieldElement::one())
* &(&constants::EDWARDS_D - &FieldElement::one()).invert(),
&(&constants::EDWARDS_D + &FieldElement::ONE)
* &(&constants::EDWARDS_D - &FieldElement::ONE).invert(),
DP1_OVER_DM1
);
@ -44,11 +43,7 @@ mod test {
&MDOUBLE_INVSQRT_A_MINUS_D * &constants::SQRT_M1
);
let (_, invsqrt_one_plus_d) = (
&constants::EDWARDS_D + &FieldElement::one()).invsqrt();
assert_eq!(
-&invsqrt_one_plus_d,
MINVSQRT_ONE_PLUS_D
);
let (_, invsqrt_one_plus_d) = (&constants::EDWARDS_D + &FieldElement::ONE).invsqrt();
assert_eq!(-&invsqrt_one_plus_d, MINVSQRT_ONE_PLUS_D);
}
}

View File

@ -2,37 +2,37 @@
#![allow(non_snake_case)]
use digest::Digest;
use digest::generic_array::typenum::U32;
use digest::Digest;
use constants;
use field::FieldElement;
use crate::constants;
use crate::field::FieldElement;
use subtle::Choice;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use subtle::Choice;
use edwards::EdwardsPoint;
use crate::edwards::EdwardsPoint;
use lizard::jacobi_quartic::JacobiPoint;
use lizard::lizard_constants;
use super::jacobi_quartic::JacobiPoint;
use super::lizard_constants;
use crate::ristretto::RistrettoPoint;
#[allow(unused_imports)]
use prelude::*;
use ristretto::RistrettoPoint;
use core::prelude::*;
impl RistrettoPoint {
/// Directly encode 253 bits as a RistrettoPoint, using Elligator
pub fn from_uniform_bytes_single_elligator(bytes: &[u8; 32]) -> RistrettoPoint {
RistrettoPoint::elligator_ristretto_flavor(&FieldElement::from_bytes(&bytes))
RistrettoPoint::elligator_ristretto_flavor(&FieldElement::from_bytes(bytes))
}
/// Encode 16 bytes of data to a RistrettoPoint, using the Lizard method
pub fn lizard_encode<D: Digest>(data: &[u8; 16]) -> RistrettoPoint
where D: Digest<OutputSize = U32>
where
D: Digest<OutputSize = U32>,
{
let mut fe_bytes: [u8;32] = Default::default();
let mut fe_bytes: [u8; 32] = Default::default();
let digest = D::digest(data);
fe_bytes[0..32].copy_from_slice(digest.as_slice());
@ -45,36 +45,35 @@ impl RistrettoPoint {
/// Decode 16 bytes of data from a RistrettoPoint, using the Lizard method
pub fn lizard_decode<D: Digest>(&self) -> Option<[u8; 16]>
where D: Digest<OutputSize = U32>
where
D: Digest<OutputSize = U32>,
{
let mut result: [u8; 16] = Default::default();
let mut h: [u8;32] = Default::default();
let mut h: [u8; 32] = Default::default();
let (mask, fes) = self.elligator_ristretto_flavor_inverse();
let mut n_found = 0;
for j in 0..8 {
for (j, fe_j) in fes.iter().enumerate() {
let mut ok = Choice::from((mask >> j) & 1);
let buf2 = fes[j].to_bytes(); // array
let buf2 = fe_j.as_bytes(); // array
h.copy_from_slice(&D::digest(&buf2[8..24])); // array
h[8..24].copy_from_slice(&buf2[8..24]);
h[0] &= 254;
h[31] &= 63;
ok &= h.ct_eq(&buf2);
for i in 0..16 {
result[i] = u8::conditional_select(&result[i], &buf2[8+i], ok);
result[i] = u8::conditional_select(&result[i], &buf2[8 + i], ok);
}
n_found += ok.unwrap_u8();
}
if n_found == 1 {
return Some(result);
}
else {
return None;
Some(result)
} else {
None
}
}
/// Directly encode 253 bits as a RistrettoPoint, using Elligator
pub fn encode_253_bits(data: &[u8; 32]) -> Option<RistrettoPoint>
{
pub fn encode_253_bits(data: &[u8; 32]) -> Option<RistrettoPoint> {
if data.len() != 32 {
return None;
}
@ -85,23 +84,23 @@ impl RistrettoPoint {
}
/// Directly decode a RistrettoPoint as 253 bits, using Elligator
pub fn decode_253_bits(&self) -> (u8, [[u8; 32]; 8])
{
let mut ret = [ [0u8; 32]; 8];
pub fn decode_253_bits(&self) -> (u8, [[u8; 32]; 8]) {
let mut ret = [[0u8; 32]; 8];
let (mask, fes) = self.elligator_ristretto_flavor_inverse();
for j in 0..8 {
ret[j] = fes[j].to_bytes();
ret[j] = fes[j].as_bytes();
}
(mask, ret)
}
/// Return the coset self + E[4], for debugging.
pub fn xcoset4(&self) -> [EdwardsPoint; 4] {
[ self.0
, &self.0 + &constants::EIGHT_TORSION[2]
, &self.0 + &constants::EIGHT_TORSION[4]
, &self.0 + &constants::EIGHT_TORSION[6]
[
self.0,
self.0 + constants::EIGHT_TORSION[2],
self.0 + constants::EIGHT_TORSION[4],
self.0 + constants::EIGHT_TORSION[6],
]
}
@ -130,26 +129,26 @@ impl RistrettoPoint {
// at the same time. The four Jacobi quartic points are two of
// such pairs.
let mut mask : u8 = 0;
let mut mask: u8 = 0;
let jcs = self.to_jacobi_quartic_ristretto();
let mut ret = [FieldElement::one(); 8];
let mut ret = [FieldElement::ONE; 8];
for i in 0..4 {
let (ok, fe) = jcs[i].elligator_inv();
let mut tmp : u8 = 0;
ret[2*i] = fe;
let mut tmp: u8 = 0;
ret[2 * i] = fe;
tmp.conditional_assign(&1, ok);
mask |= tmp << (2 * i);
let jc = jcs[i].dual();
let (ok, fe) = jc.elligator_inv();
let mut tmp : u8 = 0;
ret[2*i+1] = fe;
let mut tmp: u8 = 0;
ret[2 * i + 1] = fe;
tmp.conditional_assign(&1, ok);
mask |= tmp << (2 * i + 1);
}
return (mask, ret)
(mask, ret)
}
/// Find a point on the Jacobi quartic associated to each of the four
@ -157,20 +156,20 @@ impl RistrettoPoint {
///
/// There is one exception: for (0,-1) there is no point on the quartic and
/// so we repeat one on the quartic equivalent to (0,1).
fn to_jacobi_quartic_ristretto(&self) -> [JacobiPoint; 4] {
let x2 = self.0.X.square(); // X^2
let y2 = self.0.Y.square(); // Y^2
let y4 = y2.square(); // Y^4
let z2 = self.0.Z.square(); // Z^2
let z_min_y = &self.0.Z - &self.0.Y; // Z - Y
let z_pl_y = &self.0.Z + &self.0.Y; // Z + Y
let z2_min_y2 = &z2 - &y2; // Z^2 - Y^2
fn to_jacobi_quartic_ristretto(self) -> [JacobiPoint; 4] {
let x2 = self.0.X.square(); // X^2
let y2 = self.0.Y.square(); // Y^2
let y4 = y2.square(); // Y^4
let z2 = self.0.Z.square(); // Z^2
let z_min_y = &self.0.Z - &self.0.Y; // Z - Y
let z_pl_y = &self.0.Z + &self.0.Y; // Z + Y
let z2_min_y2 = &z2 - &y2; // Z^2 - Y^2
// gamma := 1/sqrt( Y^4 X^2 (Z^2 - Y^2) )
let (_, gamma) = (&(&y4 * &x2) * &z2_min_y2).invsqrt();
let den = &gamma * &y2;
let s_over_x = &den * &z_min_y;
let sp_over_xp = &den * &z_pl_y;
@ -187,9 +186,9 @@ impl RistrettoPoint {
let den = &(&(-(&z2_min_y2)) * &lizard_constants::MINVSQRT_ONE_PLUS_D) * &gamma;
// Same as before but with the substitution (X, Y, Z) = (Y, X, i*Z)
let iz = &constants::SQRT_M1 * &self.0.Z; // iZ
let iz_min_x = &iz - &self.0.X; // iZ - X
let iz_pl_x = &iz + &self.0.X; // iZ + X
let iz = &constants::SQRT_M1 * &self.0.Z; // iZ
let iz_min_x = &iz - &self.0.X; // iZ - X
let iz_pl_x = &iz + &self.0.X; // iZ + X
let s_over_y = &den * &iz_min_x;
let sp_over_yp = &den * &iz_pl_x;
@ -209,18 +208,24 @@ impl RistrettoPoint {
//
// Note that if X=0 or Y=0, then s_i = t_i = 0.
let x_or_y_is_zero = self.0.X.is_zero() | self.0.Y.is_zero();
t0.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
t1.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
t2.conditional_assign(&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D, x_or_y_is_zero);
t3.conditional_assign(&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D, x_or_y_is_zero);
s2.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
s3.conditional_assign(&(-(&FieldElement::one())), x_or_y_is_zero);
t0.conditional_assign(&FieldElement::ONE, x_or_y_is_zero);
t1.conditional_assign(&FieldElement::ONE, x_or_y_is_zero);
t2.conditional_assign(
&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D,
x_or_y_is_zero,
);
t3.conditional_assign(
&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D,
x_or_y_is_zero,
);
s2.conditional_assign(&FieldElement::ONE, x_or_y_is_zero);
s3.conditional_assign(&(-(&FieldElement::ONE)), x_or_y_is_zero);
return [
JacobiPoint{S: s0, T: t0},
JacobiPoint{S: s1, T: t1},
JacobiPoint{S: s2, T: t2},
JacobiPoint{S: s3, T: t3},
[
JacobiPoint { S: s0, T: t0 },
JacobiPoint { S: s1, T: t1 },
JacobiPoint { S: s2, T: t2 },
JacobiPoint { S: s3, T: t3 },
]
}
}
@ -229,37 +234,58 @@ impl RistrettoPoint {
// Tests
// ------------------------------------------------------------------------
#[cfg(all(test, feature = "stage2_build"))]
#[cfg(test)]
mod test {
extern crate sha2;
use sha2;
use self::sha2::Sha256;
use super::*;
use crate::ristretto::CompressedRistretto;
use rand_core::RngCore;
#[cfg(feature = "rand")]
use rand_os::OsRng;
use rand_core::{RngCore};
use self::sha2::{Sha256};
use ristretto::CompressedRistretto;
use super::*;
fn test_lizard_encode_helper(data: &[u8; 16], result: &[u8; 32]) {
let p = RistrettoPoint::lizard_encode::<Sha256>(data).unwrap();
let p = RistrettoPoint::lizard_encode::<Sha256>(data);
let p_bytes = p.compress().to_bytes();
assert!(&p_bytes == result);
let p = CompressedRistretto::from_slice(&p_bytes).decompress().unwrap();
let p = CompressedRistretto::from_slice(&p_bytes)
.unwrap()
.decompress()
.unwrap();
let data_out = p.lizard_decode::<Sha256>().unwrap();
assert!(&data_out == data);
}
#[test]
fn test_lizard_encode() {
test_lizard_encode_helper(&[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
&[0xf0, 0xb7, 0xe3, 0x44, 0x84, 0xf7, 0x4c, 0xf0, 0xf, 0x15, 0x2, 0x4b, 0x73, 0x85, 0x39, 0x73, 0x86, 0x46, 0xbb, 0xbe, 0x1e, 0x9b, 0xc7, 0x50, 0x9a, 0x67, 0x68, 0x15, 0x22, 0x7e, 0x77, 0x4f]);
test_lizard_encode_helper(
&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
&[
0xf0, 0xb7, 0xe3, 0x44, 0x84, 0xf7, 0x4c, 0xf0, 0xf, 0x15, 0x2, 0x4b, 0x73, 0x85,
0x39, 0x73, 0x86, 0x46, 0xbb, 0xbe, 0x1e, 0x9b, 0xc7, 0x50, 0x9a, 0x67, 0x68, 0x15,
0x22, 0x7e, 0x77, 0x4f,
],
);
test_lizard_encode_helper(&[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
&[0xcc, 0x92, 0xe8, 0x1f, 0x58, 0x5a, 0xfc, 0x5c, 0xaa, 0xc8, 0x86, 0x60, 0xd8, 0xd1, 0x7e, 0x90, 0x25, 0xa4, 0x44, 0x89, 0xa3, 0x63, 0x4, 0x21, 0x23, 0xf6, 0xaf, 0x7, 0x2, 0x15, 0x6e, 0x65]);
test_lizard_encode_helper(
&[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
&[
0xcc, 0x92, 0xe8, 0x1f, 0x58, 0x5a, 0xfc, 0x5c, 0xaa, 0xc8, 0x86, 0x60, 0xd8, 0xd1,
0x7e, 0x90, 0x25, 0xa4, 0x44, 0x89, 0xa3, 0x63, 0x4, 0x21, 0x23, 0xf6, 0xaf, 0x7,
0x2, 0x15, 0x6e, 0x65,
],
);
test_lizard_encode_helper(&[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
&[0xc8, 0x30, 0x57, 0x3f, 0x8a, 0x8e, 0x77, 0x78, 0x67, 0x1f, 0x76, 0xcd, 0xc7, 0x96, 0xdc, 0xa, 0x23, 0x5c, 0xf1, 0x77, 0xf1, 0x97, 0xd9, 0xfc, 0xba, 0x6, 0xe8, 0x4e, 0x96, 0x24, 0x74, 0x44]);
test_lizard_encode_helper(
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
&[
0xc8, 0x30, 0x57, 0x3f, 0x8a, 0x8e, 0x77, 0x78, 0x67, 0x1f, 0x76, 0xcd, 0xc7, 0x96,
0xdc, 0xa, 0x23, 0x5c, 0xf1, 0x77, 0xf1, 0x97, 0xd9, 0xfc, 0xba, 0x6, 0xe8, 0x4e,
0x96, 0x24, 0x74, 0x44,
],
);
}
#[test]
@ -274,15 +300,16 @@ mod test {
fe_bytes = [0u8; 32];
} else if i == 1 {
// Test for second corner-case: fe = +sqrt(i*d)
fe_bytes = [168, 27, 92, 74, 203, 42, 48, 117, 170, 109, 234,
14, 45, 169, 188, 205, 21, 110, 235, 115, 153, 84,
52, 117, 151, 235, 123, 244, 88, 85, 179, 5];
fe_bytes = [
168, 27, 92, 74, 203, 42, 48, 117, 170, 109, 234, 14, 45, 169, 188, 205, 21,
110, 235, 115, 153, 84, 52, 117, 151, 235, 123, 244, 88, 85, 179, 5,
];
} else {
// For the rest, just generate a random field element to test.
rng.fill_bytes(&mut fe_bytes);
}
fe_bytes[0] &= 254; // positive
fe_bytes[31] &= 127; // < 2^255-19
fe_bytes[0] &= 254; // positive
fe_bytes[31] &= 127; // < 2^255-19
let fe = FieldElement::from_bytes(&fe_bytes);
let pt = RistrettoPoint::elligator_ristretto_flavor(&fe);
@ -290,10 +317,10 @@ mod test {
let (mask, fes) = RistrettoPoint(*pt2).elligator_ristretto_flavor_inverse();
let mut found = false;
for j in 0..8 {
for (j, fe_j) in fes.iter().enumerate() {
if mask & (1 << j) != 0 {
assert_eq!(RistrettoPoint::elligator_ristretto_flavor(&fes[j]), pt);
if fes[j] == fe {
assert_eq!(RistrettoPoint::elligator_ristretto_flavor(fe_j), pt);
if *fe_j == fe {
found = true;
}
}
@ -303,4 +330,3 @@ mod test {
}
}
}

View File

@ -2,12 +2,12 @@
#![allow(non_snake_case)]
#[cfg(feature = "u32_backend")]
#[cfg(curve25519_dalek_bits = "32")]
mod u32_constants;
#[cfg(feature = "u64_backend")]
#[cfg(curve25519_dalek_bits = "64")]
mod u64_constants;
pub mod lizard_constants;
pub mod jacobi_quartic;
pub mod lizard_constants;
pub mod lizard_ristretto;

View File

@ -0,0 +1,46 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(curve25519_dalek_backend = "fiat")] {
pub use crate::backend::serial::fiat_u32::field::FieldElement2625;
const fn field_element(element: [u32; 10]) -> FieldElement2625 {
FieldElement2625(fiat_crypto::curve25519_32::fiat_25519_tight_field_element(element))
}
} else {
pub use crate::backend::serial::u32::field::FieldElement2625;
const fn field_element(element: [u32; 10]) -> FieldElement2625 {
FieldElement2625(element)
}
}
}
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
pub const SQRT_ID: FieldElement2625 = field_element([
39590824, 701138, 28659366, 23623507, 53932708, 32206357, 36326585, 24309414, 26167230, 1494357,
]);
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
pub const DP1_OVER_DM1: FieldElement2625 = field_element([
58833708, 32184294, 62457071, 26110240, 19032991, 27203620, 7122892, 18068959, 51019405,
3776288,
]);
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([
54885894, 25242303, 55597453, 9067496, 51808079, 33312638, 25456129, 14121551, 54921728,
3972023,
]);
/// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters
/// and `i = +sqrt(-1)`.
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([
58178520, 23970840, 26444491, 29801899, 41064376, 743696, 2900628, 27920316, 41968995, 5270573,
]);
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
pub const MINVSQRT_ONE_PLUS_D: FieldElement2625 = field_element([
38019585, 4791795, 20332186, 18653482, 46576675, 33182583, 65658549, 2817057, 12569934,
30919145,
]);

View File

@ -0,0 +1,63 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(curve25519_dalek_backend = "fiat")] {
pub use crate::backend::serial::fiat_u64::field::FieldElement51;
const fn field_element(element: [u64; 5]) -> FieldElement51 {
FieldElement51(fiat_crypto::curve25519_64::fiat_25519_tight_field_element(element))
}
} else {
pub use crate::backend::serial::u64::field::FieldElement51;
const fn field_element(element: [u64; 5]) -> FieldElement51 {
FieldElement51(element)
}
}
}
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
pub const SQRT_ID: FieldElement51 = field_element([
2298852427963285,
3837146560810661,
4413131899466403,
3883177008057528,
2352084440532925,
]);
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
pub const DP1_OVER_DM1: FieldElement51 = field_element([
2159851467815724,
1752228607624431,
1825604053920671,
1212587319275468,
253422448836237,
]);
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([
1693982333959686,
608509411481997,
2235573344831311,
947681270984193,
266558006233600,
]);
/// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters
/// and `i = +sqrt(-1)`.
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([
1608655899704280,
1999971613377227,
49908634785720,
1873700692181652,
353702208628067,
]);
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = field_element([
321571956990465,
1251814006996634,
2226845496292387,
189049560751797,
2074948709371214,
]);

View File

@ -34,7 +34,7 @@ macro_rules! define_add_variants {
&self + &rhs
}
}
}
};
}
/// Define non-borrow variants of `AddAssign`.
@ -45,7 +45,7 @@ macro_rules! define_add_assign_variants {
*self += &rhs;
}
}
}
};
}
/// Define borrow and non-borrow variants of `Sub`.
@ -71,7 +71,7 @@ macro_rules! define_sub_variants {
&self - &rhs
}
}
}
};
}
/// Define non-borrow variants of `SubAssign`.
@ -82,7 +82,7 @@ macro_rules! define_sub_assign_variants {
*self -= &rhs;
}
}
}
};
}
/// Define borrow and non-borrow variants of `Mul`.
@ -108,7 +108,7 @@ macro_rules! define_mul_variants {
&self * &rhs
}
}
}
};
}
/// Define non-borrow variants of `MulAssign`.
@ -119,6 +119,5 @@ macro_rules! define_mul_assign_variants {
*self *= &rhs;
}
}
}
};
}

View File

@ -49,24 +49,28 @@
// affine and projective cakes and eat both of them too.
#![allow(non_snake_case)]
use core::ops::{Mul, MulAssign};
use core::{
hash::{Hash, Hasher},
ops::{Mul, MulAssign},
};
use constants::{APLUS2_OVER_FOUR, MONTGOMERY_A, MONTGOMERY_A_NEG};
use edwards::{CompressedEdwardsY, EdwardsPoint};
use field::FieldElement;
use scalar::Scalar;
use crate::constants::{APLUS2_OVER_FOUR, MONTGOMERY_A, MONTGOMERY_A_NEG};
use crate::edwards::{CompressedEdwardsY, EdwardsPoint};
use crate::field::FieldElement;
use crate::scalar::{clamp_integer, Scalar};
use traits::Identity;
use crate::traits::Identity;
use subtle::Choice;
use subtle::ConstantTimeEq;
use subtle::{ConditionallyNegatable, ConditionallySelectable};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// Holds the \\(u\\)-coordinate of a point on the Montgomery form of
/// Curve25519 or its twist.
#[derive(Copy, Clone, Debug, Hash)]
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MontgomeryPoint(pub [u8; 32]);
@ -80,20 +84,25 @@ impl ConstantTimeEq for MontgomeryPoint {
}
}
impl Default for MontgomeryPoint {
fn default() -> MontgomeryPoint {
MontgomeryPoint([0u8; 32])
}
}
impl PartialEq for MontgomeryPoint {
fn eq(&self, other: &MontgomeryPoint) -> bool {
self.ct_eq(other).unwrap_u8() == 1u8
self.ct_eq(other).into()
}
}
impl Eq for MontgomeryPoint {}
// Equal MontgomeryPoints must hash to the same value. So we have to get them into a canonical
// encoding first
impl Hash for MontgomeryPoint {
fn hash<H: Hasher>(&self, state: &mut H) {
// Do a round trip through a `FieldElement`. `as_bytes` is guaranteed to give a canonical
// 32-byte encoding
let canonical_bytes = FieldElement::from_bytes(&self.0).as_bytes();
canonical_bytes.hash(state);
}
}
impl Identity for MontgomeryPoint {
/// Return the group identity element, which has order 4.
fn identity() -> MontgomeryPoint {
@ -101,6 +110,7 @@ impl Identity for MontgomeryPoint {
}
}
#[cfg(feature = "zeroize")]
impl Zeroize for MontgomeryPoint {
fn zeroize(&mut self) {
self.0.zeroize();
@ -108,13 +118,83 @@ impl Zeroize for MontgomeryPoint {
}
impl MontgomeryPoint {
/// Fixed-base scalar multiplication (i.e. multiplication by the base point).
pub fn mul_base(scalar: &Scalar) -> Self {
EdwardsPoint::mul_base(scalar).to_montgomery()
}
/// Multiply this point by `clamp_integer(bytes)`. For a description of clamping, see
/// [`clamp_integer`].
pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
// We have to construct a Scalar that is not reduced mod l, which breaks scalar invariant
// #2. But #2 is not necessary for correctness of variable-base multiplication. All that
// needs to hold is invariant #1, i.e., the scalar is less than 2^255. This is guaranteed
// by clamping.
// Further, we don't do any reduction or arithmetic with this clamped value, so there's no
// issues arising from the fact that the curve point is not necessarily in the prime-order
// subgroup.
let s = Scalar {
bytes: clamp_integer(bytes),
};
s * self
}
/// Multiply the basepoint by `clamp_integer(bytes)`. For a description of clamping, see
/// [`clamp_integer`].
pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
// See reasoning in Self::mul_clamped why it is OK to make an unreduced Scalar here. We
// note that fixed-base multiplication is also defined for all values of `bytes` less than
// 2^255.
let s = Scalar {
bytes: clamp_integer(bytes),
};
Self::mul_base(&s)
}
/// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
/// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
///
/// **NOTE:** You probably do not want to use this function. Almost every protocol built on
/// Curve25519 uses _clamped multiplication_, explained
/// [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/).
/// When in doubt, use [`Self::mul_clamped`].
pub fn mul_bits_be(&self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
// Algorithm 8 of Costello-Smith 2017
let affine_u = FieldElement::from_bytes(&self.0);
let mut x0 = ProjectivePoint::identity();
let mut x1 = ProjectivePoint {
U: affine_u,
W: FieldElement::ONE,
};
// Go through the bits from most to least significant, using a sliding window of 2
let mut prev_bit = false;
for cur_bit in bits {
let choice: u8 = (prev_bit ^ cur_bit) as u8;
debug_assert!(choice == 0 || choice == 1);
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
differential_add_and_double(&mut x0, &mut x1, &affine_u);
prev_bit = cur_bit;
}
// The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
// Don't leave the bit in the stack
#[cfg(feature = "zeroize")]
prev_bit.zeroize();
x0.as_affine()
}
/// View this `MontgomeryPoint` as an array of bytes.
pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] {
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
/// Convert this `MontgomeryPoint` to an array of bytes.
pub fn to_bytes(&self) -> [u8; 32] {
pub const fn to_bytes(&self) -> [u8; 32] {
self.0
}
@ -151,13 +231,15 @@ impl MontgomeryPoint {
let u = FieldElement::from_bytes(&self.0);
if u == FieldElement::minus_one() { return None; }
if u == FieldElement::MINUS_ONE {
return None;
}
let one = FieldElement::one();
let one = FieldElement::ONE;
let y = &(&u - &one) * &(&u + &one).invert();
let mut y_bytes = y.to_bytes();
let mut y_bytes = y.as_bytes();
y_bytes[31] ^= sign << 7;
CompressedEdwardsY(y_bytes).decompress()
@ -172,7 +254,7 @@ impl MontgomeryPoint {
// draft gets into a more polished/accepted state.
#[allow(unused)]
pub(crate) fn elligator_encode(r_0: &FieldElement) -> MontgomeryPoint {
let one = FieldElement::one();
let one = FieldElement::ONE;
let d_1 = &one + &r_0.square2(); /* 2r^2 */
let d = &MONTGOMERY_A_NEG * &(d_1.invert()); /* A/(1+2r^2) */
@ -185,12 +267,12 @@ pub(crate) fn elligator_encode(r_0: &FieldElement) -> MontgomeryPoint {
let (eps_is_sq, _eps) = FieldElement::sqrt_ratio_i(&eps, &one);
let zero = FieldElement::zero();
let zero = FieldElement::ZERO;
let Atemp = FieldElement::conditional_select(&MONTGOMERY_A, &zero, eps_is_sq); /* 0, or A if nonsquare*/
let mut u = &d + &Atemp; /* d, or d+A if nonsquare */
u.conditional_negate(!eps_is_sq); /* d, or -d-A if nonsquare */
MontgomeryPoint(u.to_bytes())
MontgomeryPoint(u.as_bytes())
}
/// A `ProjectivePoint` holds a point on the projective line
@ -205,8 +287,8 @@ struct ProjectivePoint {
impl Identity for ProjectivePoint {
fn identity() -> ProjectivePoint {
ProjectivePoint {
U: FieldElement::one(),
W: FieldElement::zero(),
U: FieldElement::ONE,
W: FieldElement::ZERO,
}
}
}
@ -237,9 +319,9 @@ impl ProjectivePoint {
///
/// * \\( u = U / W \\) if \\( W \neq 0 \\);
/// * \\( 0 \\) if \\( W \eq 0 \\);
pub fn to_affine(&self) -> MontgomeryPoint {
pub fn as_affine(&self) -> MontgomeryPoint {
let u = &self.U * &self.W.invert();
MontgomeryPoint(u.to_bytes())
MontgomeryPoint(u.as_bytes())
}
}
@ -251,12 +333,13 @@ impl ProjectivePoint {
/// and the affine difference
/// \\( u\_{P-Q} = u(P-Q) \\), set
/// $$
/// (U\_P : W\_P) \gets u([2]P)
/// (U\_P : W\_P) \gets u(\[2\]P)
/// $$
/// and
/// $$
/// (U\_Q : W\_Q) \gets u(P + Q).
/// $$
#[rustfmt::skip] // keep alignment of explanatory comments
fn differential_add_and_double(
P: &mut ProjectivePoint,
Q: &mut ProjectivePoint,
@ -299,49 +382,39 @@ fn differential_add_and_double(
define_mul_assign_variants!(LHS = MontgomeryPoint, RHS = Scalar);
define_mul_variants!(LHS = MontgomeryPoint, RHS = Scalar, Output = MontgomeryPoint);
define_mul_variants!(LHS = Scalar, RHS = MontgomeryPoint, Output = MontgomeryPoint);
define_mul_variants!(
LHS = MontgomeryPoint,
RHS = Scalar,
Output = MontgomeryPoint
);
define_mul_variants!(
LHS = Scalar,
RHS = MontgomeryPoint,
Output = MontgomeryPoint
);
/// Multiply this `MontgomeryPoint` by a `Scalar`.
impl<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint {
impl Mul<&Scalar> for &MontgomeryPoint {
type Output = MontgomeryPoint;
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0([n]P) \\).
fn mul(self, scalar: &'b Scalar) -> MontgomeryPoint {
// Algorithm 8 of Costello-Smith 2017
let affine_u = FieldElement::from_bytes(&self.0);
let mut x0 = ProjectivePoint::identity();
let mut x1 = ProjectivePoint {
U: affine_u,
W: FieldElement::one(),
};
let bits: [i8; 256] = scalar.bits();
for i in (0..255).rev() {
let choice: u8 = (bits[i + 1] ^ bits[i]) as u8;
debug_assert!(choice == 0 || choice == 1);
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
differential_add_and_double(&mut x0, &mut x1, &affine_u);
}
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(bits[0] as u8));
x0.to_affine()
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
// We multiply by the integer representation of the given Scalar. By scalar invariant #1,
// the MSB is 0, so we can skip it.
self.mul_bits_be(scalar.bits_le().rev().skip(1))
}
}
impl<'b> MulAssign<&'b Scalar> for MontgomeryPoint {
fn mul_assign(&mut self, scalar: &'b Scalar) {
impl MulAssign<&Scalar> for MontgomeryPoint {
fn mul_assign(&mut self, scalar: &Scalar) {
*self = (self as &MontgomeryPoint) * scalar;
}
}
impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
impl Mul<&MontgomeryPoint> for &Scalar {
type Output = MontgomeryPoint;
fn mul(self, point: &'b MontgomeryPoint) -> MontgomeryPoint {
fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
point * self
}
}
@ -353,15 +426,17 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
#[cfg(test)]
mod test {
use super::*;
use constants;
use core::convert::TryInto;
use crate::constants;
use rand_core::OsRng;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use rand_core::{CryptoRng, RngCore};
#[test]
fn identity_in_different_coordinates() {
let id_projective = ProjectivePoint::identity();
let id_montgomery = id_projective.to_affine();
let id_montgomery = id_projective.as_affine();
assert!(id_montgomery == MontgomeryPoint::identity());
}
@ -397,7 +472,7 @@ mod test {
);
// sign bit = 1 => minus basepoint
assert_eq!(
- constants::ED25519_BASEPOINT_POINT,
-constants::ED25519_BASEPOINT_POINT,
constants::X25519_BASEPOINT.to_edwards(1).unwrap()
);
}
@ -414,44 +489,150 @@ mod test {
/// Check that Montgomery -> Edwards fails for points on the twist.
#[test]
fn montgomery_to_edwards_rejects_twist() {
let one = FieldElement::one();
let one = FieldElement::ONE;
// u = 2 corresponds to a point on the twist.
let two = MontgomeryPoint((&one+&one).to_bytes());
let two = MontgomeryPoint((&one + &one).as_bytes());
assert!(two.to_edwards(0).is_none());
// u = -1 corresponds to a point on the twist, but should be
// checked explicitly because it's an exceptional point for the
// birational map. For instance, libsignal will accept it.
let minus_one = MontgomeryPoint((-&one).to_bytes());
let minus_one = MontgomeryPoint((-&one).as_bytes());
assert!(minus_one.to_edwards(0).is_none());
}
#[test]
fn eq_defined_mod_p() {
let mut u18_bytes = [0u8; 32]; u18_bytes[0] = 18;
let mut u18_bytes = [0u8; 32];
u18_bytes[0] = 18;
let u18 = MontgomeryPoint(u18_bytes);
let u18_unred = MontgomeryPoint([255; 32]);
assert_eq!(u18, u18_unred);
}
#[test]
fn montgomery_ladder_matches_edwards_scalarmult() {
let mut csprng: OsRng = OsRng;
let s: Scalar = Scalar::random(&mut csprng);
let p_edwards: EdwardsPoint = &constants::ED25519_BASEPOINT_TABLE * &s;
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
let expected = s * p_edwards;
let result = s * p_montgomery;
assert_eq!(result, expected.to_montgomery())
/// Returns a random point on the prime-order subgroup
fn rand_prime_order_point(mut rng: impl RngCore + CryptoRng) -> EdwardsPoint {
let s: Scalar = Scalar::random(&mut rng);
EdwardsPoint::mul_base(&s)
}
/// Given a bytestring that's little-endian at the byte level, return an iterator over all the
/// bits, in little-endian order.
fn bytestring_bits_le(x: &[u8]) -> impl DoubleEndedIterator<Item = bool> + Clone + '_ {
let bitlen = x.len() * 8;
(0..bitlen).map(|i| {
// As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
// the byte. Since self.bytes is little-endian at the byte level, this iterator is
// little-endian on the bit level
((x[i >> 3] >> (i & 7)) & 1u8) == 1
})
}
#[test]
fn montgomery_ladder_matches_edwards_scalarmult() {
let mut csprng = rand_core::OsRng;
for _ in 0..100 {
let p_edwards = rand_prime_order_point(&mut csprng);
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
let s: Scalar = Scalar::random(&mut csprng);
let expected = s * p_edwards;
let result = s * p_montgomery;
assert_eq!(result, expected.to_montgomery())
}
}
// Tests that, on the prime-order subgroup, MontgomeryPoint::mul_bits_be is the same as
// multiplying by the Scalar representation of the same bits
#[test]
fn montgomery_mul_bits_be() {
let mut csprng = rand_core::OsRng;
for _ in 0..100 {
// Make a random prime-order point P
let p_edwards = rand_prime_order_point(&mut csprng);
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
// Make a random integer b
let mut bigint = [0u8; 64];
csprng.fill_bytes(&mut bigint[..]);
let bigint_bits_be = bytestring_bits_le(&bigint).rev();
// Check that bP is the same whether calculated as scalar-times-edwards or
// integer-times-montgomery.
let expected = Scalar::from_bytes_mod_order_wide(&bigint) * p_edwards;
let result = p_montgomery.mul_bits_be(bigint_bits_be);
assert_eq!(result, expected.to_montgomery())
}
}
// Tests that MontgomeryPoint::mul_bits_be is consistent on any point, even ones that might be
// on the curve's twist. Specifically, this tests that b₁(b₂P) == b₂(b₁P) for random
// integers b₁, b₂ and random (curve or twist) point P.
#[test]
fn montgomery_mul_bits_be_twist() {
let mut csprng = rand_core::OsRng;
for _ in 0..100 {
// Make a random point P on the curve or its twist
let p_montgomery = {
let mut buf = [0u8; 32];
csprng.fill_bytes(&mut buf);
MontgomeryPoint(buf)
};
// Compute two big integers b₁ and b₂
let mut bigint1 = [0u8; 64];
let mut bigint2 = [0u8; 64];
csprng.fill_bytes(&mut bigint1[..]);
csprng.fill_bytes(&mut bigint2[..]);
// Compute b₁P and b₂P
let bigint1_bits_be = bytestring_bits_le(&bigint1).rev();
let bigint2_bits_be = bytestring_bits_le(&bigint2).rev();
let prod1 = p_montgomery.mul_bits_be(bigint1_bits_be.clone());
let prod2 = p_montgomery.mul_bits_be(bigint2_bits_be.clone());
// Check that b₁(b₂P) == b₂(b₁P)
assert_eq!(
prod1.mul_bits_be(bigint2_bits_be),
prod2.mul_bits_be(bigint1_bits_be)
);
}
}
/// Check that mul_base_clamped and mul_clamped agree
#[test]
fn mul_base_clamped() {
let mut csprng = rand_core::OsRng;
// Test agreement on a large integer. Even after clamping, this is not reduced mod l.
let a_bytes = [0xff; 32];
assert_eq!(
MontgomeryPoint::mul_base_clamped(a_bytes),
constants::X25519_BASEPOINT.mul_clamped(a_bytes)
);
// Test agreement on random integers
for _ in 0..100 {
// This will be reduced mod l with probability l / 2^256 ≈ 6.25%
let mut a_bytes = [0u8; 32];
csprng.fill_bytes(&mut a_bytes);
assert_eq!(
MontgomeryPoint::mul_base_clamped(a_bytes),
constants::X25519_BASEPOINT.mul_clamped(a_bytes)
);
}
}
#[cfg(feature = "alloc")]
const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [
0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac,
0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95,
@ -459,9 +640,9 @@ mod test {
];
#[test]
#[cfg(feature = "std")] // Vec
#[cfg(feature = "alloc")]
fn montgomery_elligator_correct() {
let bytes: std::vec::Vec<u8> = (0u8..32u8).collect();
let bytes: Vec<u8> = (0u8..32u8).collect();
let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken");
let fe = FieldElement::from_bytes(&bits_in);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,8 @@
use core::borrow::Borrow;
use subtle;
use scalar::Scalar;
use crate::scalar::{clamp_integer, Scalar};
use subtle::ConstantTimeEq;
// ------------------------------------------------------------------------
// Public Traits
@ -41,10 +40,10 @@ pub trait IsIdentity {
/// constructor.
impl<T> IsIdentity for T
where
T: subtle::ConstantTimeEq + Identity,
T: ConstantTimeEq + Identity,
{
fn is_identity(&self) -> bool {
self.ct_eq(&T::identity()).unwrap_u8() == 1u8
self.ct_eq(&T::identity()).into()
}
}
@ -60,7 +59,19 @@ pub trait BasepointTable {
fn basepoint(&self) -> Self::Point;
/// Multiply a `scalar` by this precomputed basepoint table, in constant time.
fn basepoint_mul(&self, scalar: &Scalar) -> Self::Point;
fn mul_base(&self, scalar: &Scalar) -> Self::Point;
/// Multiply `clamp_integer(bytes)` by this precomputed basepoint table, in constant time. For
/// a description of clamping, see [`clamp_integer`].
fn mul_base_clamped(&self, bytes: [u8; 32]) -> Self::Point {
// Basepoint multiplication is defined for all values of `bytes` up to and including
// 2^255 - 1. The limit comes from the fact that scalar.as_radix_16() doesn't work for
// most scalars larger than 2^255.
let s = Scalar {
bytes: clamp_integer(bytes),
};
self.mul_base(&s)
}
}
/// A trait for constant-time multiscalar multiplication without precomputation.
@ -84,6 +95,8 @@ pub trait MultiscalarMul {
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// # #[cfg(feature = "alloc")]
/// # {
/// use curve25519_dalek::constants;
/// use curve25519_dalek::traits::MultiscalarMul;
/// use curve25519_dalek::ristretto::RistrettoPoint;
@ -110,6 +123,7 @@ pub trait MultiscalarMul {
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// # }
/// ```
fn multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
where
@ -136,6 +150,8 @@ pub trait VartimeMultiscalarMul {
/// inlining point decompression into the multiscalar call,
/// avoiding the need for temporary buffers.
/// ```
/// #[cfg(feature = "alloc")]
/// # {
/// use curve25519_dalek::constants;
/// use curve25519_dalek::traits::VartimeMultiscalarMul;
/// use curve25519_dalek::ristretto::RistrettoPoint;
@ -175,6 +191,7 @@ pub trait VartimeMultiscalarMul {
/// );
///
/// assert_eq!(A3, Some(A1+A1));
/// # }
/// ```
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<Self::Point>
where
@ -199,6 +216,8 @@ pub trait VartimeMultiscalarMul {
/// iterators returning either `Scalar`s or `&Scalar`s.
///
/// ```
/// #[cfg(feature = "alloc")]
/// # {
/// use curve25519_dalek::constants;
/// use curve25519_dalek::traits::VartimeMultiscalarMul;
/// use curve25519_dalek::ristretto::RistrettoPoint;
@ -225,6 +244,7 @@ pub trait VartimeMultiscalarMul {
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
///
/// assert_eq!(A1.compress(), (-A2).compress());
/// # }
/// ```
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
where
@ -238,7 +258,7 @@ pub trait VartimeMultiscalarMul {
scalars,
points.into_iter().map(|P| Some(P.borrow().clone())),
)
.unwrap()
.expect("should return some point")
}
}
@ -254,15 +274,15 @@ pub trait VartimeMultiscalarMul {
///
/// This trait has three methods for performing this computation:
///
/// * [`vartime_multiscalar_mul`], which handles the special case
/// where \\(n = 0\\) and there are no dynamic points;
/// * [`Self::vartime_multiscalar_mul`], which handles the special case where
/// \\(n = 0\\) and there are no dynamic points;
///
/// * [`vartime_mixed_multiscalar_mul`], which takes the dynamic
/// points as already-validated `Point`s and is infallible;
/// * [`Self::vartime_mixed_multiscalar_mul`], which takes the dynamic points as
/// already-validated `Point`s and is infallible;
///
/// * [`optional_mixed_multiscalar_mul`], which takes the dynamic
/// points as `Option<Point>`s and returns an `Option<Point>`,
/// allowing decompression to be composed into the input iterators.
/// * [`Self::optional_mixed_multiscalar_mul`], which takes the dynamic points
/// as `Option<Point>`s and returns an `Option<Point>`, allowing decompression
/// to be composed into the input iterators.
///
/// All methods require that the lengths of the input iterators be
/// known and matching, as if they were `ExactSizeIterator`s. (It
@ -344,7 +364,7 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
dynamic_scalars,
dynamic_points.into_iter().map(|P| Some(P.borrow().clone())),
)
.unwrap()
.expect("should return some point")
}
/// Given `static_scalars`, an iterator of public scalars
@ -388,6 +408,7 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
/// This trait is only for debugging/testing, since it should be
/// impossible for a `curve25519-dalek` user to construct an invalid
/// point.
#[allow(dead_code)]
pub(crate) trait ValidityCheck {
/// Checks whether the point is on the curve. Not CT.
fn is_valid(&self) -> bool;

View File

@ -0,0 +1,276 @@
// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Code for fixed- and sliding-window functionality
#![allow(non_snake_case)]
use core::fmt::Debug;
use cfg_if::cfg_if;
use subtle::Choice;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use crate::traits::Identity;
use crate::backend::serial::curve_models::AffineNielsPoint;
use crate::backend::serial::curve_models::ProjectiveNielsPoint;
use crate::edwards::EdwardsPoint;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
macro_rules! impl_lookup_table {
(Name = $name:ident, Size = $size:expr, SizeNeg = $neg:expr, SizeRange = $range:expr, ConversionRange = $conv_range:expr) => {
/// A lookup table of precomputed multiples of a point \\(P\\), used to
/// compute \\( xP \\) for \\( -8 \leq x \leq 8 \\).
///
/// The computation of \\( xP \\) is done in constant time by the `select` function.
///
/// Since `LookupTable` does not implement `Index`, it's more difficult
/// to accidentally use the table directly. Unfortunately the table is
/// only `pub(crate)` so that we can write hardcoded constants, so it's
/// still technically possible. It would be nice to prevent direct
/// access to the table.
#[derive(Copy, Clone)]
pub struct $name<T>(pub(crate) [T; $size]);
impl<T> $name<T>
where
T: Identity + ConditionallySelectable + ConditionallyNegatable,
{
/// Given \\(-8 \leq x \leq 8\\), return \\(xP\\) in constant time.
pub fn select(&self, x: i8) -> T {
debug_assert!(x >= $neg);
debug_assert!(x as i16 <= $size as i16); // XXX We have to convert to i16s here for the radix-256 case.. this is wrong.
// Compute xabs = |x|
let xmask = x as i16 >> 7;
let xabs = (x as i16 + xmask) ^ xmask;
// Set t = 0 * P = identity
let mut t = T::identity();
for j in $range {
// Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`.
let c = (xabs as u16).ct_eq(&(j as u16));
t.conditional_assign(&self.0[j - 1], c);
}
// Now t == |x| * P.
let neg_mask = Choice::from((xmask & 1) as u8);
t.conditional_negate(neg_mask);
// Now t == x * P.
t
}
}
impl<T: Copy + Default> Default for $name<T> {
fn default() -> $name<T> {
$name([T::default(); $size])
}
}
impl<T: Debug> Debug for $name<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}(", stringify!($name))?;
for x in self.0.iter() {
write!(f, "{:?}", x)?;
}
write!(f, ")")
}
}
impl<'a> From<&'a EdwardsPoint> for $name<ProjectiveNielsPoint> {
fn from(P: &'a EdwardsPoint) -> Self {
let mut points = [P.as_projective_niels(); $size];
for j in $conv_range {
points[j + 1] = (P + &points[j]).as_extended().as_projective_niels();
}
$name(points)
}
}
impl<'a> From<&'a EdwardsPoint> for $name<AffineNielsPoint> {
fn from(P: &'a EdwardsPoint) -> Self {
let mut points = [P.as_affine_niels(); $size];
// XXX batch inversion would be good if perf mattered here
for j in $conv_range {
points[j + 1] = (P + &points[j]).as_extended().as_affine_niels()
}
$name(points)
}
}
#[cfg(feature = "zeroize")]
impl<T> Zeroize for $name<T>
where
T: Copy + Default + Zeroize,
{
fn zeroize(&mut self) {
self.0.iter_mut().zeroize();
}
}
};
} // End macro_rules! impl_lookup_table
// The first one has to be named "LookupTable" because it's used as a constructor for consts.
// This is radix-16
impl_lookup_table! {
Name = LookupTable,
Size = 8,
SizeNeg = -8,
SizeRange = 1..9,
ConversionRange = 0..7
}
// The rest only get used to make basepoint tables
cfg_if! {
if #[cfg(feature = "precomputed-tables")] {
// radix-32
impl_lookup_table! {
Name = LookupTableRadix32,
Size = 16,
SizeNeg = -16,
SizeRange = 1..17,
ConversionRange = 0..15
}
// radix-64
impl_lookup_table! {
Name = LookupTableRadix64,
Size = 32,
SizeNeg = -32,
SizeRange = 1..33,
ConversionRange = 0..31
}
// radix-128
impl_lookup_table! {
Name = LookupTableRadix128,
Size = 64,
SizeNeg = -64,
SizeRange = 1..65,
ConversionRange = 0..63
}
// radix-256
impl_lookup_table! {
Name = LookupTableRadix256,
Size = 128,
SizeNeg = -128,
SizeRange = 1..129,
ConversionRange = 0..127
}
// For homogeneity we then alias it to "LookupTableRadix16".
pub(crate) type LookupTableRadix16<T> = LookupTable<T>;
}
}
/// Holds odd multiples 1A, 3A, ..., 15A of a point A.
#[derive(Copy, Clone)]
pub(crate) struct NafLookupTable5<T>(pub(crate) [T; 8]);
impl<T: Copy> NafLookupTable5<T> {
/// Given public, odd \\( x \\) with \\( 0 < x < 2^4 \\), return \\(xA\\).
pub fn select(&self, x: usize) -> T {
debug_assert_eq!(x & 1, 1);
debug_assert!(x < 16);
self.0[x / 2]
}
}
impl<T: Debug> Debug for NafLookupTable5<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "NafLookupTable5({:?})", self.0)
}
}
impl<'a> From<&'a EdwardsPoint> for NafLookupTable5<ProjectiveNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.as_projective_niels(); 8];
let A2 = A.double();
for i in 0..7 {
Ai[i + 1] = (&A2 + &Ai[i]).as_extended().as_projective_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
NafLookupTable5(Ai)
}
}
impl<'a> From<&'a EdwardsPoint> for NafLookupTable5<AffineNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.as_affine_niels(); 8];
let A2 = A.double();
for i in 0..7 {
Ai[i + 1] = (&A2 + &Ai[i]).as_extended().as_affine_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
NafLookupTable5(Ai)
}
}
/// Holds stuff up to 8. The only time we use tables this big is for precomputed basepoint tables
/// and multiscalar multiplication (which requires alloc).
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
#[derive(Copy, Clone)]
pub(crate) struct NafLookupTable8<T>(pub(crate) [T; 64]);
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
impl<T: Copy> NafLookupTable8<T> {
pub fn select(&self, x: usize) -> T {
debug_assert_eq!(x & 1, 1);
debug_assert!(x < 128);
self.0[x / 2]
}
}
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
impl<T: Debug> Debug for NafLookupTable8<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "NafLookupTable8([")?;
for i in 0..64 {
writeln!(f, "\t{:?},", &self.0[i])?;
}
write!(f, "])")
}
}
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
impl<'a> From<&'a EdwardsPoint> for NafLookupTable8<ProjectiveNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.as_projective_niels(); 64];
let A2 = A.double();
for i in 0..63 {
Ai[i + 1] = (&A2 + &Ai[i]).as_extended().as_projective_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
NafLookupTable8(Ai)
}
}
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
impl<'a> From<&'a EdwardsPoint> for NafLookupTable8<AffineNielsPoint> {
fn from(A: &'a EdwardsPoint) -> Self {
let mut Ai = [A.as_affine_niels(); 64];
let A2 = A.double();
for i in 0..63 {
Ai[i + 1] = (&A2 + &Ai[i]).as_extended().as_affine_niels();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
NafLookupTable8(Ai)
}
}

View File

@ -0,0 +1,88 @@
#!/bin/bash
function match_and_report() {
PATTERN=$1
FILE=$2
if grep -q "$PATTERN" "$FILE"; then
echo build OK "$FILE" : "$PATTERN"
else
echo build ERROR "$FILE" : "$PATTERN"
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>"
cat "$FILE"
echo "<<<<<<<<<<<<<<<<<<<<<<<<<<"
exit 1
fi
}
# Assuming naively 64 bit host
cargo clean
OUT=build_1.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'simd'" "$OUT"
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
# Override to 32 bits assuming naively 64 bit build host
cargo clean
OUT=build_2.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
# Override to 64 bits on 32 bit target
cargo clean
OUT=build_3.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"64\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
# 32 bit target default
cargo clean
OUT=build_4.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
# wasm 32 bit target default
cargo clean
OUT=build_5.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
# wasm 32 bit target default
# Attempted override w/ "simd" should result "serial" addition
cargo clean
OUT=build_5_1.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"simd\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1
# This overide must fail the compilation since "simd" is not available
# See: issues/532
match_and_report "Could not override curve25519_dalek_backend to simd" "$OUT"
# fiat override with default 64 bit naive host assumption
cargo clean
OUT=build_6.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT"
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
# fiat 32 bit override
cargo clean
OUT=build_7.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT"
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
# serial override with default 64 bit naive host assumption
cargo clean
OUT=build_8.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
# serial 32 bit override
cargo clean
OUT=build_9.txt
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
match_and_report "curve25519_dalek_bits is '32'" "$OUT"

View File

@ -1,10 +0,0 @@
<link rel="stylesheet" href="https://doc.dalek.rs/assets/katex/katex.min.css">
<script src="https://doc.dalek.rs/assets/katex/katex.min.js"></script>
<script src="https://doc.dalek.rs/assets/katex/contrib/auto-render.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() { renderMathInElement(document.body); });
</script>
<style>
.katex { font-size: 1em !important; }
pre.rust, .docblock code, .docblock-short code { font-size: 0.85em !important; }
</style>

12
ed25519-dalek/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
target
.cargo
*~
\#*
.\#*
*.swp
*.orig
*.bak
*.s

33
ed25519-dalek/.travis.yml Normal file
View File

@ -0,0 +1,33 @@
language: rust
rust:
- stable
- beta
- nightly
env:
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES=''
matrix:
include:
# We use the 64-bit optimised curve backend by default, so also test with
# the 32-bit backend (this also exercises testing with `no_std`):
- rust: nightly
env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend alloc'
# Also test the batch feature:
- rust: nightly
env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend alloc batch'
# Test any nightly gated features on nightly:
- rust: nightly
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
# Test serde support on stable, assuming that if it works there it'll work everywhere:
- rust: stable
env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde'
script:
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
notifications:
slack:
rooms:
- dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots

View File

@ -0,0 +1,52 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Entries are listed in reverse chronological order per undeprecated major series.
# Unreleased
# 2.x series
## 2.1.1
* Fix nightly SIMD build
## 2.1.0
* Add `SigningKey::to_scalar_bytes` for getting the unclamped scalar from a signing key
* Loosened `signature` dependency to allow version 2.2
## 2.0.0
### Breaking changes
* Bump MSRV from 1.41 to 1.60.0
* Bump Rust edition
* Bump `signature` dependency to 2.0
* Make `digest` an optional dependency
* Make `zeroize` an optional dependency
* Make `rand_core` an optional dependency
* [curve25519 backends] are now automatically selected
* [curve25519 backends] are now overridable via cfg instead of using additive features
* Make all batch verification deterministic remove `batch_deterministic` (PR [#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256))
* Rename `Keypair``SigningKey` and `PublicKey``VerifyingKey`
* Remove default-public `ExpandedSecretKey` API (PR [#205](https://github.com/dalek-cryptography/ed25519-dalek/pull/205))
* Make `hazmat` feature to expose `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()`
[curve25519 backends]: https://github.com/dalek-cryptography/curve25519-dalek/#backends
### Other changes
* Add `Context` type for prehashed signing
* Add `VerifyingKey::{verify_prehash_strict, is_weak}`
* Add `pkcs` feature to support PKCS #8 (de)serialization of `SigningKey` and `VerifyingKey`
* Add `fast` feature to include basepoint tables
* Add tests for validation criteria
* Impl `DigestSigner`/`DigestVerifier` for `SigningKey`/`VerifyingKey`, respectively
* Impl `Hash` for `VerifyingKey`
* Impl `Clone`, `Drop`, and `ZeroizeOnDrop` for `SigningKey`
* Remove `rand` dependency
* Improve key deserialization diagnostics

78
ed25519-dalek/Cargo.toml Normal file
View File

@ -0,0 +1,78 @@
[package]
name = "ed25519-dalek"
version = "2.1.1"
edition = "2021"
authors = [
"isis lovecruft <isis@patternsinthevoid.net>",
"Tony Arcieri <bascule@gmail.com>",
"Michael Rosenberg <michael@mrosenberg.pub>"
]
readme = "README.md"
license = "BSD-3-Clause"
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek"
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/ed25519-dalek"
keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"]
categories = ["cryptography", "no-std"]
description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust."
exclude = [ ".gitignore", "TESTVECTORS", "VALIDATIONVECTORS", "res/*" ]
rust-version = "1.60"
[package.metadata.docs.rs]
rustdoc-args = [
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
"--cfg", "docsrs",
]
features = ["batch", "digest", "hazmat", "pem", "serde"]
[dependencies]
curve25519-dalek = { version = "4", path = "../curve25519-dalek", default-features = false, features = ["digest"] }
ed25519 = { version = ">=2.2, <2.3", default-features = false }
signature = { version = ">=2.0, <2.3", optional = true, default-features = false }
sha2 = { version = "0.10", default-features = false }
subtle = { version = "2.3.0", default-features = false }
# optional features
merlin = { version = "3", default-features = false, optional = true }
rand_core = { version = "0.6.4", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, optional = true }
zeroize = { version = "1.5", default-features = false, optional = true }
[dev-dependencies]
curve25519-dalek = { version = "4", path = "../curve25519-dalek", default-features = false, features = ["digest", "rand_core"] }
x25519-dalek = { version = "2", path = "../x25519-dalek", default-features = false, features = ["static_secrets"] }
blake2 = "0.10"
sha3 = "0.10"
hex = "0.4"
bincode = "1.0"
serde_json = "1.0"
criterion = { version = "0.5", features = ["html_reports"] }
hex-literal = "0.4"
rand = "0.8"
rand_core = { version = "0.6.4", default-features = false }
serde = { version = "1.0", features = ["derive"] }
toml = { version = "0.7" }
[[bench]]
name = "ed25519_benchmarks"
harness = false
required-features = ["rand_core"]
[features]
default = ["fast", "std", "zeroize"]
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"]
std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"]
asm = ["sha2/asm"]
batch = ["alloc", "merlin", "rand_core"]
fast = ["curve25519-dalek/precomputed-tables"]
digest = ["signature/digest"]
# Exposes the hazmat module
hazmat = []
# Turns off stricter checking for scalar malleability in signatures
legacy_compatibility = ["curve25519-dalek/legacy_compatibility"]
pkcs8 = ["ed25519/pkcs8"]
pem = ["alloc", "ed25519/pem", "pkcs8"]
rand_core = ["dep:rand_core"]
serde = ["dep:serde", "ed25519/serde"]
zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"]

28
ed25519-dalek/LICENSE Normal file
View File

@ -0,0 +1,28 @@
Copyright (c) 2017-2019 isis agora lovecruft. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

178
ed25519-dalek/README.md Normal file
View File

@ -0,0 +1,178 @@
# ed25519-dalek [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml)
Fast and efficient Rust implementation of ed25519 key generation, signing, and
verification.
# Use
To import `ed25519-dalek`, add the following to the dependencies section of
your project's `Cargo.toml`:
```toml
ed25519-dalek = "2"
```
# Feature Flags
This crate is `#[no_std]` compatible with `default-features = false`.
| Feature | Default? | Description |
| :--- | :--- | :--- |
| `alloc` | ✓ | When `pkcs8` is enabled, implements `EncodePrivateKey`/`EncodePublicKey` for `SigningKey`/`VerifyingKey`, respectively. |
| `std` | ✓ | Implements `std::error::Error` for `SignatureError`. Also enables `alloc`. |
| `zeroize` | ✓ | Implements `Zeroize` and `ZeroizeOnDrop` for `SigningKey` |
| `rand_core` | | Enables `SigningKey::generate` |
| `batch` | | Enables `verify_batch` for verifying many signatures quickly. Also enables `rand_core`. |
| `digest` | | Enables `Context`, `SigningKey::{with_context, sign_prehashed}` and `VerifyingKey::{with_context, verify_prehashed, verify_prehashed_strict}` for Ed25519ph prehashed signatures |
| `asm` | | Enables assembly optimizations in the SHA-512 compression functions |
| `pkcs8` | | Enables [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) serialization/deserialization for `SigningKey` and `VerifyingKey` |
| `pem` | | Enables PEM serialization support for PKCS#8 private keys and SPKI public keys. Also enables `alloc`. |
| `legacy_compatibility` | | **Unsafe:** Disables certain signature checks. See [below](#malleability-and-the-legacy_compatibility-feature) |
| `hazmat` | | **Unsafe:** Exposes the `hazmat` module for raw signing/verifying. Misuse of these functions will expose the private key, as in the [signing oracle attack](https://github.com/MystenLabs/ed25519-unsafe-libs). |
# Major Changes
See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past versions of this crate.
## Breaking Changes in 2.0.0
* Bump MSRV from 1.41 to 1.60.0
* Bump Rust edition
* Bump `signature` dependency to 2.0
* Make `digest` an optional dependency
* Make `zeroize` an optional dependency
* Make `rand_core` an optional dependency
* Adopt [curve25519-backend selection](https://github.com/dalek-cryptography/curve25519-dalek/#backends) over features
* Make all batch verification deterministic remove `batch_deterministic` ([#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256))
* Remove `ExpandedSecretKey` API ([#205](https://github.com/dalek-cryptography/ed25519-dalek/pull/205))
* Rename `Keypair``SigningKey` and `PublicKey``VerifyingKey`
* Make `hazmat` feature to expose, `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()`
# Documentation
Documentation is available [here](https://docs.rs/ed25519-dalek).
# Compatibility Policies
All on-by-default features of this library are covered by [semantic versioning](https://semver.org/spec/v2.0.0.html) (SemVer).
SemVer exemptions are outlined below for MSRV and public API.
## Minimum Supported Rust Version
| Releases | MSRV |
| :--- | :--- |
| 2.x | 1.60 |
| 1.x | 1.41 |
From 2.x onwards, MSRV changes will be accompanied by a minor version bump.
## Public API SemVer Exemptions
Breaking changes to SemVer-exempted components affecting the public API will be accompanied by some version bump.
Below are the specific policies:
| Releases | Public API Component(s) | Policy |
| :--- | :--- | :--- |
| 2.x | Dependencies `digest`, `pkcs8` and `rand_core` | Minor SemVer bump |
# Safety
`ed25519-dalek` is designed to prevent misuse. Signing is constant-time, all signing keys are zeroed when they go out of scope (unless `zeroize` is disabled), detached public keys [cannot](https://github.com/MystenLabs/ed25519-unsafe-libs/blob/main/README.md) be used for signing, and extra functions like [`VerifyingKey::verify_strict`](#weak-key-forgery-and-verify_strict) are made available to avoid known gotchas.
Further, this crate has no—and in fact forbids—unsafe code. You can opt in to using some highly optimized unsafe code that resides in `curve25519-dalek`, though. See [below](#microarchitecture-specific-backends) for more information on backend selection.
# Performance
Performance is a secondary goal behind correctness, safety, and clarity, but we
aim to be competitive with other implementations.
## Benchmarks
Benchmarks are run using [criterion.rs](https://github.com/japaric/criterion.rs):
```sh
cargo bench --features "batch"
# Uses avx2 or ifma only if compiled for an appropriate target.
export RUSTFLAGS='-C target_cpu=native'
cargo +nightly bench --features "batch"
```
On an Intel 10700K running at stock comparing between the `curve25519-dalek` backends.
| Benchmark | u64 | simd +avx2 | fiat |
| :--- | :---- | :--- | :--- |
| signing | 15.017 µs | 13.906 µs -7.3967% | 15.877 μs +5.7268% |
| signature verification | 40.144 µs | 25.963 µs -35.603% | 42.118 μs +4.9173% |
| strict signature verification | 41.334 µs | 27.874 µs -32.660% | 43.985 μs +6.4136% |
| batch signature verification/4 | 109.44 µs | 81.778 µs -25.079% | 117.80 μs +7.6389% |
| batch signature verification/8 | 182.75 µs | 138.40 µs -23.871% | 195.86 μs +7.1737% |
| batch signature verification/16 | 328.67 µs | 251.39 µs -23.744% | 351.55 μs +6.9614% |
| batch signature verification/32 | 619.49 µs | 477.36 µs -23.053% | 669.41 μs +8.0582% |
| batch signature verification/64 | 1.2136 ms | 936.85 µs -22.543% | 1.3028 ms +7.3500% |
| batch signature verification/96 | 1.8677 ms | 1.2357 ms -33.936% | 2.0552 ms +10.039% |
| batch signature verification/128| 2.3281 ms | 1.5795 ms -31.996% | 2.5596 ms +9.9437% |
| batch signature verification/256| 4.1868 ms | 2.8864 ms -31.061% | 4.6494 μs +11.049% |
| keypair generation | 13.973 µs | 13.108 µs -6.5062% | 15.099 μs +8.0584% |
## Batch Performance
If your protocol or application is able to batch signatures for verification,
the [`verify_batch`][func_verify_batch] function has greatly improved performance.
As you can see, there's an optimal batch size for each machine, so you'll likely
want to test the benchmarks on your target CPU to discover the best size.
## (Micro)Architecture Specific Backends
A _backend_ refers to an implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]).
Backend selection details and instructions can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends).
# Contributing
See [CONTRIBUTING.md](../CONTRIBUTING.md)
# Batch Signature Verification
The standard variants of batch signature verification (i.e. many signatures made with potentially many different public keys over potentially many different messages) is available via the `batch` feature. It uses deterministic randomness, i.e., it hashes the inputs (using [`merlin`](https://merlin.cool/), which handles transcript item separation) and uses the result to generate random coefficients. Batch verification requires allocation, so this won't function in heapless settings.
# Validation Criteria
The _validation criteria_ of a signature scheme are the criteria that signatures and public keys must satisfy in order to be accepted. Unfortunately, Ed25519 has some underspecified parts, leading to different validation criteria across implementations. For a very good overview of this, see [Henry's post][validation].
In this section, we mention some specific details about our validation criteria, and how to navigate them.
## Malleability and the `legacy_compatibility` Feature
A signature scheme is considered to produce _malleable signatures_ if a passive attacker with knowledge of a public key _A_, message _m_, and valid signature _σ'_ can produce a distinct _σ'_ such that _σ'_ is a valid signature of _m_ with respect to _A_. A scheme is only malleable if the attacker can do this _without_ knowledge of the private key corresponding to _A_.
`ed25519-dalek` is not a malleable signature scheme.
Some other Ed25519 implementations are malleable, though, such as [libsodium with `ED25519_COMPAT` enabled](https://github.com/jedisct1/libsodium/blob/24211d370a9335373f0715664271dfe203c7c2cd/src/libsodium/crypto_sign/ed25519/ref10/open.c#L30), [ed25519-donna](https://github.com/floodyberry/ed25519-donna/blob/8757bd4cd209cb032853ece0ce413f122eef212c/ed25519.c#L100), [NaCl's ref10 impl](https://github.com/floodyberry/ed25519-donna/blob/8757bd4cd209cb032853ece0ce413f122eef212c/fuzz/ed25519-ref10.c#L4627), and probably a lot more.
If you need to interoperate with such implementations and accept otherwise invalid signatures, you can enable the `legacy_compatibility` flag. **Do not enable `legacy_compatibility`** if you don't have to, because it will make your signatures malleable.
Note: [CIRCL](https://github.com/cloudflare/circl/blob/fa6e0cca79a443d7be18ed241e779adf9ed2a301/sign/ed25519/ed25519.go#L358) has no scalar range check at all. We do not have a feature flag for interoperating with the larger set of RFC-disallowed signatures that CIRCL accepts.
## Weak key Forgery and `verify_strict()`
A _signature forgery_ is what it sounds like: it's when an attacker, given a public key _A_, creates a signature _σ_ and message _m_ such that _σ_ is a valid signature of _m_ with respect to _A_. Since this is the core security definition of any signature scheme, Ed25519 signatures cannot be forged.
However, there's a much looser kind of forgery that Ed25519 permits, which we call _weak key forgery_. An attacker can produce a special public key _A_ (which we call a _weak_ public key) and a signature _σ_ such that _σ_ is a valid signature of _any_ message _m_, with respect to _A_, with high probability. This attack is acknowledged in the [Ed25519 paper](https://ed25519.cr.yp.to/ed25519-20110926.pdf), and caused an exploitable bug in the Scuttlebutt protocol ([paper](https://eprint.iacr.org/2019/526.pdf), section 7.1). The [`VerifyingKey::verify()`][method_verify] function permits weak keys.
We provide [`VerifyingKey::verify_strict`][method_verify_strict] (and [`verify_strict_prehashed`][method_verify_strict_ph]) to help users avoid these scenarios. These functions perform an extra check on _A_, ensuring it's not a weak public key. In addition, we provide the [`VerifyingKey::is_weak`][method_is_weak] to allow users to perform this check before attempting signature verification.
## Batch verification
As mentioned above, weak public keys can be used to produce signatures for unknown messages with high probability. This means that sometimes a weak forgery attempt will fail. In fact, it can fail up to 7/8 of the time. If you call `verify()` twice on the same failed forgery, it will return an error both times, as expected. However, if you call `verify_batch()` twice on two distinct otherwise-valid batches, both of which contain the failed forgery, there's a 21% chance that one fails and the other succeeds.
Why is this? It's because `verify_batch()` does not do the weak key testing of `verify_strict()`, and it multiplies each verification equation by some random coefficient. If the failed forgery gets multiplied by 8, then the weak key (which is a low-order point) becomes 0, and the verification equation on the attempted forgery will succeed.
Since `verify_batch()` is intended to be high-throughput, we think it's best not to put weak key checks in it. If you want to prevent weird behavior due to weak public keys in your batches, you should call [`VerifyingKey::is_weak`][method_is_weak] on the inputs in advance.
[fiat]: https://github.com/mit-plv/fiat-crypto
[validation]: https://hdevalence.ca/blog/2020-10-04-its-25519am
[func_verify_batch]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/fn.verify_batch.html
[method_verify]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify
[method_verify_strict]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify_strict
[method_verify_strict_ph]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.verify_strict_prehashed
[method_is_weak]: https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.VerifyingKey.html#method.is_weak

128
ed25519-dalek/TESTVECTORS Normal file
View File

@ -0,0 +1,128 @@
9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a:d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a::e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b:
4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:72:92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c0072:
c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:af82:6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40aaf82:
0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:cbc77b:d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00ccbc77b:
6df9340c138cc188b5fe4464ebaa3f7fc206a2d55c3434707e74c9fc04e20ebbc0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:5f4c8989:124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c075f4c8989:
b780381a65edf8b78f6945e8dbec7941ac049fd4c61040cf0c324357975a293ce253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:e253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:18b6bec097:b2fc46ad47af464478c199e1f8be169f1be6327c7f9a0a6689371ca94caf04064a01b22aff1520abd58951341603faed768cf78ce97ae7b038abfe456aa17c0918b6bec097:
78ae9effe6f245e924a7be63041146ebc670dbd3060cba67fbc6216febc44546fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:89010d855972:6ed629fc1d9ce9e1468755ff636d5a3f40a5d9c91afd93b79d241830f7e5fa29854b8f20cc6eecbb248dbd8d16d14e99752194e4904d09c74d639518839d230089010d855972:
691865bfc82a1e4b574eecde4c7519093faf0cf867380234e3664645c61c5f7998a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:98a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:b4a8f381e70e7a:6e0af2fe55ae377a6b7a7278edfb419bd321e06d0df5e27037db8812e7e3529810fa5552f6c0020985ca17a0e02e036d7b222a24f99b77b75fdd16cb05568107b4a8f381e70e7a:
3b26516fb3dc88eb181b9ed73f0bcd52bcd6b4c788e4bcaf46057fd078bee073f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:4284abc51bb67235:d6addec5afb0528ac17bb178d3e7f2887f9adbb1ad16e110545ef3bc57f9de2314a5c8388f723b8907be0f3ac90c6259bbe885ecc17645df3db7d488f805fa084284abc51bb67235:
edc6f5fbdd1cee4d101c063530a30490b221be68c036f5b07d0f953b745df192c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:672bf8965d04bc5146:2c76a04af2391c147082e33faacdbe56642a1e134bd388620b852b901a6bc16ff6c9cc9404c41dea12ed281da067a1513866f9d964f8bdd24953856c50042901672bf8965d04bc5146:
4e7d21fb3b1897571a445833be0f9fd41cd62be3aa04040f8934e1fcbdcacd4531b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:31b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:33d7a786aded8c1bf691:28e4598c415ae9de01f03f9f3fab4e919e8bf537dd2b0cdf6e79b9e6559c9409d9151a4c40f083193937627c369488259e99da5a9f0a87497fa6696a5dd6ce0833d7a786aded8c1bf691:
a980f892db13c99a3e8971e965b2ff3d41eafd54093bc9f34d1fd22d84115bb644b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:44b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:3486f68848a65a0eb5507d:77d389e599630d934076329583cd4105a649a9292abc44cd28c40000c8e2f5ac7660a81c85b72af8452d7d25c070861dae91601c7803d656531650dd4e5c41003486f68848a65a0eb5507d:
5b5a619f8ce1c66d7ce26e5a2ae7b0c04febcd346d286c929e19d0d5973bfef96fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:6fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:5a8d9d0a22357e6655f9c785:0f9ad9793033a2fa06614b277d37381e6d94f65ac2a5a94558d09ed6ce922258c1a567952e863ac94297aec3c0d0c8ddf71084e504860bb6ba27449b55adc40e5a8d9d0a22357e6655f9c785:
940c89fe40a81dafbdb2416d14ae469119869744410c3303bfaa0241dac57800a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:b87d3813e03f58cf19fd0b6395:d8bb64aad8c9955a115a793addd24f7f2b077648714f49c4694ec995b330d09d640df310f447fd7b6cb5c14f9fe9f490bcf8cfadbfd2169c8ac20d3b8af49a0cb87d3813e03f58cf19fd0b6395:
9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:55c7fa434f5ed8cdec2b7aeac173:6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf0655c7fa434f5ed8cdec2b7aeac173:
d5aeee41eeb0e9d1bf8337f939587ebe296161e6bf5209f591ec939e1440c300fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:0a688e79be24f866286d4646b5d81c:f68d04847e5b249737899c014d31c805c5007a62c0a10d50bb1538c5f35503951fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b000a688e79be24f866286d4646b5d81c:
0a47d10452ae2febec518a1c7c362890c3fc1a49d34b03b6467d35c904a8362d34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:c942fa7ac6b23ab7ff612fdc8e68ef39:2a3d27dc40d0a8127949a3b7f908b3688f63b7f14f651aacd715940bdbe27a0809aac142f47ab0e1e44fa490ba87ce5392f33a891539caf1ef4c367cae54500cc942fa7ac6b23ab7ff612fdc8e68ef39:
f8148f7506b775ef46fdc8e8c756516812d47d6cfbfa318c27c9a22641e56f170445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:0445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:7368724a5b0efb57d28d97622dbde725af:3653ccb21219202b8436fb41a32ba2618c4a133431e6e63463ceb3b6106c4d56e1d2ba165ba76eaad3dc39bffb130f1de3d8e6427db5b71938db4e272bc3e20b7368724a5b0efb57d28d97622dbde725af:
77f88691c4eff23ebb7364947092951a5ff3f10785b417e918823a552dab7c7574d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:74d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:bd8e05033f3a8bcdcbf4beceb70901c82e31:fbe929d743a03c17910575492f3092ee2a2bf14a60a3fcacec74a58c7334510fc262db582791322d6c8c41f1700adb80027ecabc14270b703444ae3ee7623e0abd8e05033f3a8bcdcbf4beceb70901c82e31:
ab6f7aee6a0837b334ba5eb1b2ad7fcecfab7e323cab187fe2e0a95d80eff1325b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:5b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:8171456f8b907189b1d779e26bc5afbb08c67a:73bca64e9dd0db88138eedfafcea8f5436cfb74bfb0e7733cf349baa0c49775c56d5934e1d38e36f39b7c5beb0a836510c45126f8ec4b6810519905b0ca07c098171456f8b907189b1d779e26bc5afbb08c67a:
8d135de7c8411bbdbd1b31e5dc678f2ac7109e792b60f38cd24936e8a898c32d1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:8ba6a4c9a15a244a9c26bb2a59b1026f21348b49:a1adc2bc6a2d980662677e7fdff6424de7dba50f5795ca90fdf3e96e256f3285cac71d3360482e993d0294ba4ec7440c61affdf35fe83e6e04263937db93f1058ba6a4c9a15a244a9c26bb2a59b1026f21348b49:
0e765d720e705f9366c1ab8c3fa84c9a44370c06969f803296884b2846a652a47fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:7fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:1d566a6232bbaab3e6d8804bb518a498ed0f904986:bb61cf84de61862207c6a455258bc4db4e15eea0317ff88718b882a06b5cf6ec6fd20c5a269e5d5c805bafbcc579e2590af414c7c227273c102a10070cdfe80f1d566a6232bbaab3e6d8804bb518a498ed0f904986:
db36e326d676c2d19cc8fe0c14b709202ecfc761d27089eb6ea4b1bb021ecfa748359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:48359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:1b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:b6dcd09989dfbac54322a3ce87876e1d62134da998c79d24b50bd7a6a797d86a0e14dc9d7491d6c14a673c652cfbec9f962a38c945da3b2f0879d0b68a9213001b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:
c89955e0f7741d905df0730b3dc2b0ce1a13134e44fef3d40d60c020ef19df77fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:7ef66e5e86f2360848e0014e94880ae2920ad8a3185a46b35d1e07dea8fa8ae4f6b843ba174d99fa7986654a0891c12a794455669375bf92af4cc2770b579e0c507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:
4e62627fc221142478aee7f00781f817f662e3b75db29bb14ab47cf8e84104d6b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:d3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:836afa764d9c48aa4770a4388b654e97b3c16f082967febca27f2fc47ddfd9244b03cfc729698acf5109704346b60b230f255430089ddc56912399d1122de70ad3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:
6b83d7da8908c3e7205b39864b56e5f3e17196a3fc9c2f5805aad0f5554c142dd0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:d0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:6ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:16e462a29a6dd498685a3718b3eed00cc1598601ee47820486032d6b9acc9bf89f57684e08d8c0f05589cda2882a05dc4c63f9d0431d6552710812433003bc086ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:
19a91fe23a4e9e33ecc474878f57c64cf154b394203487a7035e1ad9cd697b0d2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:881f5b8c5a030df0f75b6634b070dd27bd1ee3c08738ae349338b3ee6469bbf9760b13578a237d5182535ede121283027a90b5f865d63a6537dca07b44049a0f82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:
1d5b8cb6215c18141666baeefcf5d69dad5bea9a3493dddaa357a4397a13d4de94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:a9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:3acd39bec8c3cd2b44299722b5850a0400c1443590fd4861d59aae7496acb3df73fc3fdf7969ae5f50ba47dddc435246e5fd376f6b891cd4c2caf5d614b6170ca9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:
6a91b3227c472299089bdce9356e726a40efd840f11002708b7ee55b64105ac29d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:9d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:5cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:f5875423781b66216cb5e8998de5d9ffc29d1d67107054ace3374503a9c3ef811577f269de81296744bd706f1ac478caf09b54cdf871b3f802bd57f9a6cb91015cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:
93eaa854d791f05372ce72b94fc6503b2ff8ae6819e6a21afe825e27ada9e4fb16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:32fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:d834197c1a3080614e0a5fa0aaaa808824f21c38d692e6ffbd200f7dfb3c8f44402a7382180b98ad0afc8eec1a02acecf3cb7fde627b9f18111f260ab1db9a0732fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:
941cac69fb7b1815c57bb987c4d6c2ad2c35d5f9a3182a79d4ba13eab253a8ad23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:0f8fad1e6bde771b4f5420eac75c378bae6db5ac6650cd2bc210c1823b432b48e016b10595458ffab92f7a8989b293ceb8dfed6c243a2038fc06652aaaf16f02bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:
1acdbb793b0384934627470d795c3d1dd4d79cea59ef983f295b9b59179cbb283f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:3f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:7cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:be71ef4806cb041d885effd9e6b0fbb73d65d7cdec47a89c8a994892f4e55a568c4cc78d61f901e80dbb628b86a23ccd594e712b57fa94c2d67ec266348785077cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:
8ed7a797b9cea8a8370d419136bcdf683b759d2e3c6947f17e13e2485aa9d420b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:a750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:04266c033b91c1322ceb3446c901ffcf3cc40c4034e887c9597ca1893ba7330becbbd8b48142ef35c012c6ba51a66df9308cb6268ad6b1e4b03e70102495790ba750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:
f2ab396fe8906e3e5633e99cabcd5b09df0859b516230b1e0450b580b65f616c8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:a06a23d982d81ab883aae230adbc368a6a9977f003cebb00d4c2e4018490191a84d3a282fdbfb2fc88046e62de43e15fb575336b3c8b77d19ce6a009ce51f50c5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:
550a41c013f79bab8f06e43ad1836d51312736a9713806fafe6645219eaa1f9daf6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:af6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:8bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:16dc1e2b9fa909eefdc277ba16ebe207b8da5e91143cde78c5047a89f681c33c4e4e3428d5c928095903a811ec002d52a39ed7f8b3fe1927200c6dd0b9ab3e048bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:
19ac3e272438c72ddf7b881964867cb3b31ff4c793bb7ea154613c1db068cb7ef85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:f85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:95872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:ea855d781cbea4682e350173cb89e8619ccfddb97cdce16f9a2f6f6892f46dbe68e04b12b8d88689a7a31670cdff409af98a93b49a34537b6aa009d2eb8b470195872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:
ca267de96c93c238fafb1279812059ab93ac03059657fd994f8fa5a09239c821017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:e05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:ac957f82335aa7141e96b59d63e3ccee95c3a2c47d026540c2af42dc9533d5fd81827d1679ad187aeaf37834915e75b147a9286806c8017516ba43dd051a5e0ce05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:
3dff5e899475e7e91dd261322fab09980c52970de1da6e2e201660cc4fce7032f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:5efe7a92ff9623089b3e3b78f352115366e26ba3fb1a416209bc029e9cadccd9f4affa333555a8f3a35a9d0f7c34b292cae77ec96fa3adfcaadee2d9ced8f805938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:
9a6b847864e70cfe8ba6ab22fa0ca308c0cc8bec7141fbcaa3b81f5d1e1cfcfc34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:2ab255169c489c54c732232e37c87349d486b1eba20509dbabe7fed329ef08fd75ba1cd145e67b2ea26cb5cc51cab343eeb085fe1fd7b0ec4c6afcd9b979f905838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:
575be07afca5d063c238cd9b8028772cc49cda34471432a2e166e096e2219efc94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:33e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:58271d44236f3b98c58fd7ae0d2f49ef2b6e3affdb225aa3ba555f0e11cc53c23ad19baf24346590d05d7d5390582082cf94d39cad6530ab93d13efb3927950633e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:
15ffb45514d43444d61fcb105e30e135fd268523dda20b82758b1794231104411772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:1772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:6828cd7624e793b8a4ceb96d3c2a975bf773e5ff6645f353614058621e58835289e7f31f42dfe6af6d736f2644511e320c0fa698582a79778d18730ed3e8cb08da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:
fe0568642943b2e1afbfd1f10fe8df87a4236bea40dce742072cb21886eec1fa299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:d59e6dfcc6d7e3e2c58dec81e985d245e681acf6594a23c59214f7bed8015d813c7682b60b3583440311e72a8665ba2c96dec23ce826e160127e18132b030404c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:
5ecb16c2df27c8cf58e436a9d3affbd58e9538a92659a0f97c4c4f994635a8cada768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:da768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:1c723a20c6772426a670e4d5c4a97c6ebe9147f71bb0a415631e44406e290322e4ca977d348fe7856a8edc235d0fe95f7ed91aefddf28a77e2c7dbfd8f552f0a56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:
d599d637b3c30a82a9984e2f758497d144de6f06b9fba04dd40fd949039d7c846791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:6791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:ebf10d9ac7c96108140e7def6fe9533d727646ff5b3af273c1df95762a66f32b65a09634d013f54b5dd6011f91bc336ca8b355ce33f8cfbec2535a4c427f8205a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:
30ab8232fa7018f0ce6c39bd8f782fe2e159758bb0f2f4386c7f28cfd2c85898ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:9af885344cc7239498f712df80bc01b80638291ed4a1d28baa5545017a72e2f65649ccf9603da6eb5bfab9f5543a6ca4a7af3866153c76bf66bf95def615b00c63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:
0ddcdc872c7b748d40efe96c2881ae189d87f56148ed8af3ebbbc80324e38bdd588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:65641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:c179c09456e235fe24105afa6e8ec04637f8f943817cd098ba95387f9653b2add181a31447d92d1a1ddf1ceb0db62118de9dffb7dcd2424057cbdff5d41d040365641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:
89f0d68299ba0a5a83f248ae0c169f8e3849a9b47bd4549884305c9912b46603aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:2c691fa8d487ce20d5d2fa41559116e0bbf4397cf5240e152556183541d66cf753582401a4388d390339dbef4d384743caa346f55f8daba68ba7b9131a8a6e0b4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:
0a3c1844e2db070fb24e3c95cb1cc6714ef84e2ccd2b9dd2f1460ebf7ecf13b172e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:72e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:4c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:87f7fdf46095201e877a588fe3e5aaf476bd63138d8a878b89d6ac60631b3458b9d41a3c61a588e1db8d29a5968981b018776c588780922f5aa732ba6379dd054c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:
c8d7a8818b98dfdb20839c871cb5c48e9e9470ca3ad35ba2613a5d3199c8ab2390d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:90d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:fa2e994421aef1d5856674813d05cbd2cf84ef5eb424af6ecd0dc6fdbdc2fe605fe985883312ecf34f59bfb2f1c9149e5b9cc9ecda05b2731130f3ed28ddae0b783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:
b482703612d0c586f76cfcb21cfd2103c957251504a8c0ac4c86c9c6f3e429fffd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:fd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:29d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:58832bdeb26feafc31b46277cf3fb5d7a17dfb7ccd9b1f58ecbe6feb979666828f239ba4d75219260ecac0acf40f0e5e2590f4caa16bbbcd8a155d347967a60729d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:
84e50dd9a0f197e3893c38dbd91fafc344c1776d3a400e2f0f0ee7aa829eb8a22c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:2c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:69e6a4491a63837316e86a5f4ba7cd0d731ecc58f1d0a264c67c89befdd8d3829d8de13b33cc0bf513931715c7809657e2bfb960e5c764c971d733746093e500f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:
b322d46577a2a991a4d1698287832a39c487ef776b4bff037a05c7f1812bdeeceb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:eb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:19f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:c7b55137317ca21e33489ff6a9bfab97c855dc6f85684a70a9125a261b56d5e6f149c5774d734f2d8debfc77b721896a8267c23768e9badb910eef83ec25880219f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:
960cab5034b9838d098d2dcbf4364bec16d388f6376d73a6273b70f82bbc98c05e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:5e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:27d4c3a1811ef9d4360b3bdd133c2ccc30d02c2f248215776cb07ee4177f9b13fc42dd70a6c2fed8f225c7663c7f182e7ee8eccff20dc7b0e1d5834ec5b1ea01f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:
eb77b2638f23eebc82efe45ee9e5a0326637401e663ed029699b21e6443fb48e9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:99e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:18dc56d7bd9acd4f4daa78540b4ac8ff7aa9815f45a0bba370731a14eaabe96df8b5f37dbf8eae4cb15a64b244651e59d6a3d6761d9e3c50f2d0cbb09c05ec0699e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:
b625aa89d3f7308715427b6c39bbac58effd3a0fb7316f7a22b99ee5922f2dc965a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:65a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:e07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:01bb901d83b8b682d3614af46a807ba2691358feb775325d3423f549ff0aa5757e4e1a74e9c70f9721d8f354b319d4f4a1d91445c870fd0ffb94fed64664730de07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:
b1c9f8bd03fe82e78f5c0fb06450f27dacdf716434db268275df3e1dc177af427fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:7fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:4b229951ef262f16978f7914bc672e7226c5f8379d2778c5a2dc0a2650869f7acfbd0bcd30fdb0619bb44fc1ae5939b87cc318133009c20395b6c7eb98107701331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:
6d8cdb2e075f3a2f86137214cb236ceb89a6728bb4a200806bf3557fb78fac6957a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:57a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:a6cbc947f9c87d1455cf1a708528c090f11ecee4855d1dbaadf47454a4de55fa4ce84b36d73a5b5f8f59298ccf21992df492ef34163d87753b7e9d32f2c3660b7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:
47adc6d6bf571ee9570ca0f75b604ac43e303e4ab339ca9b53cacc5be45b2ccba3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:a3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:ce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:4e8c318343c306adbba60c92b75cb0569b9219d8a86e5d57752ed235fc109a43c2cf4e942cacf297279fbb28675347e08027722a4eb7395e00a17495d32edf0bce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:
3c19b50b0fe47961719c381d0d8da9b9869d312f13e3298b97fb22f0af29cbbe0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:8ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:efbd41f26a5d62685516f882b6ec74e0d5a71830d203c231248f26e99a9c6578ec900d68cdb8fa7216ad0d24f9ecbc9ffa655351666582f626645395a31fa7048ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:
34e1e9d539107eb86b393a5ccea1496d35bc7d5e9a8c5159d957e4e5852b3eb00ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:0ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:32d22904d3e7012d6f5a441b0b4228064a5cf95b723a66b048a087ecd55920c31c204c3f2006891a85dd1932e3f1d614cfd633b5e63291c6d8166f3011431e09a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:
49dd473ede6aa3c866824a40ada4996c239a20d84c9365e4f0a4554f8031b9cf788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:d2fde02791e720852507faa7c3789040d9ef86646321f313ac557f4002491542dd67d05c6990cdb0d495501fbc5d5188bfbb84dc1bf6098bee0603a47fc2690f3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:
331c64da482b6b551373c36481a02d8136ecadbb01ab114b4470bf41607ac57152a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:52a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:20e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:22c99aa946ead39ac7997562810c01c20b46bd610645bd2d56dcdcbaacc5452c74fbf4b8b1813b0e94c30d808ce5498e61d4f7ccbb4cc5f04dfc6140825a960020e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:
5c0b96f2af8712122cf743c8f8dc77b6cd5570a7de13297bb3dde1886213cce20510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:0510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:06e5d8436ac7705b3a90f1631cdd38ec1a3fa49778a9b9f2fa5ebea4e7d560ada7dd26ff42fafa8ba420323742761aca6904940dc21bbef63ff72daab45d430b54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:
bf5ba5d6a49dd5ef7b4d5d7d3e4ecc505c01f6ccee4c54b5ef7b40af6a4541401be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:1be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:16152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:279cace6fdaf3945e3837df474b28646143747632bede93e7a66f5ca291d2c24978512ca0cb8827c8c322685bd605503a5ec94dbae61bbdcae1e49650602bc0716152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:
65de297b70cbe80980500af0561a24db50001000125f4490366d8300d3128592ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:7a9b736b01cc92a3349f1a3c32dbd91959825394ff443c567405e899c8185ce8fad9500e1fce89d95a6253c00477435acf04bff993de1b00495def0834ee1f07131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:
0826e7333324e7ec8c764292f6015d4670e9b8d7c4a89e8d909e8ef435d18d15ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:7f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:4bac7fabec8724d81ab09ae130874d70b5213492104372f601ae5abb10532799373c4dad215876441f474e2c006be37c3c8f5f6f017d0870414fd276a8f428087f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:
00ad6227977b5f38ccda994d928bba9086d2daeb013f8690db986648b90c1d4591a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:91a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:cb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:dc501db79fd782bc88cae792557d5d273f9ba560c7d90037fe84ac879d684f612a77452c4443e95c07b8be192c35769b17bbdfca42280de796d92119d833670dcb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:
1521c6dbd6f724de73eaf7b56264f01035c04e01c1f3eb3cbe83efd26c439ada2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:3e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:a817ed23ec398a128601c1832dc6af7643bf3a5f517bcc579450fdb4759028f4966164125f6ebd0d6bf86ff298a39c766d0c21fdb0cbfdf81cd0eb1f03cd8a083e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:
17e5f0a8f34751babc5c723ecf339306992f39ea065ac140fcbc397d2dd32c4b4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:efe2cb63fe7b4fc98946dc82fb6998e741ed9ce6b9c1a93bb45bc0a7d8396d7405282b43fe363ba5b23589f8e1fae130e157ce888cd72d053d0cc19d257a4300c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:
0cd7aa7d605e44d5ffb97966b2cb93c189e4c5a85db87fad7ab8d62463c59b594889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:4889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:28a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:bf9115fd3d02706e398d4bf3b02a82674ff3041508fd39d29f867e501634b9261f516a794f98738d7c7013a3f2f858ffdd08047fb6bf3dddfb4b4f4cbeef300328a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:
33371d9e892f9875052ac8e325ba505e7477c1ace24ba7822643d43d0acef3de35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:27a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:985ca446ddc007827cc8f2852cbd8115ef8c5975e9d7ce96d74dfed859aa14a4c15254006bea5e08359efe2625d715e0897ee5a16f151203be5010418637de0527a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:
beedb8073df58f8c1bffbdbd77ec7decb2c82a9babecefc0331507bdc2c2a7e7b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:35ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:8c890cccadc7760e1e82e43c44b3dc0b685a48b479ae13cc0a6b0557d0fb1cbabba63d2a96843412ea8d36c50acbf52b92cfb2dce49dc48af6ddcf8ee47a860835ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:
9184ef618816832592bc8eb35f4ffd4ff98dfbf7776c90f2aad212ce7e03351e687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:b3c24e75132c563475422d5ea412b5c1e8e6e5ea1c08ead1393c412da134c9a1638284ea7e2ca032fe3d3e32a9066a8c8839903f6ef46e966bb5e492d8c2aa00729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:
354e13152ee1fe748a1252204c6527bdc1b1eb2eb53678150e6359924708d812d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:8e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:de2b46e65f3decef34332e500f2e11306fbdcf1be85a1c1ee68ba3045dcec2c7be608d22927da1f44c0e2083ae622cf3c29d893887994efcfa2ca594f5051f038e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:
7ff62d4b3c4d99d342d4bb401d726b21e99f4ef592149fc311b68761f5567ff67fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:7fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:058f79927fbf6178724815c7b11c63baaa90bcc15d7272be082f8a9141861c816433055f6cf6491424853f9ec78bb91ace913a93411b4e5ed58bc4ba5715c60a99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:
6cabadd03f8a2e6ebab96a74f80e18164e4d1b6baa678f5a82e25604af989aaf2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:4e65c6c1d493045e8a9250e397c1d1d30ffed24db66a8961aa458f8f0fcb760c39fe8657d7ab8f84000b96d519717cff71f926522c1efec7f8b2624eae55f60c279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:
0fa0c32c3ae34be51b92f91945405981a8e202488558a8e220c288c7d6a5532dd6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:d6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:53f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:7e9ab85ee94fe4b35dcb545329a0ef25923de5c9dc23e7df1a7e77ab0dcfb89e03f4e785ca6429cb2b0df50da6230f733f00f33a45c4e576cd40bdb84f1ae00153f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:
7b06f88026fa86f39fce2426f67cc5996bedd0cfc4b5ebb1b5e3edbb47e080aa3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:71175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:42f133e34e3eb7032a133ed781537ec62e44a5ce8381e5e0bf9e13a914a4b2c757811d6d3b1e86672424ea4230d10f7c610abb7069e61e319b4066a2bd7bc90071175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:
c3f5e149968a24f4de9119531975f443015ccca305d7119ed4749e8bf6d94fc739aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:39aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:5fa2b531677b00b85b0a313cbd479f55f4ab3ec5cfce5e454d2b74176ccc3399c899f9d6b51ed4c1e76185ac9fe730c4b4014044f7041185bc3c85722eb2ea02c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:
42305c9302f45ea6f87e26e2208fd94b3c4ad037b1b6c83cf6677aa1096a013c3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:d110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:18d05e5d01668e83f40fa3bbee28b388acf318d1b0b5ad668c672f345c8eda14c2f884cd2a9039459ce0810bc5b580fe70d3964a43edb49e73a6ff914bbf040cd110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:
c57a43dcd7bab8516009546918d71ad459b7345efdca8d4f19929875c839d7222083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:2083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:1edef9bc036971f1fa88edf45393c802e6c1a1631c8a06871a09a320821dce40beca97e53a0361a955a4c6d60b8ca8e400c81340911ccb4f56284041cdbb1804a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:
2dddb6b8fd04fa90ece1a709f8418f2e5d0c9c43afe7cfce19e6ad15a73476f78059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:8059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:c634ea7bf72e895a2e796e2834201415b8b45e05e045559284eb9052c0e84f62a5a9f0c9764f7576788c7228b19ef517c195497325a48a9344b147c12fd75509474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:
5547f1004baedfce5cfc0850b05302374aad24f6163994ecd751df3af3c106207ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:7ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:29df3ad589009c667baa5e72dabb4e53cb7876de4e7efe5cc21ead7fa878db57f97c1103ddb39a861eb88653c1d4ec3b4306e4584b47b8bc90423119e7e4af00a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:
3dd7203c237aefe9e38a201ff341490179905f9f100828da18fcbe58768b5760f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:4c036935a96abc0d050d907bedbe9946fb97439f039c742e051ccf09add7df44d17da98c2ca01bdc2424da1e4debf347f8fff48ac8030d2cc07f9575c044be04db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:
282775df9ebbd7c5a65f3a2b096e36ee64a8f8ea719da77758739e4e7476111da2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:a2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:14cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:15763973859402907d8dcb86adc24a2a168ba3abf2246173d6348afed51ef60b0c0edeff4e10bcef4c6e5778c8bc1f5e9ee0237373445b455155d23de127a20214cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:
4730a5cf9772d7d6665ba787bea4c95252e6ecd63ec62390547bf100c0a46375f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:552c7347bdfe131646ce0932d82a36d2c1b76d7c30ee890e0592e19f9d18b9a56f48d7a9b68c017da6b550c943af4a907baf317e419fbbc96f6cf4bfad42de00e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:
2770aadd1d123e9547832dfb2a837eba089179ef4f23abc4a53f2a714e423ee23c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:3c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:f267715e9a84c7314f2d5869ef4ab8d2149a13f7e8e1c728c423906293b49ce6283454dd1c7b04741df2eabedc4d6ab1397dc95a679df04d2c17d66c79bb7601a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:
4fdab7c1600e70114b11f533242376af7614b4d5da046ac4bedea21d8a361598a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:5075c090cfbeb6b01802af7f4da5aa4f434d5ee2f3530eebb75c85e08621f83edc08aa96693894a4277633ba81e19e9e55af5c495daa5e1a6f8cbb79c01c7207da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:
264504604e70d72dc4474dbb34913e9c0f806dfe18c7879a41762a9e4390ec61eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:eea439a00f7e459b402b835150a779eed171ab971bd1b58dcc7f9386dadd583de8dc69e267121dde41f0f9493d450b16219cdf3c22f09482ce402fe17ca49e08901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:
2ca7447a3668b748b1fd3d52d2080d30e34d397bb2846caf8f659ac168788ca5ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:f93ada15ae9cd2b54f26f86f0c28392aed5eb6b6b44d01a4e33a54e7da37c38e8d53366f73fd85be642e4ec81236d163f0d025e76c8bbdd65d43df49f09c1f01a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:
494ea9bcce26885b7d17d1fc114448f239f0ce46e5f247b4c999fa86296924726901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:6901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:3badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:548a093a680361b7dc56f14503b55eeec3b3f4fd4ca99d6aedce0830f7f4ae2f7328539b34c48fc9760922333dae9c7c017e7db73b8faa6c06be05e347992b063badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:
00d735ebaee75dd579a40dfd82508274d01a1572df99b811d5b01190d82192e4ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:59c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:dcdc54611937d2bd06cacd9818b3be15ce7425427a75f50d197a337a3b8ba6714ef48866f243bd5ac7415e914517a2c1c5a953f432b99db0e620d64f74eb850559c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:
8c34b905440b61911d1d8137c53d46a1a76d4609af973e18eb4c5709295627bbb69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:b69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:3e0b72073dc9375eedcca6c4fc1cd315938a050c92716bd2284f4629a962beec0b7d7cf16ab923d58f5b90d3901a8e5c75c8f17dab9998e007d8c49511973d0e30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:
77a83e18c9f000eeff7deeac959ecba2206c0aa39d2f0e2aed5729482a7a022962b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:62b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:1eaad8420ac12c99ac1ff4476678e3cbbe94da6a797f174664d5ee0f641433fb1e7cb2f5613e10805df8654cd8e0d45d96230932bc7f20b04eae836435134309f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:
73b03373ef1fd849005ecd6270dd9906f19f4439e40376cdbc520902bc976812663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:a40abe98fc69da8a1ff9ff5c2cca93632e975980ee8b82c3c376022d6524ab736d01b072f2b681b5f1cd3ea067012ed6d074e949c42327a366caa9e4750a3c08d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:
eab179e41ed5c889ffe6aabdc054faf1307c395e46e313e17a14fe01023ffa3086f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:86f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:143cb28027c2f82e375e5f340e7fe6e60ce7bd51000b49c74168af85e26ed2ed630ed2672090164cc54b052da694ebdd21a21b3053f4dcfd7895ea5f6c8aa80d971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:
fbf146ebd51075570ec51ac410ae9f391db75b610ada6362b4dbd949656cfb66be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:cd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:6768006fe0f201b217dd10eb05d4b82adcfeb2ecfc8373c3308f4150394811eb60491881a2e53d1289d96478e18a64c34b2a19832cdccfd96a2e4a0c469fdc0bcd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:
dff0eb6b426dea2fd33c1d3fc24df9b31b486facb7edb8502954a3e8da99d9fdc245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:c245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:6b48b10f545ddb7a89cd5829f4e5b20146cf6bc96e550d06f65de8bdae7ccdded26cd630f86c9266bccf88e924033e04f83a54f8290d7f734cf8673cca8f9703e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:
9f32958c7679b90fd5036056a75ec2eb2f56ec1effc7c012461dc89a3a1674201d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:1d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:9881a5763bdb259a3fefbba3d957162d6c70b804fa94ab613406a6ec42505b8789465ca1a9a33e1895988842270c55e5bdd5483f6b17b31781b593507a6c1808a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:
f86d6f766f88b00717b7d6327eb26cf3ceeba5385184426f9cfd8295e2421ff2cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:da8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:ec61c0b292203a8f1d87235ede92b74723c8d23408423773ae50b1e9bc4464e03e446da9dce4c39f6dd159bea26c009ed00120bc36d4a247dc0d24bcefcc110cda8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:
a5b34cefab9479df8389d7e6f6c146aa8affb0bec837f78af64624a145cc344e7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:2fbd899d72b6d39e4f45b8b62cbbd5f3c0acb1ad8540913fa585877e91ccfef7bee50a4b0f9fedf5cc1e0d1953ad399c8389a93391e1b7c929af6d6f3b796c08e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:
ad75c9ce299c4d59393367d77a4c9f8df8dcec765c6dbd25b527fb7669913604b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:62fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:6b7ef27bcfbf2b714985033764fccff555e3f5bc44610d6c8c62117cb3831a07f4a8bddb0eaed1d46b0289b15de1aa4dcc17d71be96a09e66ba4dc4627c7870562fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:
1ced574529b9b416977e92eb39448a8717cac2934a243a5c44fb44b73ccc16da85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:e0303aefe08a77738dcc657afbb9b835ed279613a53c73fdc5ddbfb350e5cff4d6c9bb43dc07c95bf4e23b64c40f8804c7169952e3c8d59a7197241bfed0740f1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:
f0790d93e2d3b84f61ef4c807147aba410e415e72b71b0d61d01026fed99da3defdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:efdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:7973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:08773a6a78762cbb1e25fcbb29139941bdf16f4e09a1fa08fc701f32f933edd74c0ae983c12a0a5b020b6bcf44bb719dde8ed0781a8298265640e1608c98b3017973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:
4cb9df7ce6fae9d62ba09e8eb70e4c969bdeafcb5ec7d7024326e6603b0621bf018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:14627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:e33c07836c537d6bfbd0f4592d6e35b163499ba78dc7ffcec565d04f9a7db781943e29e6ce76763e9baddf57437fd9c6b03239a6e6850e4502a356c2e12c370514627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:
a136e009d53e5ef59d0946bc175663a86bc0fcd29eadd95cfc9d266037b1e4fb9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:bc094ba91c115dee15d753361a75f3f03d6af45c92157e95dbe8d32194b6c5ce72b9dc66f73df12dca0b639f3e791d478616a1f8d7359a42c8eae0dda16b1606a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:
ff0f1c57dd884fbeea6e2917282b79ba67f8a6851267b9f4636dafda33bd2b5bfef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:fef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:d5008486726cce330a29dd7e4d7474d735798201afd1206feb869a112e5b43523c06976761be3cf9b2716378273c94f93572a7d2b8982634e0755c632b449008522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:
0bc6af64de5709d3dbc28f7ef6d3fe28b6de529f08f5857ccb910695de454f56fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:dbc7134d1cd6b0813b53352714b6df939498e91cf37c324337d9c088a1b998347d26185b430900412929e4f63e910379fc42e355a4e98f6fee27dafad1957206ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:
2f5e83bd5b412e71ae3e9084cd369efcc79bf6037c4b174dfd6a11fb0f5da218a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:b766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:9f80922bc8db32d0cc43f9936affebe7b2bc35a5d82277cd187b5d50dc7fc4c4832fffa34e9543806b485c04548e7c75429425e14d55d91fc1052efd8667430bb766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:
722a2da50e42c11a61c9afac7be1a2fed2267d650f8f7d8e5bc706b807c1b91dfd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:fd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:c2695a57172aaa31bd0890f231ca8eeec0287a87172669a899ad0891cea4c47579b50420e791cdec8c182c8a0e8dde21b2480b0cfd8111e28e5603347a352d04173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:
5fe9c3960ed5bd374cc94d42357e6a24dc7e3060788f726365defacf13cd12da0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:379f9c54c413af0d192e9bc736b29da9d521e7ba7841d309f9bcc1e742ec4308fe9f7ba51e0b22aed487cb4aa3913b9bebfb3aacd38f4039f9bbbebe1ad80002c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:
ec2fa541ac14b414149c3825eaa7001b795aa1957d4040dda92573904afa7ee471b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:71b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:2749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:84d18d56f964e3776759bba92c510c2b6d574555c3cddade212da90374554991e7d77e278d63e34693e1958078cc3685f8c41c1f5342e351899638ef612114012749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:
6132692a5ef27bf476b1e991e6c431a8c764f1aebd470282db3321bb7cb09c207a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:7a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:a9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:eb677f3347e1a1ea929efdf62bf9105a6c8f4993033b4f6d03cb0dbf9c742b270704e383ab7c0676bdb1ad0ce9b16673083c9602ec10ae1dd98e8748b336440ba9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:
f219b2101164aa9723bde3a7346f68a35061c01f9782072580ba32df903ba891f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:17f0127ca3bafa5f4ee959cd60f772be87a0034961517e39a0a1d0f4b9e26db1336e60c82b352c4cbacdbbd11771c3774f8cc5a1a795d6e4f4ebd51def36770b015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:
fc180035aec0f5ede7bda93bf77ade7a81ed06de07ee2e3aa8576be81608610a4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:a43a71c3a19c35660dae6f31a254b8c0ea3593fc8fca74d13640012b9e9473d4afe070db01e7fb399bf4ca6070e062180011285a67dd6858b761e46c6bd32004b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:
a2836a65427912122d25dcdfc99d7046fe9b53d5c1bb23617f11890e94ca93ed8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:e6a9a6b436559a4320c45c0c2c4a2aedecb90d416d52c82680ac7330d062aebef3e9ac9f2c5ffa455c9be113013a2b282e5600fd306435ada83b1e48ba2a3605813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:
f051af426d0c3282fafc8bf912ade1c24211a95ad200e1eef549320e1cb1a252fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:b48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:b8f713578a64466719aceb432fce302a87cf066bf3e102a350616921a840964bfc7e685d8fd17455ac3eb4861edcb8979d35e3a4bd82a078cd707721d733400eb48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:
a103e92672c65f81ea5da1fff1a4038788479e941d503a756f4a755201a57c1dee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:ee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:2aa2035c2ce5b5e6ae161e168f3ad0d6592bcf2c4a049d3ed342fceb56be9c7cb372027573ae0178e8878ebefca7b030327b8aad41857de58cb78e1a00cbac05b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:
d47c1b4b9e50cbb71fd07d096d91d87213d44b024373044761c4822f9d9df880f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:88d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:627e7ca7e34ed6331d62b9541c1ea9a9292be7b0a65d805e266b5122272a82db7d765acc7e2a290d685804922f91ed04a3c382c03ff21a1768f584413c4e5f0088d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:
fc0c32c5eb6c71ea08dc2b300cbcef18fdde3ea20f68f21733237b4ddaab900e47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:6887c6e2b98a82af5ee3dfa7ca2cb25d9c10745620a82956acba85cb57c8ec24279fa42f092359a1b6bbeafba050f14b6288209e6ef7bc1e0a2b872c1138f305a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:
a8d73d639a23cc6a967ef31bcabb5d063e53e1eab8fcc7cab9bc3a17fde9c2f88daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:8daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:fd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:c4dcef1a2453939b364b340250c3129431431d5ba3f47670ab07ce680c69bf28b678627c76a6360fc40dc109aa7dea371b825e46134f624572182acf3957e70ffd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:
79c7dcb7d59a8df6b2b2ba0413059d89680995c20e916da01b8f067dc60cdeb4298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:7b7cbe44c771e4371bae13b0722babcc1064155732962f407cba2acd35381d42210bece822f4681121fd4dab745a1f3077922fba1a78045b712902baccac660e5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:
b9ced0412593fefed95e94ac965e5b23ff9d4b0e797db02bf497994d3b793e60c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:f1b797ded8a6942b12626848340fb719fcddafd98f33e2992d357bfdd35933c7ac561e5b2f939464338c5666854ca885c4d046eb2c54e48a1b5ed266ad34de05555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:
81da168f02d46bb87cda845da43f8a6cba2c016878d6f49c6f061a60f155a04aaff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:aff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:4aaca947e3f22cc8b8588ee030ace8f6b5f5711c2974f20cc18c3b655b07a5bc1366b59a1708032d12cae01ab794f8cbcc1a330874a75035db1d69422d2fc00c6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:
af2e60da0f29bb1614fc3f193cc353331986b73f3f9a0aec9421b9473d6a4b6ac8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:c8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:7dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:50f9f941a8da9f6240f76d2fa3b06dd6b2292ed32d1c05218097d34d8a19dfe553f76ae3c6b4a2ed20852128461540decf418f52d38e64037eec7771bd1afe007dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:
605f90b53d8e4a3b48b97d745439f2a0807d83b8502e8e2979f03e8d376ac9feaa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:aa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:3bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:dd0212e63288cbe14a4569b4d891da3c7f92727c5e7f9a801cf9d6827085e7095b669d7d45f882ca5f0745dccd24d87a57181320191e5b7a47c3f7f2dccbd7073bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:
9e2c3d189838f4dd52ef0832886874c5ca493983ddadc07cbc570af2ee9d6209f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:38a31b6b465084738262a26c065fe5d9e2886bf9dd35cde05df9bad0cc7db401c750aa19e66090bce25a3c721201e60502c8c10454346648af065eab0ee7d80f19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:
575f8fb6c7465e92c250caeec1786224bc3eed729e463953a394c9849cba908f71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:2cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:903b484cb24bc503cdced844614073256c6d5aa45f1f9f62c7f22e5649212bc1d6ef9eaa617b6b835a6de2beff2faac83d37a4a5fc5cc3b556f56edde2651f022cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2018-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
use criterion::{criterion_group, Criterion};
mod ed25519_benches {
use super::*;
use ed25519_dalek::Signature;
use ed25519_dalek::Signer;
use ed25519_dalek::SigningKey;
use rand::prelude::ThreadRng;
use rand::thread_rng;
fn sign(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let keypair: SigningKey = SigningKey::generate(&mut csprng);
let msg: &[u8] = b"";
c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg)));
}
fn verify(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let keypair: SigningKey = SigningKey::generate(&mut csprng);
let msg: &[u8] = b"";
let sig: Signature = keypair.sign(msg);
c.bench_function("Ed25519 signature verification", move |b| {
b.iter(|| keypair.verify(msg, &sig))
});
}
fn verify_strict(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
let keypair: SigningKey = SigningKey::generate(&mut csprng);
let msg: &[u8] = b"";
let sig: Signature = keypair.sign(msg);
c.bench_function("Ed25519 strict signature verification", move |b| {
b.iter(|| keypair.verify_strict(msg, &sig))
});
}
#[cfg(feature = "batch")]
fn verify_batch_signatures(c: &mut Criterion) {
use ed25519_dalek::verify_batch;
static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256];
// Benchmark batch verification for all the above batch sizes
let mut group = c.benchmark_group("Ed25519 batch signature verification");
for size in BATCH_SIZES {
let name = format!("size={size}");
group.bench_function(name, |b| {
let mut csprng: ThreadRng = thread_rng();
let keypairs: Vec<SigningKey> = (0..size)
.map(|_| SigningKey::generate(&mut csprng))
.collect();
let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect();
let signatures: Vec<Signature> = keypairs.iter().map(|key| key.sign(msg)).collect();
let verifying_keys: Vec<_> =
keypairs.iter().map(|key| key.verifying_key()).collect();
b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..]));
});
}
}
// If the above function isn't defined, make a placeholder function
#[cfg(not(feature = "batch"))]
fn verify_batch_signatures(_: &mut Criterion) {}
fn key_generation(c: &mut Criterion) {
let mut csprng: ThreadRng = thread_rng();
c.bench_function("Ed25519 keypair generation", move |b| {
b.iter(|| SigningKey::generate(&mut csprng))
});
}
criterion_group! {
name = ed25519_benches;
config = Criterion::default();
targets =
sign,
verify,
verify_strict,
verify_batch_signatures,
key_generation,
}
}
criterion::criterion_main!(ed25519_benches::ed25519_benches);

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1,12 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.css" integrity="sha384-Juol1FqnotbkyZUT5Z7gUPjQ9gzlwCENvUZTpQBAPxtusdwFLRy382PSDx5UUJ4/" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.js" integrity="sha384-97gW6UIJxnlKemYavrqDHSX3SiygeOwIZhwyOKRfSaf0JWKRVj9hLASHgFTzT+0O" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
<style>
.katex { font-size: 1em !important; }
</style>

Some files were not shown because too many files have changed in this diff Show More