Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c6d347563 | ||
|
|
fa03398012 | ||
|
|
5312a0311e | ||
|
|
b4f9e4df92 | ||
|
|
415892acf1 | ||
|
|
56bf398d0c | ||
|
|
9252fa5c0d | ||
|
|
1efe6a93b1 | ||
|
|
cc3421a22f | ||
|
|
858c4ca8ae | ||
|
|
31ccb67050 | ||
|
|
19c7f4a5d5 | ||
|
|
a62e4a5c57 | ||
|
|
17eab3d6c1 | ||
|
|
50401ab430 | ||
|
|
4ac84dd066 | ||
|
|
ff1c309b23 | ||
|
|
ba737a3790 | ||
|
|
0b45e00ad5 | ||
|
|
a12ab4e584 | ||
|
|
7051bd8dcf | ||
|
|
ba7a073487 | ||
|
|
a2ff6ba9e4 | ||
|
|
f08bbb7f57 | ||
|
|
04f811ad21 | ||
|
|
ac51ef6ecf | ||
|
|
89aabac235 | ||
|
|
72761ca6b4 | ||
|
|
3c85f778b3 | ||
|
|
78a86f1c49 | ||
|
|
f4cd43f606 | ||
|
|
81d0756bdc | ||
|
|
cd9378e6fd | ||
|
|
8a41a29939 | ||
|
|
b92421916d | ||
|
|
598695c400 | ||
|
|
e6675c67ce | ||
|
|
0cd099a9fb | ||
|
|
76a8b2a081 | ||
|
|
533b53a0ec | ||
|
|
c157a1ed6d | ||
|
|
e94a5fe5ab | ||
|
|
9db51a6bf7 | ||
|
|
8ed1666b97 | ||
|
|
1ec4a36a80 | ||
|
|
a3a08b01ab | ||
|
|
135476c9f5 | ||
|
|
5c5a32057c | ||
|
|
c8d1d400f1 | ||
|
|
60dd3100c0 | ||
|
|
594b1f9ffe | ||
|
|
c058cd9057 | ||
|
|
8e0cef5b72 | ||
|
|
4373695c50 | ||
|
|
098658dc8b | ||
|
|
b93ace8c7f | ||
|
|
c66973c823 | ||
|
|
bf2c4eea23 | ||
|
|
6dd17b2836 | ||
|
|
42b55fd117 | ||
|
|
345364d4ec |
44
.github/workflows/cross.yml
vendored
Normal file
44
.github/workflows/cross.yml
vendored
Normal 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
145
.github/workflows/curve25519-dalek.yml
vendored
Normal 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
33
.github/workflows/ed25519-dalek.yml
vendored
Normal 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
|
||||
131
.github/workflows/rust.yml
vendored
131
.github/workflows/rust.yml
vendored
@ -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
111
.github/workflows/workspace.yml
vendored
Normal 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
33
.github/workflows/x25519-dalek.yml
vendored
Normal 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
4
.gitignore
vendored
@ -1,6 +1,8 @@
|
||||
*/target/*
|
||||
target
|
||||
Cargo.lock
|
||||
|
||||
*/Cargo.lock
|
||||
build*.txt
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
|
||||
41
.travis.yml
41
.travis.yml
@ -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
|
||||
@ -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.
|
||||
@ -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
|
||||
|
||||
80
Cargo.toml
80
Cargo.toml
@ -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"]
|
||||
|
||||
8
Makefile
8
Makefile
@ -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
235
README.md
@ -1,226 +1,31 @@
|
||||
|
||||
# curve25519-dalek [](https://crates.io/crates/curve25519-dalek) [](https://doc.dalek.rs) [](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 |
|
||||
-------------------------------------------|----------------|-----------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
| [`curve25519‑dalek`](./curve25519-dalek) | A library for arithmetic over the Curve25519 and Ristretto elliptic curves and their associated scalars. | [](https://crates.io/crates/curve25519-dalek) | [](https://docs.rs/curve25519-dalek) | [](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml) |
|
||||
| [`ed25519‑dalek`](./ed25519-dalek) | An implementation of the EdDSA digital signature scheme over Curve25519. | [](https://crates.io/crates/ed25519-dalek) | [](https://docs.rs/ed25519-dalek) | [](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml) |
|
||||
| [`x25519‑dalek`](./x25519-dalek) | An implementation of elliptic curve Diffie-Hellman key exchange over Curve25519. | [](https://crates.io/crates/x25519-dalek) | [](https://docs.rs/x25519-dalek) | [](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.
|
||||
|
||||
8
curve25519-dalek-derive/CHANGELOG.md
Normal file
8
curve25519-dalek-derive/CHANGELOG.md
Normal 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
|
||||
19
curve25519-dalek-derive/Cargo.toml
Normal file
19
curve25519-dalek-derive/Cargo.toml
Normal 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"] }
|
||||
201
curve25519-dalek-derive/LICENSE-APACHE
Normal file
201
curve25519-dalek-derive/LICENSE-APACHE
Normal 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.
|
||||
23
curve25519-dalek-derive/LICENSE-MIT
Normal file
23
curve25519-dalek-derive/LICENSE-MIT
Normal 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.
|
||||
198
curve25519-dalek-derive/README.md
Normal file
198
curve25519-dalek-derive/README.md
Normal 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.
|
||||
466
curve25519-dalek-derive/src/lib.rs
Normal file
466
curve25519-dalek-derive/src/lib.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
141
curve25519-dalek-derive/tests/tests.rs
Normal file
141
curve25519-dalek-derive/tests/tests.rs
Normal 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);
|
||||
}
|
||||
@ -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
|
||||
74
curve25519-dalek/Cargo.toml
Normal file
74
curve25519-dalek/Cargo.toml
Normal 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
11
curve25519-dalek/Makefile
Normal 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
323
curve25519-dalek/README.md
Normal file
@ -0,0 +1,323 @@
|
||||
|
||||
# curve25519-dalek [](https://crates.io/crates/curve25519-dalek) [](https://docs.rs/curve25519-dalek) [](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
|
||||
@ -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
126
curve25519-dalek/build.rs
Normal 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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
251
curve25519-dalek/src/backend/mod.rs
Normal file
251
curve25519-dalek/src/backend/mod.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
49
curve25519-dalek/src/backend/serial/mod.rs
Normal file
49
curve25519-dalek/src/backend/serial/mod.rs
Normal 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;
|
||||
@ -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")]
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
4801
curve25519-dalek/src/backend/serial/u32/constants.rs
Normal file
4801
curve25519-dalek/src/backend/serial/u32/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
7769
curve25519-dalek/src/backend/serial/u64/constants.rs
Normal file
7769
curve25519-dalek/src/backend/serial/u64/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
@ -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]);
|
||||
}
|
||||
1191
curve25519-dalek/src/backend/vector/avx2/constants.rs
Normal file
1191
curve25519-dalek/src/backend/vector/avx2/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
@ -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]));
|
||||
@ -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};
|
||||
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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::*;
|
||||
@ -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};
|
||||
@ -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;
|
||||
337
curve25519-dalek/src/backend/vector/packed_simd.rs
Normal file
337
curve25519-dalek/src/backend/vector/packed_simd.rs
Normal 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() }
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
176
curve25519-dalek/src/backend/vector/scalar_mul/pippenger.rs
Normal file
176
curve25519-dalek/src/backend/vector/scalar_mul/pippenger.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
126
curve25519-dalek/src/backend/vector/scalar_mul/straus.rs
Normal file
126
curve25519-dalek/src/backend/vector/scalar_mul/straus.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
178
curve25519-dalek/src/constants.rs
Normal file
178
curve25519-dalek/src/constants.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
25
curve25519-dalek/src/diagnostics.rs
Normal file
25
curve25519-dalek/src/diagnostics.rs
Normal 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
@ -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]) {
|
||||
// Montgomery’s 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
117
curve25519-dalek/src/lib.rs
Normal 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;
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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) * γ
|
||||
|
||||
// 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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
46
curve25519-dalek/src/lizard/u32_constants.rs
Normal file
46
curve25519-dalek/src/lizard/u32_constants.rs
Normal 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,
|
||||
]);
|
||||
63
curve25519-dalek/src/lizard/u64_constants.rs
Normal file
63
curve25519-dalek/src/lizard/u64_constants.rs
Normal 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,
|
||||
]);
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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
@ -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;
|
||||
276
curve25519-dalek/src/window.rs
Normal file
276
curve25519-dalek/src/window.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
88
curve25519-dalek/tests/build_tests.sh
Executable file
88
curve25519-dalek/tests/build_tests.sh
Executable 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"
|
||||
@ -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
12
ed25519-dalek/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
target
|
||||
|
||||
.cargo
|
||||
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
*.swp
|
||||
*.orig
|
||||
*.bak
|
||||
|
||||
*.s
|
||||
33
ed25519-dalek/.travis.yml
Normal file
33
ed25519-dalek/.travis.yml
Normal 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
|
||||
52
ed25519-dalek/CHANGELOG.md
Normal file
52
ed25519-dalek/CHANGELOG.md
Normal 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
78
ed25519-dalek/Cargo.toml
Normal 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
28
ed25519-dalek/LICENSE
Normal 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
178
ed25519-dalek/README.md
Normal file
@ -0,0 +1,178 @@
|
||||
# ed25519-dalek [](https://crates.io/crates/ed25519-dalek) [](https://docs.rs/ed25519-dalek) [](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
128
ed25519-dalek/TESTVECTORS
Normal 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:
|
||||
11136
ed25519-dalek/VALIDATIONVECTORS
Normal file
11136
ed25519-dalek/VALIDATIONVECTORS
Normal file
File diff suppressed because it is too large
Load Diff
100
ed25519-dalek/benches/ed25519_benchmarks.rs
Normal file
100
ed25519-dalek/benches/ed25519_benchmarks.rs
Normal 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);
|
||||
BIN
ed25519-dalek/docs/assets/ed25519-malleability.png
Normal file
BIN
ed25519-dalek/docs/assets/ed25519-malleability.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
12
ed25519-dalek/docs/assets/rustdoc-include-katex-header.html
Normal file
12
ed25519-dalek/docs/assets/rustdoc-include-katex-header.html
Normal 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
Loading…
Reference in New Issue
Block a user