Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
829f52e79c |
44
.github/workflows/cross.yml
vendored
44
.github/workflows/cross.yml
vendored
@ -1,44 +0,0 @@
|
||||
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 }}
|
||||
117
.github/workflows/curve25519-dalek.yml
vendored
117
.github/workflows/curve25519-dalek.yml
vendored
@ -1,117 +0,0 @@
|
||||
name: curve25519 Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
paths: 'curve25519-dalek/**'
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
paths: 'curve25519-dalek/**'
|
||||
|
||||
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 }}
|
||||
|
||||
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 --target x86_64-unknown-linux-gnu
|
||||
|
||||
msrv:
|
||||
name: Current MSRV is 1.60.0
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# First run `cargo +nightly -Z minimal-verisons check` in order to get a
|
||||
# Cargo.lock with the oldest possible deps
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
# 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
|
||||
34
.github/workflows/ed25519-dalek.yml
vendored
34
.github/workflows/ed25519-dalek.yml
vendored
@ -1,34 +0,0 @@
|
||||
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
|
||||
# Now run `cargo +nightly -Z minimal-verisons check` in order to get a
|
||||
# Cargo.lock with the oldest possible deps
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
# 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
|
||||
35
.github/workflows/no_std.yml
vendored
35
.github/workflows/no_std.yml
vendored
@ -1,35 +0,0 @@
|
||||
name: no_std
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: '-D warnings'
|
||||
|
||||
jobs:
|
||||
|
||||
build-nostd:
|
||||
name: Build 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
|
||||
131
.github/workflows/rust.yml
vendored
Normal file
131
.github/workflows/rust.yml
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
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"
|
||||
88
.github/workflows/workspace.yml
vendored
88
.github/workflows/workspace.yml
vendored
@ -1,88 +0,0 @@
|
||||
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
|
||||
|
||||
clippy:
|
||||
name: Check that clippy is happy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: clippy
|
||||
- run: cargo clippy --target x86_64-unknown-linux-gnu
|
||||
|
||||
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
|
||||
34
.github/workflows/x25519-dalek.yml
vendored
34
.github/workflows/x25519-dalek.yml
vendored
@ -1,34 +0,0 @@
|
||||
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
|
||||
# Now run `cargo +nightly -Z minimal-verisons check` in order to get a
|
||||
# Cargo.lock with the oldest possible deps
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
# 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,8 +1,6 @@
|
||||
*/target/*
|
||||
target
|
||||
Cargo.lock
|
||||
*/Cargo.lock
|
||||
build*.txt
|
||||
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
|
||||
41
.travis.yml
Normal file
41
.travis.yml
Normal file
@ -0,0 +1,41 @@
|
||||
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
|
||||
@ -3,47 +3,6 @@
|
||||
Entries are listed in reverse chronological order per undeprecated
|
||||
major series.
|
||||
|
||||
## 4.x series
|
||||
|
||||
### 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
|
||||
@ -94,8 +53,6 @@ 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.
|
||||
@ -123,20 +80,12 @@ 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`
|
||||
@ -145,6 +94,9 @@ besides the `rand_core` version bump, there are no other user-visible changes.
|
||||
* 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
|
||||
8
CODE_OF_CONDUCT.md
Normal file
8
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,8 @@
|
||||
# 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 @rozbb or @tarcieri.
|
||||
ask @isislovecruft or @hdevalence.
|
||||
|
||||
Some issues are easier than others. The `easy` label can be used to find the
|
||||
easy issues. If you want to work on an issue, please leave a comment so that we
|
||||
|
||||
80
Cargo.toml
80
Cargo.toml
@ -1,12 +1,74 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"curve25519-dalek",
|
||||
"curve25519-dalek-derive",
|
||||
"ed25519-dalek",
|
||||
"x25519-dalek"
|
||||
[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",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 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"]
|
||||
|
||||
[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
Normal file
8
Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
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
|
||||
|
||||
236
README.md
236
README.md
@ -1,32 +1,226 @@
|
||||
<p align="center">
|
||||
|
||||
# curve25519-dalek [](https://crates.io/crates/curve25519-dalek) [](https://doc.dalek.rs) [](https://travis-ci.org/dalek-cryptography/curve25519-dalek)
|
||||
|
||||
<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>
|
||||
width="33%"
|
||||
align="right"
|
||||
src="https://doc.dalek.rs/assets/dalek-logo-clear.png"/>
|
||||
|
||||
# Dalek elliptic curve cryptography
|
||||
**A pure-Rust implementation of group operations on Ristretto and Curve25519.**
|
||||
|
||||
This repo contains pure-Rust crates for elliptic curve cryptography:
|
||||
[![curve25519 Rust]()](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml)
|
||||
`curve25519-dalek` is a library providing group operations on the Edwards and
|
||||
Montgomery forms of Curve25519, and on the prime-order Ristretto group.
|
||||
|
||||
| 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 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.
|
||||
|
||||
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.
|
||||
`curve25519-dalek` is intended to provide a clean and safe _mid-level_ API for use
|
||||
implementing a wide range of ECC-based crypto protocols, such as key agreement,
|
||||
signatures, anonymous credentials, rangeproofs, and zero-knowledge proof
|
||||
systems.
|
||||
|
||||
In particular, `curve25519-dalek` implements Ristretto, which constructs a
|
||||
prime-order group from a non-prime-order Edwards curve. This provides the
|
||||
speed and safety benefits of Edwards curve arithmetic, without the pitfalls of
|
||||
cofactor-related abstraction mismatches.
|
||||
|
||||
# Documentation
|
||||
|
||||
The semver-stable, public-facing `curve25519-dalek` API is documented
|
||||
[here][docs-external]. In addition, the unstable internal implementation
|
||||
details are documented [here][docs-internal].
|
||||
|
||||
The `curve25519-dalek` documentation requires a custom HTML header to include
|
||||
KaTeX for math support. Unfortunately `cargo doc` does not currently support
|
||||
this, but docs can be built using
|
||||
```sh
|
||||
make doc
|
||||
make doc-internal
|
||||
```
|
||||
|
||||
# Use
|
||||
|
||||
To import `curve25519-dalek`, add the following to the dependencies section of
|
||||
your project's `Cargo.toml`:
|
||||
```toml
|
||||
curve25519-dalek = "3"
|
||||
```
|
||||
|
||||
The sole breaking change in the `3.x` series was an update to the `digest`
|
||||
version, and in terms of non-breaking changes it includes:
|
||||
|
||||
* support for using `alloc` instead of `std` on stable Rust,
|
||||
* the Elligator2 encoding for Edwards points,
|
||||
* a fix to use `packed_simd2`,
|
||||
* various documentation fixes and improvements,
|
||||
* support for configurably-sized, precomputed lookup tables for basepoint scalar
|
||||
multiplication,
|
||||
* two new formally-verified field arithmetic backends which use the Fiat Crypto
|
||||
Rust code, which is generated from proofs of functional correctness checked by
|
||||
the Coq theorem proving system, and
|
||||
* support for explicitly calling the `zeroize` traits for all point types.
|
||||
|
||||
The `2.x` series has API almost entirely unchanged from the `1.x` series,
|
||||
except that:
|
||||
|
||||
* an error in the data modeling for the (optional) `serde` feature was
|
||||
corrected, so that when the `2.x`-series `serde` implementation is used
|
||||
with `serde-bincode`, the derived serialization matches the usual X/Ed25519
|
||||
formats;
|
||||
* the `rand` version was updated.
|
||||
|
||||
See `CHANGELOG.md` for more details.
|
||||
|
||||
# Backends and Features
|
||||
|
||||
The `nightly` feature enables features available only when using a Rust nightly
|
||||
compiler. In particular, it is required for rendering documentation and for
|
||||
the SIMD backends.
|
||||
|
||||
Curve arithmetic is implemented using one of the following backends:
|
||||
|
||||
* a `u32` backend using serial formulas and `u64` products;
|
||||
* a `u64` backend using serial formulas and `u128` products;
|
||||
* an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records);
|
||||
* an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records);
|
||||
|
||||
By default the `u64` backend is selected. To select a specific backend, use:
|
||||
```sh
|
||||
cargo build --no-default-features --features "std u32_backend"
|
||||
cargo build --no-default-features --features "std u64_backend"
|
||||
# Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2
|
||||
cargo build --no-default-features --features "std simd_backend"
|
||||
# Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma
|
||||
cargo build --no-default-features --features "std simd_backend"
|
||||
```
|
||||
Crates using `curve25519-dalek` can either select a backend on behalf of their
|
||||
users, or expose feature flags that control the `curve25519-dalek` backend.
|
||||
|
||||
The `std` feature is enabled by default, but it can be disabled for no-`std`
|
||||
builds using `--no-default-features`. Note that this requires explicitly
|
||||
selecting an arithmetic backend using one of the `_backend` features.
|
||||
If no backend is selected, compilation will fail.
|
||||
|
||||
# Safety
|
||||
|
||||
The `curve25519-dalek` types are designed to make illegal states
|
||||
unrepresentable. For example, any instance of an `EdwardsPoint` is
|
||||
guaranteed to hold a point on the Edwards curve, and any instance of a
|
||||
`RistrettoPoint` is guaranteed to hold a valid point in the Ristretto
|
||||
group.
|
||||
|
||||
All operations are implemented using constant-time logic (no
|
||||
secret-dependent branches, no secret-dependent memory accesses),
|
||||
unless specifically marked as being variable-time code.
|
||||
We believe that our constant-time logic is lowered to constant-time
|
||||
assembly, at least on `x86_64` targets.
|
||||
|
||||
As an additional guard against possible future compiler optimizations,
|
||||
the `subtle` crate places an optimization barrier before every
|
||||
conditional move or assignment. More details can be found in [the
|
||||
documentation for the `subtle` crate][subtle_doc].
|
||||
|
||||
Some functionality (e.g., multiscalar multiplication or batch
|
||||
inversion) requires heap allocation for temporary buffers. All
|
||||
heap-allocated buffers of potentially secret data are explicitly
|
||||
zeroed before release.
|
||||
|
||||
However, we do not attempt to zero stack data, for two reasons.
|
||||
First, it's not possible to do so correctly: we don't have control
|
||||
over stack allocations, so there's no way to know how much data to
|
||||
wipe. Second, because `curve25519-dalek` provides a mid-level API,
|
||||
the correct place to start zeroing stack data is likely not at the
|
||||
entrypoints of `curve25519-dalek` functions, but at the entrypoints of
|
||||
functions in other crates.
|
||||
|
||||
The implementation is memory-safe, and contains no significant
|
||||
`unsafe` code. The SIMD backend uses `unsafe` internally to call SIMD
|
||||
intrinsics. These are marked `unsafe` only because invoking them on an
|
||||
inappropriate CPU would cause `SIGILL`, but the entire backend is only
|
||||
compiled with appropriate `target_feature`s, so this cannot occur.
|
||||
|
||||
# Performance
|
||||
|
||||
Benchmarks are run using [`criterion.rs`][criterion]:
|
||||
|
||||
```sh
|
||||
cargo bench --no-default-features --features "std u32_backend"
|
||||
cargo bench --no-default-features --features "std u64_backend"
|
||||
# Uses avx2 or ifma only if compiled for an appropriate target.
|
||||
export RUSTFLAGS="-C target_cpu=native"
|
||||
cargo bench --no-default-features --features "std simd_backend"
|
||||
```
|
||||
|
||||
Performance is a secondary goal behind correctness, safety, and
|
||||
clarity, but we aim to be competitive with other implementations.
|
||||
|
||||
# FFI
|
||||
|
||||
Unfortunately, we have no plans to add FFI to `curve25519-dalek` directly. The
|
||||
reason is that we use Rust features to provide an API that maintains safety
|
||||
invariants, which are not possible to maintain across an FFI boundary. For
|
||||
instance, as described in the _Safety_ section above, invalid points are
|
||||
impossible to construct, and this would not be the case if we exposed point
|
||||
operations over FFI.
|
||||
|
||||
However, `curve25519-dalek` is designed as a *mid-level* API, aimed at
|
||||
implementing other, higher-level primitives. Instead of providing FFI at the
|
||||
mid-level, our suggestion is to implement the higher-level primitive (a
|
||||
signature, PAKE, ZKP, etc) in Rust, using `curve25519-dalek` as a dependency,
|
||||
and have that crate provide a minimal, byte-buffer-oriented FFI specific to
|
||||
that primitive.
|
||||
|
||||
# Contributing
|
||||
|
||||
Please see [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
Please see [CONTRIBUTING.md][contributing].
|
||||
|
||||
# Code of Conduct
|
||||
Patches and pull requests should be make against the `develop`
|
||||
branch, **not** `main`.
|
||||
|
||||
We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html),
|
||||
with the following additional clauses:
|
||||
# About
|
||||
|
||||
* 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.
|
||||
**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/
|
||||
|
||||
@ -1,14 +1,22 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use rand::{rngs::OsRng, thread_rng};
|
||||
extern crate rand;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::thread_rng;
|
||||
|
||||
use criterion::{
|
||||
criterion_main, measurement::Measurement, BatchSize, BenchmarkGroup, BenchmarkId, Criterion,
|
||||
};
|
||||
#[macro_use]
|
||||
extern crate criterion;
|
||||
|
||||
use criterion::measurement::Measurement;
|
||||
use criterion::BatchSize;
|
||||
use criterion::Criterion;
|
||||
use criterion::{BenchmarkGroup, BenchmarkId};
|
||||
|
||||
extern crate curve25519_dalek;
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
use curve25519_dalek::field::FieldElement;
|
||||
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];
|
||||
@ -18,26 +26,27 @@ mod edwards_benches {
|
||||
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
|
||||
fn compress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn compress(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
c.bench_function("EdwardsPoint compression", move |b| b.iter(|| B.compress()));
|
||||
}
|
||||
|
||||
fn decompress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn decompress(c: &mut Criterion) {
|
||||
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<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn consttime_fixed_base_scalar_mul(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_TABLE;
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
c.bench_function("Constant-time fixed-base scalar mul", move |b| {
|
||||
b.iter(|| EdwardsPoint::mul_base(&s))
|
||||
b.iter(|| B * &s)
|
||||
});
|
||||
}
|
||||
|
||||
fn consttime_variable_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn consttime_variable_base_scalar_mul(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
c.bench_function("Constant-time variable-base scalar mul", move |b| {
|
||||
@ -45,10 +54,10 @@ mod edwards_benches {
|
||||
});
|
||||
}
|
||||
|
||||
fn vartime_double_base_scalar_mul<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
||||
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
||||
let mut rng = thread_rng();
|
||||
let A = EdwardsPoint::mul_base(&Scalar::random(&mut rng));
|
||||
let A = &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE;
|
||||
bench.iter_batched(
|
||||
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|
||||
|(a, b)| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b),
|
||||
@ -57,15 +66,15 @@ mod edwards_benches {
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,10 +95,14 @@ mod multiscalar_benches {
|
||||
fn construct_points(n: usize) -> Vec<EdwardsPoint> {
|
||||
let mut rng = thread_rng();
|
||||
(0..n)
|
||||
.map(|_| EdwardsPoint::mul_base(&Scalar::random(&mut rng)))
|
||||
.map(|_| &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE)
|
||||
.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(
|
||||
@ -141,7 +154,7 @@ 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| {
|
||||
@ -170,59 +183,66 @@ 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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn multiscalar_benches() {
|
||||
let mut c = Criterion::default();
|
||||
let mut g = c.benchmark_group("multiscalar benches");
|
||||
fn multiscalar_multiplications(c: &mut Criterion) {
|
||||
let mut group: BenchmarkGroup<_> = c.benchmark_group("Multiscalar multiplications");
|
||||
|
||||
consttime_multiscalar_mul(&mut g);
|
||||
vartime_multiscalar_mul(&mut g);
|
||||
vartime_precomputed_pure_static(&mut g);
|
||||
consttime_multiscalar_mul(&mut group);
|
||||
vartime_multiscalar_mul(&mut group);
|
||||
vartime_precomputed_pure_static(&mut group);
|
||||
|
||||
let dynamic_fracs = [0.0, 0.2, 0.5];
|
||||
|
||||
for frac in dynamic_fracs.iter() {
|
||||
vartime_precomputed_helper(&mut g, *frac);
|
||||
vartime_precomputed_helper(&mut group, *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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,21 +250,21 @@ mod ristretto_benches {
|
||||
use super::*;
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
|
||||
fn compress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn compress(c: &mut Criterion) {
|
||||
c.bench_function("RistrettoPoint compression", |b| {
|
||||
let B = &constants::RISTRETTO_BASEPOINT_POINT;
|
||||
b.iter(|| B.compress())
|
||||
});
|
||||
}
|
||||
|
||||
fn decompress<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn decompress(c: &mut Criterion) {
|
||||
c.bench_function("RistrettoPoint decompression", |b| {
|
||||
let B_comp = &constants::RISTRETTO_BASEPOINT_COMPRESSED;
|
||||
b.iter(|| B_comp.decompress().unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn elligator<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn elligator(c: &mut Criterion) {
|
||||
let fe_bytes = [0u8; 32];
|
||||
let fe = FieldElement::from_bytes(&fe_bytes);
|
||||
|
||||
@ -269,22 +289,26 @@ mod ristretto_benches {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ristretto_benches() {
|
||||
let mut c = Criterion::default();
|
||||
let mut g = c.benchmark_group("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();
|
||||
}
|
||||
|
||||
compress(&mut g);
|
||||
decompress(&mut g);
|
||||
elligator(&mut g);
|
||||
double_and_compress_batch(&mut g);
|
||||
criterion_group! {
|
||||
name = ristretto_benches;
|
||||
config = Criterion::default();
|
||||
targets =
|
||||
compress,
|
||||
decompress,
|
||||
double_and_compress_group,
|
||||
}
|
||||
}
|
||||
|
||||
mod montgomery_benches {
|
||||
use super::*;
|
||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||
|
||||
fn montgomery_ladder<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
fn montgomery_ladder(c: &mut Criterion) {
|
||||
c.bench_function("Montgomery pseudomultiplication", |b| {
|
||||
let B = constants::X25519_BASEPOINT;
|
||||
let s = Scalar::from(897987897u64).invert();
|
||||
@ -292,53 +316,21 @@ mod montgomery_benches {
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
criterion_group! {
|
||||
name = montgomery_benches;
|
||||
config = Criterion::default();
|
||||
targets = montgomery_ladder,
|
||||
}
|
||||
}
|
||||
|
||||
mod scalar_benches {
|
||||
use super::*;
|
||||
|
||||
fn scalar_arith<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
fn scalar_inversion(c: &mut Criterion) {
|
||||
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>) {
|
||||
@ -359,12 +351,18 @@ mod scalar_benches {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn scalar_benches() {
|
||||
let mut c = Criterion::default();
|
||||
let mut g = c.benchmark_group("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();
|
||||
}
|
||||
|
||||
scalar_arith(&mut g);
|
||||
batch_scalar_inversion(&mut g);
|
||||
criterion_group! {
|
||||
name = scalar_benches;
|
||||
config = Criterion::default();
|
||||
targets =
|
||||
scalar_inversion,
|
||||
batch_scalar_inversion_group,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
[package]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.0"
|
||||
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/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"] }
|
||||
@ -1,191 +0,0 @@
|
||||
# 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;
|
||||
impl Backend for AVX {
|
||||
#[target_feature(enable = "avx")]
|
||||
unsafe fn sum(xs: &[u32]) -> u32 {
|
||||
// ...
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
struct AVX2;
|
||||
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`
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn func() {}
|
||||
```
|
||||
|
||||
```rust
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
// No `unsafe` on the function itself!
|
||||
#[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;
|
||||
|
||||
#[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;
|
||||
|
||||
#[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() { /* ... */ }
|
||||
}
|
||||
|
||||
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) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/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.
|
||||
@ -1,466 +0,0 @@
|
||||
#![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()
|
||||
}
|
||||
}
|
||||
@ -1,152 +0,0 @@
|
||||
#![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")]
|
||||
#[cfg(feature = "dummy")]
|
||||
fn function_with_cfg() {}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
#[cfg(feature = "dummy")]
|
||||
fn member_function_with_cfg() {}
|
||||
}
|
||||
|
||||
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", conditional("avx512ifma", disabled))]
|
||||
mod inner_spec {
|
||||
use std;
|
||||
|
||||
#[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);
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
[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.0.0"
|
||||
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"
|
||||
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"]
|
||||
|
||||
[dev-dependencies]
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
bincode = "1"
|
||||
criterion = { version = "0.4.0", features = ["html_reports"] }
|
||||
hex = "0.4.2"
|
||||
rand = "0.8"
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
|
||||
[build-dependencies]
|
||||
platforms = "3.0.2"
|
||||
rustc_version = "0.4.0"
|
||||
|
||||
[[bench]]
|
||||
name = "dalek_benchmarks"
|
||||
harness = false
|
||||
required-features = ["alloc", "rand_core"]
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
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 = "0.1.19"
|
||||
|
||||
[features]
|
||||
default = ["alloc", "precomputed-tables", "zeroize", "lizard"]
|
||||
alloc = ["zeroize?/alloc"]
|
||||
precomputed-tables = []
|
||||
legacy_compatibility = []
|
||||
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" }
|
||||
@ -1,11 +0,0 @@
|
||||
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
|
||||
@ -1,316 +0,0 @@
|
||||
|
||||
# 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"
|
||||
```
|
||||
|
||||
## 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. |
|
||||
|
||||
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 `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/ed25519-dalek
|
||||
[x25519-dalek]: https://github.com/dalek-cryptography/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,127 +0,0 @@
|
||||
//! 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,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
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(),
|
||||
};
|
||||
|
||||
match curve25519_dalek_bits {
|
||||
DalekBits::Dalek64 => println!("cargo:rustc-cfg=curve25519_dalek_bits=\"64\""),
|
||||
DalekBits::Dalek32 => println!("cargo:rustc-cfg=curve25519_dalek_bits=\"32\""),
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") {
|
||||
Ok(arch) => arch,
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
// 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::*;
|
||||
|
||||
// Standard Cargo TARGET environment variable of triplet is required
|
||||
static ERR_MSG_NO_TARGET: &str = "Standard Cargo TARGET environment variable is not set";
|
||||
|
||||
// Custom Non-Rust standard target platforms require explicit settings.
|
||||
static ERR_MSG_NO_PLATFORM: &str = "Unknown Rust target platform.";
|
||||
|
||||
// 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() -> DalekBits {
|
||||
use platforms::target::PointerWidth;
|
||||
|
||||
// TARGET environment is supplied by Cargo
|
||||
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
|
||||
let target_triplet = match std::env::var("TARGET") {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
determine_curve25519_dalek_bits_warning(ERR_MSG_NO_TARGET);
|
||||
return DalekBits::Dalek32;
|
||||
}
|
||||
};
|
||||
|
||||
// platforms crate is the source of truth used to determine the platform
|
||||
let platform = match platforms::Platform::find(&target_triplet) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
determine_curve25519_dalek_bits_warning(ERR_MSG_NO_PLATFORM);
|
||||
return DalekBits::Dalek32;
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(clippy::match_single_binding)]
|
||||
match platform.target_arch {
|
||||
//Issues: 449 and 456
|
||||
//TODO(Arm): Needs tests + benchmarks to back this up
|
||||
//platforms::target::Arch::Arm => DalekBits::Dalek64,
|
||||
//TODO(Wasm32): Needs tests + benchmarks to back this up
|
||||
//platforms::target::Arch::Wasm32 => DalekBits::Dalek64,
|
||||
_ => match platform.target_pointer_width {
|
||||
PointerWidth::U64 => DalekBits::Dalek64,
|
||||
PointerWidth::U32 => DalekBits::Dalek32,
|
||||
// Intended default solely for non-32/64 target pointer widths
|
||||
// Otherwise known target platforms only.
|
||||
_ => DalekBits::Dalek32,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
<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>
|
||||
@ -1,256 +0,0 @@
|
||||
// -*- 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 =>
|
||||
self::vector::scalar_mul::pippenger::spec_avx2::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 =>
|
||||
self::vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
||||
BackendKind::Serial =>
|
||||
self::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(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
Avx512ifma(
|
||||
self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
|
||||
),
|
||||
Scalar(self::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(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus::new(static_points)),
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 =>
|
||||
VartimePrecomputedStraus::Avx512ifma(self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
|
||||
BackendKind::Serial =>
|
||||
VartimePrecomputedStraus::Scalar(self::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 => {
|
||||
self::vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(
|
||||
scalars, points,
|
||||
)
|
||||
}
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<
|
||||
I,
|
||||
J,
|
||||
>(scalars, points)
|
||||
}
|
||||
BackendKind::Serial => {
|
||||
self::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 => {
|
||||
self::vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
|
||||
scalars, points,
|
||||
)
|
||||
}
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
|
||||
I,
|
||||
J,
|
||||
>(scalars, points)
|
||||
}
|
||||
BackendKind::Serial => {
|
||||
self::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 => self::vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
|
||||
}
|
||||
BackendKind::Serial => self::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 => self::vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
|
||||
}
|
||||
BackendKind::Serial => self::serial::scalar_mul::vartime_double_base::mul(a, A, b),
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
// -*- 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")]
|
||||
pub mod fiat_u32;
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
pub mod fiat_u64;
|
||||
|
||||
} else {
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
pub mod u32;
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
pub mod u64;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub mod curve_models;
|
||||
|
||||
pub mod scalar_mul;
|
||||
@ -1,72 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of curve25519-dalek.
|
||||
// Copyright (c) 2016-2021 isis lovecruft
|
||||
// Copyright (c) 2016-2019 Henry de Valence
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::backend::serial::curve_models::{ProjectiveNielsPoint, ProjectivePoint};
|
||||
use crate::constants;
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::Identity;
|
||||
use crate::window::NafLookupTable5;
|
||||
|
||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||
pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||
let a_naf = a.non_adjacent_form(5);
|
||||
|
||||
#[cfg(feature = "precomputed-tables")]
|
||||
let b_naf = b.non_adjacent_form(8);
|
||||
#[cfg(not(feature = "precomputed-tables"))]
|
||||
let b_naf = b.non_adjacent_form(5);
|
||||
|
||||
// Find starting index
|
||||
let mut i: usize = 255;
|
||||
for j in (0..256).rev() {
|
||||
i = j;
|
||||
if a_naf[i] != 0 || b_naf[i] != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let table_A = NafLookupTable5::<ProjectiveNielsPoint>::from(A);
|
||||
#[cfg(feature = "precomputed-tables")]
|
||||
let table_B = &constants::AFFINE_ODD_MULTIPLES_OF_BASEPOINT;
|
||||
#[cfg(not(feature = "precomputed-tables"))]
|
||||
let table_B =
|
||||
&NafLookupTable5::<ProjectiveNielsPoint>::from(&constants::ED25519_BASEPOINT_POINT);
|
||||
|
||||
let mut r = ProjectivePoint::identity();
|
||||
loop {
|
||||
let mut t = r.double();
|
||||
|
||||
match a_naf[i].cmp(&0) {
|
||||
Ordering::Greater => t = &t.as_extended() + &table_A.select(a_naf[i] as usize),
|
||||
Ordering::Less => t = &t.as_extended() - &table_A.select(-a_naf[i] as usize),
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
|
||||
match b_naf[i].cmp(&0) {
|
||||
Ordering::Greater => t = &t.as_extended() + &table_B.select(b_naf[i] as usize),
|
||||
Ordering::Less => t = &t.as_extended() - &table_B.select(-b_naf[i] as usize),
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
|
||||
r = t.as_projective();
|
||||
|
||||
if i == 0 {
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
r.as_extended()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,337 +0,0 @@
|
||||
// -*- 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() }
|
||||
}
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
// -*- 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()`.
|
||||
// `unwrap()` always succeeds because we know we have more than zero digits.
|
||||
let hi_column = columns.next().unwrap();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
// -*- 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
// -*- 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
#![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()
|
||||
}
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
// -*- 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()
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
//! 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'");
|
||||
@ -1,99 +0,0 @@
|
||||
// -*- 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), feature(stdsimd))]
|
||||
#![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)))]
|
||||
#![cfg_attr(allow_unused_unsafe, allow(unused_unsafe))]
|
||||
//------------------------------------------------------------------------
|
||||
// Documentation:
|
||||
//------------------------------------------------------------------------
|
||||
#![deny(missing_docs)]
|
||||
#![doc(
|
||||
html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png"
|
||||
)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// 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;
|
||||
@ -1,38 +0,0 @@
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
pub use crate::backend::serial::fiat_u32::field::FieldElement2625;
|
||||
} else {
|
||||
pub use crate::backend::serial::u32::field::FieldElement2625;
|
||||
}
|
||||
}
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement2625 = FieldElement2625([
|
||||
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 = FieldElement2625([
|
||||
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 = FieldElement2625([
|
||||
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 = FieldElement2625([
|
||||
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 = FieldElement2625([
|
||||
38019585, 4791795, 20332186, 18653482, 46576675, 33182583, 65658549, 2817057, 12569934,
|
||||
30919145,
|
||||
]);
|
||||
@ -1,55 +0,0 @@
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
pub use crate::backend::serial::fiat_u64::field::FieldElement51;
|
||||
} else {
|
||||
pub use crate::backend::serial::u64::field::FieldElement51;
|
||||
}
|
||||
}
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement51 = FieldElement51([
|
||||
2298852427963285,
|
||||
3837146560810661,
|
||||
4413131899466403,
|
||||
3883177008057528,
|
||||
2352084440532925,
|
||||
]);
|
||||
|
||||
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
|
||||
pub const DP1_OVER_DM1: FieldElement51 = FieldElement51([
|
||||
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 = FieldElement51([
|
||||
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 = FieldElement51([
|
||||
1608655899704280,
|
||||
1999971613377227,
|
||||
49908634785720,
|
||||
1873700692181652,
|
||||
353702208628067,
|
||||
]);
|
||||
|
||||
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = FieldElement51([
|
||||
321571956990465,
|
||||
1251814006996634,
|
||||
2226845496292387,
|
||||
189049560751797,
|
||||
2074948709371214,
|
||||
]);
|
||||
@ -1,276 +0,0 @@
|
||||
// -*- 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)
|
||||
}
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
#!/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"
|
||||
10
docs/assets/rustdoc-include-katex-header.html
Normal file
10
docs/assets/rustdoc-include-katex-header.html
Normal file
@ -0,0 +1,10 @@
|
||||
<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>
|
||||
@ -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://docs.rs/curve25519-dalek/latest/curve25519-dalek/backend/serial/curve_models/index.html
|
||||
[curve_models]: https://doc-internal.dalek.rs/curve25519_dalek/backend/serial/curve_models/index.html
|
||||
[bbjlp08]: https://eprint.iacr.org/2008/013
|
||||
[cmo98]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf
|
||||
[intel]: https://software.intel.com/sites/default/files/managed/9e/bc/64-ia-32-architectures-optimization-manual.pdf
|
||||
12
ed25519-dalek/.gitignore
vendored
12
ed25519-dalek/.gitignore
vendored
@ -1,12 +0,0 @@
|
||||
target
|
||||
|
||||
.cargo
|
||||
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
*.swp
|
||||
*.orig
|
||||
*.bak
|
||||
|
||||
*.s
|
||||
@ -1,33 +0,0 @@
|
||||
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
|
||||
@ -1,41 +0,0 @@
|
||||
# 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.
|
||||
|
||||
# 2.x series
|
||||
|
||||
## 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
|
||||
@ -1,75 +0,0 @@
|
||||
[package]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.0.0-rc.3"
|
||||
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/ed25519-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.1", optional = true, default-features = false }
|
||||
sha2 = { version = "0.10", 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"] }
|
||||
blake2 = "0.10"
|
||||
sha3 = "0.10"
|
||||
hex = "0.4"
|
||||
bincode = "1.0"
|
||||
serde_json = "1.0"
|
||||
criterion = { version = "0.4", features = ["html_reports"] }
|
||||
hex-literal = "0.3"
|
||||
rand = "0.8"
|
||||
rand_core = { version = "0.6.4", default-features = false }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = { version = "0.5" }
|
||||
|
||||
[[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"]
|
||||
@ -1,28 +0,0 @@
|
||||
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.
|
||||
@ -1,188 +0,0 @@
|
||||
# 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
|
||||
|
||||
## Stable
|
||||
|
||||
To import `ed25519-dalek`, add the following to the dependencies section of
|
||||
your project's `Cargo.toml`:
|
||||
```toml
|
||||
ed25519-dalek = "1"
|
||||
```
|
||||
|
||||
## Beta
|
||||
|
||||
To use the latest prerelease (see changes [below](#breaking-changes-in-200)),
|
||||
use the following line in your project's `Cargo.toml`:
|
||||
```toml
|
||||
ed25519-dalek = "2.0.0-rc.3"
|
||||
```
|
||||
|
||||
# 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 version 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 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 |
|
||||
| :--- | :--- | :--- |
|
||||
| 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
|
||||
@ -1,128 +0,0 @@
|
||||
9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a:d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a::e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b:
|
||||
4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:72:92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c0072:
|
||||
c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:af82:6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40aaf82:
|
||||
0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:cbc77b:d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00ccbc77b:
|
||||
6df9340c138cc188b5fe4464ebaa3f7fc206a2d55c3434707e74c9fc04e20ebbc0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:5f4c8989:124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c075f4c8989:
|
||||
b780381a65edf8b78f6945e8dbec7941ac049fd4c61040cf0c324357975a293ce253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:e253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:18b6bec097:b2fc46ad47af464478c199e1f8be169f1be6327c7f9a0a6689371ca94caf04064a01b22aff1520abd58951341603faed768cf78ce97ae7b038abfe456aa17c0918b6bec097:
|
||||
78ae9effe6f245e924a7be63041146ebc670dbd3060cba67fbc6216febc44546fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:89010d855972:6ed629fc1d9ce9e1468755ff636d5a3f40a5d9c91afd93b79d241830f7e5fa29854b8f20cc6eecbb248dbd8d16d14e99752194e4904d09c74d639518839d230089010d855972:
|
||||
691865bfc82a1e4b574eecde4c7519093faf0cf867380234e3664645c61c5f7998a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:98a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:b4a8f381e70e7a:6e0af2fe55ae377a6b7a7278edfb419bd321e06d0df5e27037db8812e7e3529810fa5552f6c0020985ca17a0e02e036d7b222a24f99b77b75fdd16cb05568107b4a8f381e70e7a:
|
||||
3b26516fb3dc88eb181b9ed73f0bcd52bcd6b4c788e4bcaf46057fd078bee073f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:4284abc51bb67235:d6addec5afb0528ac17bb178d3e7f2887f9adbb1ad16e110545ef3bc57f9de2314a5c8388f723b8907be0f3ac90c6259bbe885ecc17645df3db7d488f805fa084284abc51bb67235:
|
||||
edc6f5fbdd1cee4d101c063530a30490b221be68c036f5b07d0f953b745df192c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:672bf8965d04bc5146:2c76a04af2391c147082e33faacdbe56642a1e134bd388620b852b901a6bc16ff6c9cc9404c41dea12ed281da067a1513866f9d964f8bdd24953856c50042901672bf8965d04bc5146:
|
||||
4e7d21fb3b1897571a445833be0f9fd41cd62be3aa04040f8934e1fcbdcacd4531b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:31b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:33d7a786aded8c1bf691:28e4598c415ae9de01f03f9f3fab4e919e8bf537dd2b0cdf6e79b9e6559c9409d9151a4c40f083193937627c369488259e99da5a9f0a87497fa6696a5dd6ce0833d7a786aded8c1bf691:
|
||||
a980f892db13c99a3e8971e965b2ff3d41eafd54093bc9f34d1fd22d84115bb644b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:44b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:3486f68848a65a0eb5507d:77d389e599630d934076329583cd4105a649a9292abc44cd28c40000c8e2f5ac7660a81c85b72af8452d7d25c070861dae91601c7803d656531650dd4e5c41003486f68848a65a0eb5507d:
|
||||
5b5a619f8ce1c66d7ce26e5a2ae7b0c04febcd346d286c929e19d0d5973bfef96fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:6fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:5a8d9d0a22357e6655f9c785:0f9ad9793033a2fa06614b277d37381e6d94f65ac2a5a94558d09ed6ce922258c1a567952e863ac94297aec3c0d0c8ddf71084e504860bb6ba27449b55adc40e5a8d9d0a22357e6655f9c785:
|
||||
940c89fe40a81dafbdb2416d14ae469119869744410c3303bfaa0241dac57800a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:b87d3813e03f58cf19fd0b6395:d8bb64aad8c9955a115a793addd24f7f2b077648714f49c4694ec995b330d09d640df310f447fd7b6cb5c14f9fe9f490bcf8cfadbfd2169c8ac20d3b8af49a0cb87d3813e03f58cf19fd0b6395:
|
||||
9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:55c7fa434f5ed8cdec2b7aeac173:6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf0655c7fa434f5ed8cdec2b7aeac173:
|
||||
d5aeee41eeb0e9d1bf8337f939587ebe296161e6bf5209f591ec939e1440c300fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:0a688e79be24f866286d4646b5d81c:f68d04847e5b249737899c014d31c805c5007a62c0a10d50bb1538c5f35503951fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b000a688e79be24f866286d4646b5d81c:
|
||||
0a47d10452ae2febec518a1c7c362890c3fc1a49d34b03b6467d35c904a8362d34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:c942fa7ac6b23ab7ff612fdc8e68ef39:2a3d27dc40d0a8127949a3b7f908b3688f63b7f14f651aacd715940bdbe27a0809aac142f47ab0e1e44fa490ba87ce5392f33a891539caf1ef4c367cae54500cc942fa7ac6b23ab7ff612fdc8e68ef39:
|
||||
f8148f7506b775ef46fdc8e8c756516812d47d6cfbfa318c27c9a22641e56f170445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:0445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:7368724a5b0efb57d28d97622dbde725af:3653ccb21219202b8436fb41a32ba2618c4a133431e6e63463ceb3b6106c4d56e1d2ba165ba76eaad3dc39bffb130f1de3d8e6427db5b71938db4e272bc3e20b7368724a5b0efb57d28d97622dbde725af:
|
||||
77f88691c4eff23ebb7364947092951a5ff3f10785b417e918823a552dab7c7574d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:74d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:bd8e05033f3a8bcdcbf4beceb70901c82e31:fbe929d743a03c17910575492f3092ee2a2bf14a60a3fcacec74a58c7334510fc262db582791322d6c8c41f1700adb80027ecabc14270b703444ae3ee7623e0abd8e05033f3a8bcdcbf4beceb70901c82e31:
|
||||
ab6f7aee6a0837b334ba5eb1b2ad7fcecfab7e323cab187fe2e0a95d80eff1325b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:5b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:8171456f8b907189b1d779e26bc5afbb08c67a:73bca64e9dd0db88138eedfafcea8f5436cfb74bfb0e7733cf349baa0c49775c56d5934e1d38e36f39b7c5beb0a836510c45126f8ec4b6810519905b0ca07c098171456f8b907189b1d779e26bc5afbb08c67a:
|
||||
8d135de7c8411bbdbd1b31e5dc678f2ac7109e792b60f38cd24936e8a898c32d1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:8ba6a4c9a15a244a9c26bb2a59b1026f21348b49:a1adc2bc6a2d980662677e7fdff6424de7dba50f5795ca90fdf3e96e256f3285cac71d3360482e993d0294ba4ec7440c61affdf35fe83e6e04263937db93f1058ba6a4c9a15a244a9c26bb2a59b1026f21348b49:
|
||||
0e765d720e705f9366c1ab8c3fa84c9a44370c06969f803296884b2846a652a47fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:7fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:1d566a6232bbaab3e6d8804bb518a498ed0f904986:bb61cf84de61862207c6a455258bc4db4e15eea0317ff88718b882a06b5cf6ec6fd20c5a269e5d5c805bafbcc579e2590af414c7c227273c102a10070cdfe80f1d566a6232bbaab3e6d8804bb518a498ed0f904986:
|
||||
db36e326d676c2d19cc8fe0c14b709202ecfc761d27089eb6ea4b1bb021ecfa748359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:48359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:1b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:b6dcd09989dfbac54322a3ce87876e1d62134da998c79d24b50bd7a6a797d86a0e14dc9d7491d6c14a673c652cfbec9f962a38c945da3b2f0879d0b68a9213001b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:
|
||||
c89955e0f7741d905df0730b3dc2b0ce1a13134e44fef3d40d60c020ef19df77fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:7ef66e5e86f2360848e0014e94880ae2920ad8a3185a46b35d1e07dea8fa8ae4f6b843ba174d99fa7986654a0891c12a794455669375bf92af4cc2770b579e0c507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:
|
||||
4e62627fc221142478aee7f00781f817f662e3b75db29bb14ab47cf8e84104d6b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:d3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:836afa764d9c48aa4770a4388b654e97b3c16f082967febca27f2fc47ddfd9244b03cfc729698acf5109704346b60b230f255430089ddc56912399d1122de70ad3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:
|
||||
6b83d7da8908c3e7205b39864b56e5f3e17196a3fc9c2f5805aad0f5554c142dd0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:d0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:6ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:16e462a29a6dd498685a3718b3eed00cc1598601ee47820486032d6b9acc9bf89f57684e08d8c0f05589cda2882a05dc4c63f9d0431d6552710812433003bc086ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:
|
||||
19a91fe23a4e9e33ecc474878f57c64cf154b394203487a7035e1ad9cd697b0d2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:881f5b8c5a030df0f75b6634b070dd27bd1ee3c08738ae349338b3ee6469bbf9760b13578a237d5182535ede121283027a90b5f865d63a6537dca07b44049a0f82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:
|
||||
1d5b8cb6215c18141666baeefcf5d69dad5bea9a3493dddaa357a4397a13d4de94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:a9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:3acd39bec8c3cd2b44299722b5850a0400c1443590fd4861d59aae7496acb3df73fc3fdf7969ae5f50ba47dddc435246e5fd376f6b891cd4c2caf5d614b6170ca9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:
|
||||
6a91b3227c472299089bdce9356e726a40efd840f11002708b7ee55b64105ac29d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:9d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:5cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:f5875423781b66216cb5e8998de5d9ffc29d1d67107054ace3374503a9c3ef811577f269de81296744bd706f1ac478caf09b54cdf871b3f802bd57f9a6cb91015cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:
|
||||
93eaa854d791f05372ce72b94fc6503b2ff8ae6819e6a21afe825e27ada9e4fb16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:32fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:d834197c1a3080614e0a5fa0aaaa808824f21c38d692e6ffbd200f7dfb3c8f44402a7382180b98ad0afc8eec1a02acecf3cb7fde627b9f18111f260ab1db9a0732fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:
|
||||
941cac69fb7b1815c57bb987c4d6c2ad2c35d5f9a3182a79d4ba13eab253a8ad23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:0f8fad1e6bde771b4f5420eac75c378bae6db5ac6650cd2bc210c1823b432b48e016b10595458ffab92f7a8989b293ceb8dfed6c243a2038fc06652aaaf16f02bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:
|
||||
1acdbb793b0384934627470d795c3d1dd4d79cea59ef983f295b9b59179cbb283f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:3f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:7cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:be71ef4806cb041d885effd9e6b0fbb73d65d7cdec47a89c8a994892f4e55a568c4cc78d61f901e80dbb628b86a23ccd594e712b57fa94c2d67ec266348785077cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:
|
||||
8ed7a797b9cea8a8370d419136bcdf683b759d2e3c6947f17e13e2485aa9d420b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:a750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:04266c033b91c1322ceb3446c901ffcf3cc40c4034e887c9597ca1893ba7330becbbd8b48142ef35c012c6ba51a66df9308cb6268ad6b1e4b03e70102495790ba750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:
|
||||
f2ab396fe8906e3e5633e99cabcd5b09df0859b516230b1e0450b580b65f616c8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:a06a23d982d81ab883aae230adbc368a6a9977f003cebb00d4c2e4018490191a84d3a282fdbfb2fc88046e62de43e15fb575336b3c8b77d19ce6a009ce51f50c5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:
|
||||
550a41c013f79bab8f06e43ad1836d51312736a9713806fafe6645219eaa1f9daf6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:af6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:8bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:16dc1e2b9fa909eefdc277ba16ebe207b8da5e91143cde78c5047a89f681c33c4e4e3428d5c928095903a811ec002d52a39ed7f8b3fe1927200c6dd0b9ab3e048bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:
|
||||
19ac3e272438c72ddf7b881964867cb3b31ff4c793bb7ea154613c1db068cb7ef85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:f85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:95872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:ea855d781cbea4682e350173cb89e8619ccfddb97cdce16f9a2f6f6892f46dbe68e04b12b8d88689a7a31670cdff409af98a93b49a34537b6aa009d2eb8b470195872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:
|
||||
ca267de96c93c238fafb1279812059ab93ac03059657fd994f8fa5a09239c821017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:e05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:ac957f82335aa7141e96b59d63e3ccee95c3a2c47d026540c2af42dc9533d5fd81827d1679ad187aeaf37834915e75b147a9286806c8017516ba43dd051a5e0ce05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:
|
||||
3dff5e899475e7e91dd261322fab09980c52970de1da6e2e201660cc4fce7032f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:5efe7a92ff9623089b3e3b78f352115366e26ba3fb1a416209bc029e9cadccd9f4affa333555a8f3a35a9d0f7c34b292cae77ec96fa3adfcaadee2d9ced8f805938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:
|
||||
9a6b847864e70cfe8ba6ab22fa0ca308c0cc8bec7141fbcaa3b81f5d1e1cfcfc34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:2ab255169c489c54c732232e37c87349d486b1eba20509dbabe7fed329ef08fd75ba1cd145e67b2ea26cb5cc51cab343eeb085fe1fd7b0ec4c6afcd9b979f905838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:
|
||||
575be07afca5d063c238cd9b8028772cc49cda34471432a2e166e096e2219efc94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:33e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:58271d44236f3b98c58fd7ae0d2f49ef2b6e3affdb225aa3ba555f0e11cc53c23ad19baf24346590d05d7d5390582082cf94d39cad6530ab93d13efb3927950633e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:
|
||||
15ffb45514d43444d61fcb105e30e135fd268523dda20b82758b1794231104411772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:1772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:6828cd7624e793b8a4ceb96d3c2a975bf773e5ff6645f353614058621e58835289e7f31f42dfe6af6d736f2644511e320c0fa698582a79778d18730ed3e8cb08da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:
|
||||
fe0568642943b2e1afbfd1f10fe8df87a4236bea40dce742072cb21886eec1fa299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:d59e6dfcc6d7e3e2c58dec81e985d245e681acf6594a23c59214f7bed8015d813c7682b60b3583440311e72a8665ba2c96dec23ce826e160127e18132b030404c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:
|
||||
5ecb16c2df27c8cf58e436a9d3affbd58e9538a92659a0f97c4c4f994635a8cada768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:da768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:1c723a20c6772426a670e4d5c4a97c6ebe9147f71bb0a415631e44406e290322e4ca977d348fe7856a8edc235d0fe95f7ed91aefddf28a77e2c7dbfd8f552f0a56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:
|
||||
d599d637b3c30a82a9984e2f758497d144de6f06b9fba04dd40fd949039d7c846791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:6791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:ebf10d9ac7c96108140e7def6fe9533d727646ff5b3af273c1df95762a66f32b65a09634d013f54b5dd6011f91bc336ca8b355ce33f8cfbec2535a4c427f8205a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:
|
||||
30ab8232fa7018f0ce6c39bd8f782fe2e159758bb0f2f4386c7f28cfd2c85898ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:9af885344cc7239498f712df80bc01b80638291ed4a1d28baa5545017a72e2f65649ccf9603da6eb5bfab9f5543a6ca4a7af3866153c76bf66bf95def615b00c63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:
|
||||
0ddcdc872c7b748d40efe96c2881ae189d87f56148ed8af3ebbbc80324e38bdd588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:65641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:c179c09456e235fe24105afa6e8ec04637f8f943817cd098ba95387f9653b2add181a31447d92d1a1ddf1ceb0db62118de9dffb7dcd2424057cbdff5d41d040365641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:
|
||||
89f0d68299ba0a5a83f248ae0c169f8e3849a9b47bd4549884305c9912b46603aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:2c691fa8d487ce20d5d2fa41559116e0bbf4397cf5240e152556183541d66cf753582401a4388d390339dbef4d384743caa346f55f8daba68ba7b9131a8a6e0b4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:
|
||||
0a3c1844e2db070fb24e3c95cb1cc6714ef84e2ccd2b9dd2f1460ebf7ecf13b172e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:72e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:4c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:87f7fdf46095201e877a588fe3e5aaf476bd63138d8a878b89d6ac60631b3458b9d41a3c61a588e1db8d29a5968981b018776c588780922f5aa732ba6379dd054c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:
|
||||
c8d7a8818b98dfdb20839c871cb5c48e9e9470ca3ad35ba2613a5d3199c8ab2390d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:90d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:fa2e994421aef1d5856674813d05cbd2cf84ef5eb424af6ecd0dc6fdbdc2fe605fe985883312ecf34f59bfb2f1c9149e5b9cc9ecda05b2731130f3ed28ddae0b783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:
|
||||
b482703612d0c586f76cfcb21cfd2103c957251504a8c0ac4c86c9c6f3e429fffd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:fd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:29d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:58832bdeb26feafc31b46277cf3fb5d7a17dfb7ccd9b1f58ecbe6feb979666828f239ba4d75219260ecac0acf40f0e5e2590f4caa16bbbcd8a155d347967a60729d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:
|
||||
84e50dd9a0f197e3893c38dbd91fafc344c1776d3a400e2f0f0ee7aa829eb8a22c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:2c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:69e6a4491a63837316e86a5f4ba7cd0d731ecc58f1d0a264c67c89befdd8d3829d8de13b33cc0bf513931715c7809657e2bfb960e5c764c971d733746093e500f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:
|
||||
b322d46577a2a991a4d1698287832a39c487ef776b4bff037a05c7f1812bdeeceb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:eb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:19f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:c7b55137317ca21e33489ff6a9bfab97c855dc6f85684a70a9125a261b56d5e6f149c5774d734f2d8debfc77b721896a8267c23768e9badb910eef83ec25880219f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:
|
||||
960cab5034b9838d098d2dcbf4364bec16d388f6376d73a6273b70f82bbc98c05e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:5e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:27d4c3a1811ef9d4360b3bdd133c2ccc30d02c2f248215776cb07ee4177f9b13fc42dd70a6c2fed8f225c7663c7f182e7ee8eccff20dc7b0e1d5834ec5b1ea01f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:
|
||||
eb77b2638f23eebc82efe45ee9e5a0326637401e663ed029699b21e6443fb48e9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:99e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:18dc56d7bd9acd4f4daa78540b4ac8ff7aa9815f45a0bba370731a14eaabe96df8b5f37dbf8eae4cb15a64b244651e59d6a3d6761d9e3c50f2d0cbb09c05ec0699e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:
|
||||
b625aa89d3f7308715427b6c39bbac58effd3a0fb7316f7a22b99ee5922f2dc965a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:65a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:e07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:01bb901d83b8b682d3614af46a807ba2691358feb775325d3423f549ff0aa5757e4e1a74e9c70f9721d8f354b319d4f4a1d91445c870fd0ffb94fed64664730de07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:
|
||||
b1c9f8bd03fe82e78f5c0fb06450f27dacdf716434db268275df3e1dc177af427fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:7fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:4b229951ef262f16978f7914bc672e7226c5f8379d2778c5a2dc0a2650869f7acfbd0bcd30fdb0619bb44fc1ae5939b87cc318133009c20395b6c7eb98107701331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:
|
||||
6d8cdb2e075f3a2f86137214cb236ceb89a6728bb4a200806bf3557fb78fac6957a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:57a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:a6cbc947f9c87d1455cf1a708528c090f11ecee4855d1dbaadf47454a4de55fa4ce84b36d73a5b5f8f59298ccf21992df492ef34163d87753b7e9d32f2c3660b7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:
|
||||
47adc6d6bf571ee9570ca0f75b604ac43e303e4ab339ca9b53cacc5be45b2ccba3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:a3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:ce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:4e8c318343c306adbba60c92b75cb0569b9219d8a86e5d57752ed235fc109a43c2cf4e942cacf297279fbb28675347e08027722a4eb7395e00a17495d32edf0bce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:
|
||||
3c19b50b0fe47961719c381d0d8da9b9869d312f13e3298b97fb22f0af29cbbe0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:8ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:efbd41f26a5d62685516f882b6ec74e0d5a71830d203c231248f26e99a9c6578ec900d68cdb8fa7216ad0d24f9ecbc9ffa655351666582f626645395a31fa7048ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:
|
||||
34e1e9d539107eb86b393a5ccea1496d35bc7d5e9a8c5159d957e4e5852b3eb00ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:0ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:32d22904d3e7012d6f5a441b0b4228064a5cf95b723a66b048a087ecd55920c31c204c3f2006891a85dd1932e3f1d614cfd633b5e63291c6d8166f3011431e09a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:
|
||||
49dd473ede6aa3c866824a40ada4996c239a20d84c9365e4f0a4554f8031b9cf788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:d2fde02791e720852507faa7c3789040d9ef86646321f313ac557f4002491542dd67d05c6990cdb0d495501fbc5d5188bfbb84dc1bf6098bee0603a47fc2690f3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:
|
||||
331c64da482b6b551373c36481a02d8136ecadbb01ab114b4470bf41607ac57152a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:52a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:20e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:22c99aa946ead39ac7997562810c01c20b46bd610645bd2d56dcdcbaacc5452c74fbf4b8b1813b0e94c30d808ce5498e61d4f7ccbb4cc5f04dfc6140825a960020e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:
|
||||
5c0b96f2af8712122cf743c8f8dc77b6cd5570a7de13297bb3dde1886213cce20510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:0510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:06e5d8436ac7705b3a90f1631cdd38ec1a3fa49778a9b9f2fa5ebea4e7d560ada7dd26ff42fafa8ba420323742761aca6904940dc21bbef63ff72daab45d430b54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:
|
||||
bf5ba5d6a49dd5ef7b4d5d7d3e4ecc505c01f6ccee4c54b5ef7b40af6a4541401be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:1be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:16152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:279cace6fdaf3945e3837df474b28646143747632bede93e7a66f5ca291d2c24978512ca0cb8827c8c322685bd605503a5ec94dbae61bbdcae1e49650602bc0716152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:
|
||||
65de297b70cbe80980500af0561a24db50001000125f4490366d8300d3128592ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:7a9b736b01cc92a3349f1a3c32dbd91959825394ff443c567405e899c8185ce8fad9500e1fce89d95a6253c00477435acf04bff993de1b00495def0834ee1f07131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:
|
||||
0826e7333324e7ec8c764292f6015d4670e9b8d7c4a89e8d909e8ef435d18d15ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:7f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:4bac7fabec8724d81ab09ae130874d70b5213492104372f601ae5abb10532799373c4dad215876441f474e2c006be37c3c8f5f6f017d0870414fd276a8f428087f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:
|
||||
00ad6227977b5f38ccda994d928bba9086d2daeb013f8690db986648b90c1d4591a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:91a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:cb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:dc501db79fd782bc88cae792557d5d273f9ba560c7d90037fe84ac879d684f612a77452c4443e95c07b8be192c35769b17bbdfca42280de796d92119d833670dcb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:
|
||||
1521c6dbd6f724de73eaf7b56264f01035c04e01c1f3eb3cbe83efd26c439ada2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:3e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:a817ed23ec398a128601c1832dc6af7643bf3a5f517bcc579450fdb4759028f4966164125f6ebd0d6bf86ff298a39c766d0c21fdb0cbfdf81cd0eb1f03cd8a083e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:
|
||||
17e5f0a8f34751babc5c723ecf339306992f39ea065ac140fcbc397d2dd32c4b4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:efe2cb63fe7b4fc98946dc82fb6998e741ed9ce6b9c1a93bb45bc0a7d8396d7405282b43fe363ba5b23589f8e1fae130e157ce888cd72d053d0cc19d257a4300c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:
|
||||
0cd7aa7d605e44d5ffb97966b2cb93c189e4c5a85db87fad7ab8d62463c59b594889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:4889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:28a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:bf9115fd3d02706e398d4bf3b02a82674ff3041508fd39d29f867e501634b9261f516a794f98738d7c7013a3f2f858ffdd08047fb6bf3dddfb4b4f4cbeef300328a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:
|
||||
33371d9e892f9875052ac8e325ba505e7477c1ace24ba7822643d43d0acef3de35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:27a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:985ca446ddc007827cc8f2852cbd8115ef8c5975e9d7ce96d74dfed859aa14a4c15254006bea5e08359efe2625d715e0897ee5a16f151203be5010418637de0527a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:
|
||||
beedb8073df58f8c1bffbdbd77ec7decb2c82a9babecefc0331507bdc2c2a7e7b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:35ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:8c890cccadc7760e1e82e43c44b3dc0b685a48b479ae13cc0a6b0557d0fb1cbabba63d2a96843412ea8d36c50acbf52b92cfb2dce49dc48af6ddcf8ee47a860835ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:
|
||||
9184ef618816832592bc8eb35f4ffd4ff98dfbf7776c90f2aad212ce7e03351e687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:b3c24e75132c563475422d5ea412b5c1e8e6e5ea1c08ead1393c412da134c9a1638284ea7e2ca032fe3d3e32a9066a8c8839903f6ef46e966bb5e492d8c2aa00729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:
|
||||
354e13152ee1fe748a1252204c6527bdc1b1eb2eb53678150e6359924708d812d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:8e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:de2b46e65f3decef34332e500f2e11306fbdcf1be85a1c1ee68ba3045dcec2c7be608d22927da1f44c0e2083ae622cf3c29d893887994efcfa2ca594f5051f038e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:
|
||||
7ff62d4b3c4d99d342d4bb401d726b21e99f4ef592149fc311b68761f5567ff67fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:7fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:058f79927fbf6178724815c7b11c63baaa90bcc15d7272be082f8a9141861c816433055f6cf6491424853f9ec78bb91ace913a93411b4e5ed58bc4ba5715c60a99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:
|
||||
6cabadd03f8a2e6ebab96a74f80e18164e4d1b6baa678f5a82e25604af989aaf2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:4e65c6c1d493045e8a9250e397c1d1d30ffed24db66a8961aa458f8f0fcb760c39fe8657d7ab8f84000b96d519717cff71f926522c1efec7f8b2624eae55f60c279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:
|
||||
0fa0c32c3ae34be51b92f91945405981a8e202488558a8e220c288c7d6a5532dd6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:d6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:53f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:7e9ab85ee94fe4b35dcb545329a0ef25923de5c9dc23e7df1a7e77ab0dcfb89e03f4e785ca6429cb2b0df50da6230f733f00f33a45c4e576cd40bdb84f1ae00153f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:
|
||||
7b06f88026fa86f39fce2426f67cc5996bedd0cfc4b5ebb1b5e3edbb47e080aa3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:71175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:42f133e34e3eb7032a133ed781537ec62e44a5ce8381e5e0bf9e13a914a4b2c757811d6d3b1e86672424ea4230d10f7c610abb7069e61e319b4066a2bd7bc90071175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:
|
||||
c3f5e149968a24f4de9119531975f443015ccca305d7119ed4749e8bf6d94fc739aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:39aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:5fa2b531677b00b85b0a313cbd479f55f4ab3ec5cfce5e454d2b74176ccc3399c899f9d6b51ed4c1e76185ac9fe730c4b4014044f7041185bc3c85722eb2ea02c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:
|
||||
42305c9302f45ea6f87e26e2208fd94b3c4ad037b1b6c83cf6677aa1096a013c3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:d110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:18d05e5d01668e83f40fa3bbee28b388acf318d1b0b5ad668c672f345c8eda14c2f884cd2a9039459ce0810bc5b580fe70d3964a43edb49e73a6ff914bbf040cd110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:
|
||||
c57a43dcd7bab8516009546918d71ad459b7345efdca8d4f19929875c839d7222083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:2083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:1edef9bc036971f1fa88edf45393c802e6c1a1631c8a06871a09a320821dce40beca97e53a0361a955a4c6d60b8ca8e400c81340911ccb4f56284041cdbb1804a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:
|
||||
2dddb6b8fd04fa90ece1a709f8418f2e5d0c9c43afe7cfce19e6ad15a73476f78059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:8059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:c634ea7bf72e895a2e796e2834201415b8b45e05e045559284eb9052c0e84f62a5a9f0c9764f7576788c7228b19ef517c195497325a48a9344b147c12fd75509474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:
|
||||
5547f1004baedfce5cfc0850b05302374aad24f6163994ecd751df3af3c106207ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:7ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:29df3ad589009c667baa5e72dabb4e53cb7876de4e7efe5cc21ead7fa878db57f97c1103ddb39a861eb88653c1d4ec3b4306e4584b47b8bc90423119e7e4af00a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:
|
||||
3dd7203c237aefe9e38a201ff341490179905f9f100828da18fcbe58768b5760f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:4c036935a96abc0d050d907bedbe9946fb97439f039c742e051ccf09add7df44d17da98c2ca01bdc2424da1e4debf347f8fff48ac8030d2cc07f9575c044be04db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:
|
||||
282775df9ebbd7c5a65f3a2b096e36ee64a8f8ea719da77758739e4e7476111da2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:a2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:14cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:15763973859402907d8dcb86adc24a2a168ba3abf2246173d6348afed51ef60b0c0edeff4e10bcef4c6e5778c8bc1f5e9ee0237373445b455155d23de127a20214cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:
|
||||
4730a5cf9772d7d6665ba787bea4c95252e6ecd63ec62390547bf100c0a46375f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:552c7347bdfe131646ce0932d82a36d2c1b76d7c30ee890e0592e19f9d18b9a56f48d7a9b68c017da6b550c943af4a907baf317e419fbbc96f6cf4bfad42de00e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:
|
||||
2770aadd1d123e9547832dfb2a837eba089179ef4f23abc4a53f2a714e423ee23c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:3c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:f267715e9a84c7314f2d5869ef4ab8d2149a13f7e8e1c728c423906293b49ce6283454dd1c7b04741df2eabedc4d6ab1397dc95a679df04d2c17d66c79bb7601a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:
|
||||
4fdab7c1600e70114b11f533242376af7614b4d5da046ac4bedea21d8a361598a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:5075c090cfbeb6b01802af7f4da5aa4f434d5ee2f3530eebb75c85e08621f83edc08aa96693894a4277633ba81e19e9e55af5c495daa5e1a6f8cbb79c01c7207da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:
|
||||
264504604e70d72dc4474dbb34913e9c0f806dfe18c7879a41762a9e4390ec61eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:eea439a00f7e459b402b835150a779eed171ab971bd1b58dcc7f9386dadd583de8dc69e267121dde41f0f9493d450b16219cdf3c22f09482ce402fe17ca49e08901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:
|
||||
2ca7447a3668b748b1fd3d52d2080d30e34d397bb2846caf8f659ac168788ca5ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:f93ada15ae9cd2b54f26f86f0c28392aed5eb6b6b44d01a4e33a54e7da37c38e8d53366f73fd85be642e4ec81236d163f0d025e76c8bbdd65d43df49f09c1f01a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:
|
||||
494ea9bcce26885b7d17d1fc114448f239f0ce46e5f247b4c999fa86296924726901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:6901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:3badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:548a093a680361b7dc56f14503b55eeec3b3f4fd4ca99d6aedce0830f7f4ae2f7328539b34c48fc9760922333dae9c7c017e7db73b8faa6c06be05e347992b063badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:
|
||||
00d735ebaee75dd579a40dfd82508274d01a1572df99b811d5b01190d82192e4ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:59c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:dcdc54611937d2bd06cacd9818b3be15ce7425427a75f50d197a337a3b8ba6714ef48866f243bd5ac7415e914517a2c1c5a953f432b99db0e620d64f74eb850559c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:
|
||||
8c34b905440b61911d1d8137c53d46a1a76d4609af973e18eb4c5709295627bbb69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:b69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:3e0b72073dc9375eedcca6c4fc1cd315938a050c92716bd2284f4629a962beec0b7d7cf16ab923d58f5b90d3901a8e5c75c8f17dab9998e007d8c49511973d0e30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:
|
||||
77a83e18c9f000eeff7deeac959ecba2206c0aa39d2f0e2aed5729482a7a022962b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:62b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:1eaad8420ac12c99ac1ff4476678e3cbbe94da6a797f174664d5ee0f641433fb1e7cb2f5613e10805df8654cd8e0d45d96230932bc7f20b04eae836435134309f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:
|
||||
73b03373ef1fd849005ecd6270dd9906f19f4439e40376cdbc520902bc976812663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:a40abe98fc69da8a1ff9ff5c2cca93632e975980ee8b82c3c376022d6524ab736d01b072f2b681b5f1cd3ea067012ed6d074e949c42327a366caa9e4750a3c08d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:
|
||||
eab179e41ed5c889ffe6aabdc054faf1307c395e46e313e17a14fe01023ffa3086f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:86f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:143cb28027c2f82e375e5f340e7fe6e60ce7bd51000b49c74168af85e26ed2ed630ed2672090164cc54b052da694ebdd21a21b3053f4dcfd7895ea5f6c8aa80d971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:
|
||||
fbf146ebd51075570ec51ac410ae9f391db75b610ada6362b4dbd949656cfb66be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:cd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:6768006fe0f201b217dd10eb05d4b82adcfeb2ecfc8373c3308f4150394811eb60491881a2e53d1289d96478e18a64c34b2a19832cdccfd96a2e4a0c469fdc0bcd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:
|
||||
dff0eb6b426dea2fd33c1d3fc24df9b31b486facb7edb8502954a3e8da99d9fdc245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:c245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:6b48b10f545ddb7a89cd5829f4e5b20146cf6bc96e550d06f65de8bdae7ccdded26cd630f86c9266bccf88e924033e04f83a54f8290d7f734cf8673cca8f9703e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:
|
||||
9f32958c7679b90fd5036056a75ec2eb2f56ec1effc7c012461dc89a3a1674201d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:1d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:9881a5763bdb259a3fefbba3d957162d6c70b804fa94ab613406a6ec42505b8789465ca1a9a33e1895988842270c55e5bdd5483f6b17b31781b593507a6c1808a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:
|
||||
f86d6f766f88b00717b7d6327eb26cf3ceeba5385184426f9cfd8295e2421ff2cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:da8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:ec61c0b292203a8f1d87235ede92b74723c8d23408423773ae50b1e9bc4464e03e446da9dce4c39f6dd159bea26c009ed00120bc36d4a247dc0d24bcefcc110cda8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:
|
||||
a5b34cefab9479df8389d7e6f6c146aa8affb0bec837f78af64624a145cc344e7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:2fbd899d72b6d39e4f45b8b62cbbd5f3c0acb1ad8540913fa585877e91ccfef7bee50a4b0f9fedf5cc1e0d1953ad399c8389a93391e1b7c929af6d6f3b796c08e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:
|
||||
ad75c9ce299c4d59393367d77a4c9f8df8dcec765c6dbd25b527fb7669913604b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:62fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:6b7ef27bcfbf2b714985033764fccff555e3f5bc44610d6c8c62117cb3831a07f4a8bddb0eaed1d46b0289b15de1aa4dcc17d71be96a09e66ba4dc4627c7870562fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:
|
||||
1ced574529b9b416977e92eb39448a8717cac2934a243a5c44fb44b73ccc16da85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:e0303aefe08a77738dcc657afbb9b835ed279613a53c73fdc5ddbfb350e5cff4d6c9bb43dc07c95bf4e23b64c40f8804c7169952e3c8d59a7197241bfed0740f1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:
|
||||
f0790d93e2d3b84f61ef4c807147aba410e415e72b71b0d61d01026fed99da3defdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:efdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:7973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:08773a6a78762cbb1e25fcbb29139941bdf16f4e09a1fa08fc701f32f933edd74c0ae983c12a0a5b020b6bcf44bb719dde8ed0781a8298265640e1608c98b3017973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:
|
||||
4cb9df7ce6fae9d62ba09e8eb70e4c969bdeafcb5ec7d7024326e6603b0621bf018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:14627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:e33c07836c537d6bfbd0f4592d6e35b163499ba78dc7ffcec565d04f9a7db781943e29e6ce76763e9baddf57437fd9c6b03239a6e6850e4502a356c2e12c370514627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:
|
||||
a136e009d53e5ef59d0946bc175663a86bc0fcd29eadd95cfc9d266037b1e4fb9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:bc094ba91c115dee15d753361a75f3f03d6af45c92157e95dbe8d32194b6c5ce72b9dc66f73df12dca0b639f3e791d478616a1f8d7359a42c8eae0dda16b1606a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:
|
||||
ff0f1c57dd884fbeea6e2917282b79ba67f8a6851267b9f4636dafda33bd2b5bfef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:fef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:d5008486726cce330a29dd7e4d7474d735798201afd1206feb869a112e5b43523c06976761be3cf9b2716378273c94f93572a7d2b8982634e0755c632b449008522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:
|
||||
0bc6af64de5709d3dbc28f7ef6d3fe28b6de529f08f5857ccb910695de454f56fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:dbc7134d1cd6b0813b53352714b6df939498e91cf37c324337d9c088a1b998347d26185b430900412929e4f63e910379fc42e355a4e98f6fee27dafad1957206ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:
|
||||
2f5e83bd5b412e71ae3e9084cd369efcc79bf6037c4b174dfd6a11fb0f5da218a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:b766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:9f80922bc8db32d0cc43f9936affebe7b2bc35a5d82277cd187b5d50dc7fc4c4832fffa34e9543806b485c04548e7c75429425e14d55d91fc1052efd8667430bb766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:
|
||||
722a2da50e42c11a61c9afac7be1a2fed2267d650f8f7d8e5bc706b807c1b91dfd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:fd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:c2695a57172aaa31bd0890f231ca8eeec0287a87172669a899ad0891cea4c47579b50420e791cdec8c182c8a0e8dde21b2480b0cfd8111e28e5603347a352d04173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:
|
||||
5fe9c3960ed5bd374cc94d42357e6a24dc7e3060788f726365defacf13cd12da0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:379f9c54c413af0d192e9bc736b29da9d521e7ba7841d309f9bcc1e742ec4308fe9f7ba51e0b22aed487cb4aa3913b9bebfb3aacd38f4039f9bbbebe1ad80002c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:
|
||||
ec2fa541ac14b414149c3825eaa7001b795aa1957d4040dda92573904afa7ee471b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:71b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:2749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:84d18d56f964e3776759bba92c510c2b6d574555c3cddade212da90374554991e7d77e278d63e34693e1958078cc3685f8c41c1f5342e351899638ef612114012749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:
|
||||
6132692a5ef27bf476b1e991e6c431a8c764f1aebd470282db3321bb7cb09c207a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:7a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:a9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:eb677f3347e1a1ea929efdf62bf9105a6c8f4993033b4f6d03cb0dbf9c742b270704e383ab7c0676bdb1ad0ce9b16673083c9602ec10ae1dd98e8748b336440ba9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:
|
||||
f219b2101164aa9723bde3a7346f68a35061c01f9782072580ba32df903ba891f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:17f0127ca3bafa5f4ee959cd60f772be87a0034961517e39a0a1d0f4b9e26db1336e60c82b352c4cbacdbbd11771c3774f8cc5a1a795d6e4f4ebd51def36770b015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:
|
||||
fc180035aec0f5ede7bda93bf77ade7a81ed06de07ee2e3aa8576be81608610a4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:a43a71c3a19c35660dae6f31a254b8c0ea3593fc8fca74d13640012b9e9473d4afe070db01e7fb399bf4ca6070e062180011285a67dd6858b761e46c6bd32004b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:
|
||||
a2836a65427912122d25dcdfc99d7046fe9b53d5c1bb23617f11890e94ca93ed8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:e6a9a6b436559a4320c45c0c2c4a2aedecb90d416d52c82680ac7330d062aebef3e9ac9f2c5ffa455c9be113013a2b282e5600fd306435ada83b1e48ba2a3605813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:
|
||||
f051af426d0c3282fafc8bf912ade1c24211a95ad200e1eef549320e1cb1a252fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:b48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:b8f713578a64466719aceb432fce302a87cf066bf3e102a350616921a840964bfc7e685d8fd17455ac3eb4861edcb8979d35e3a4bd82a078cd707721d733400eb48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:
|
||||
a103e92672c65f81ea5da1fff1a4038788479e941d503a756f4a755201a57c1dee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:ee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:2aa2035c2ce5b5e6ae161e168f3ad0d6592bcf2c4a049d3ed342fceb56be9c7cb372027573ae0178e8878ebefca7b030327b8aad41857de58cb78e1a00cbac05b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:
|
||||
d47c1b4b9e50cbb71fd07d096d91d87213d44b024373044761c4822f9d9df880f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:88d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:627e7ca7e34ed6331d62b9541c1ea9a9292be7b0a65d805e266b5122272a82db7d765acc7e2a290d685804922f91ed04a3c382c03ff21a1768f584413c4e5f0088d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:
|
||||
fc0c32c5eb6c71ea08dc2b300cbcef18fdde3ea20f68f21733237b4ddaab900e47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:6887c6e2b98a82af5ee3dfa7ca2cb25d9c10745620a82956acba85cb57c8ec24279fa42f092359a1b6bbeafba050f14b6288209e6ef7bc1e0a2b872c1138f305a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:
|
||||
a8d73d639a23cc6a967ef31bcabb5d063e53e1eab8fcc7cab9bc3a17fde9c2f88daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:8daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:fd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:c4dcef1a2453939b364b340250c3129431431d5ba3f47670ab07ce680c69bf28b678627c76a6360fc40dc109aa7dea371b825e46134f624572182acf3957e70ffd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:
|
||||
79c7dcb7d59a8df6b2b2ba0413059d89680995c20e916da01b8f067dc60cdeb4298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:7b7cbe44c771e4371bae13b0722babcc1064155732962f407cba2acd35381d42210bece822f4681121fd4dab745a1f3077922fba1a78045b712902baccac660e5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:
|
||||
b9ced0412593fefed95e94ac965e5b23ff9d4b0e797db02bf497994d3b793e60c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:f1b797ded8a6942b12626848340fb719fcddafd98f33e2992d357bfdd35933c7ac561e5b2f939464338c5666854ca885c4d046eb2c54e48a1b5ed266ad34de05555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:
|
||||
81da168f02d46bb87cda845da43f8a6cba2c016878d6f49c6f061a60f155a04aaff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:aff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:4aaca947e3f22cc8b8588ee030ace8f6b5f5711c2974f20cc18c3b655b07a5bc1366b59a1708032d12cae01ab794f8cbcc1a330874a75035db1d69422d2fc00c6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:
|
||||
af2e60da0f29bb1614fc3f193cc353331986b73f3f9a0aec9421b9473d6a4b6ac8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:c8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:7dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:50f9f941a8da9f6240f76d2fa3b06dd6b2292ed32d1c05218097d34d8a19dfe553f76ae3c6b4a2ed20852128461540decf418f52d38e64037eec7771bd1afe007dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:
|
||||
605f90b53d8e4a3b48b97d745439f2a0807d83b8502e8e2979f03e8d376ac9feaa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:aa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:3bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:dd0212e63288cbe14a4569b4d891da3c7f92727c5e7f9a801cf9d6827085e7095b669d7d45f882ca5f0745dccd24d87a57181320191e5b7a47c3f7f2dccbd7073bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:
|
||||
9e2c3d189838f4dd52ef0832886874c5ca493983ddadc07cbc570af2ee9d6209f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:38a31b6b465084738262a26c065fe5d9e2886bf9dd35cde05df9bad0cc7db401c750aa19e66090bce25a3c721201e60502c8c10454346648af065eab0ee7d80f19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:
|
||||
575f8fb6c7465e92c250caeec1786224bc3eed729e463953a394c9849cba908f71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:2cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:903b484cb24bc503cdced844614073256c6d5aa45f1f9f62c7f22e5649212bc1d6ef9eaa617b6b835a6de2beff2faac83d37a4a5fc5cc3b556f56edde2651f022cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,101 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2018-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
use criterion::{criterion_group, Criterion};
|
||||
|
||||
mod ed25519_benches {
|
||||
use super::*;
|
||||
use ed25519_dalek::Signature;
|
||||
use ed25519_dalek::Signer;
|
||||
use ed25519_dalek::SigningKey;
|
||||
use rand::prelude::ThreadRng;
|
||||
use rand::thread_rng;
|
||||
|
||||
fn sign(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
|
||||
c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg)));
|
||||
}
|
||||
|
||||
fn verify(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
let sig: Signature = keypair.sign(msg);
|
||||
|
||||
c.bench_function("Ed25519 signature verification", move |b| {
|
||||
b.iter(|| keypair.verify(msg, &sig))
|
||||
});
|
||||
}
|
||||
|
||||
fn verify_strict(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypair: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let msg: &[u8] = b"";
|
||||
let sig: Signature = keypair.sign(msg);
|
||||
|
||||
c.bench_function("Ed25519 strict signature verification", move |b| {
|
||||
b.iter(|| keypair.verify_strict(msg, &sig))
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "batch")]
|
||||
fn verify_batch_signatures(c: &mut Criterion) {
|
||||
use ed25519_dalek::verify_batch;
|
||||
|
||||
static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256];
|
||||
|
||||
// Benchmark batch verification for all the above batch sizes
|
||||
let mut group = c.benchmark_group("Ed25519 batch signature verification");
|
||||
for size in BATCH_SIZES {
|
||||
let name = format!("size={size}");
|
||||
group.bench_function(name, |b| {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
let keypairs: Vec<SigningKey> = (0..size)
|
||||
.map(|_| SigningKey::generate(&mut csprng))
|
||||
.collect();
|
||||
let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect();
|
||||
let signatures: Vec<Signature> =
|
||||
keypairs.iter().map(|key| key.sign(&msg)).collect();
|
||||
let verifying_keys: Vec<_> =
|
||||
keypairs.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If the above function isn't defined, make a placeholder function
|
||||
#[cfg(not(feature = "batch"))]
|
||||
fn verify_batch_signatures(_: &mut Criterion) {}
|
||||
|
||||
fn key_generation(c: &mut Criterion) {
|
||||
let mut csprng: ThreadRng = thread_rng();
|
||||
|
||||
c.bench_function("Ed25519 keypair generation", move |b| {
|
||||
b.iter(|| SigningKey::generate(&mut csprng))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = ed25519_benches;
|
||||
config = Criterion::default();
|
||||
targets =
|
||||
sign,
|
||||
verify,
|
||||
verify_strict,
|
||||
verify_batch_signatures,
|
||||
key_generation,
|
||||
}
|
||||
}
|
||||
|
||||
criterion::criterion_main!(ed25519_benches::ed25519_benches);
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
@ -1,12 +0,0 @@
|
||||
<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>
|
||||
@ -1,242 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! Batch signature verification.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::iter::once;
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use curve25519_dalek::traits::IsIdentity;
|
||||
use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||
|
||||
pub use curve25519_dalek::digest::Digest;
|
||||
|
||||
use merlin::Transcript;
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::errors::SignatureError;
|
||||
use crate::signature::InternalSignature;
|
||||
use crate::VerifyingKey;
|
||||
|
||||
/// An implementation of `rand_core::RngCore` which does nothing. This is necessary because merlin
|
||||
/// demands an `Rng` as input to `TranscriptRngBuilder::finalize()`. Using this with `finalize()`
|
||||
/// yields a PRG whose input is the hashed transcript.
|
||||
struct ZeroRng;
|
||||
|
||||
impl rand_core::RngCore for ZeroRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
rand_core::impls::next_u32_via_fill(self)
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
rand_core::impls::next_u64_via_fill(self)
|
||||
}
|
||||
|
||||
/// A no-op function which leaves the destination bytes for randomness unchanged.
|
||||
///
|
||||
/// In this case, the internal merlin code is initialising the destination
|
||||
/// by doing `[0u8; …]`, which means that when we call
|
||||
/// `merlin::TranscriptRngBuilder.finalize()`, rather than rekeying the
|
||||
/// STROBE state based on external randomness, we're doing an
|
||||
/// `ENC_{state}(00000000000000000000000000000000)` operation, which is
|
||||
/// identical to the STROBE `MAC` operation.
|
||||
fn fill_bytes(&mut self, _dest: &mut [u8]) {}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// `TranscriptRngBuilder::finalize()` requires a `CryptoRng`
|
||||
impl rand_core::CryptoRng for ZeroRng {}
|
||||
|
||||
// We write our own gen() function so we don't need to pull in the rand crate
|
||||
fn gen_u128<R: RngCore>(rng: &mut R) -> u128 {
|
||||
let mut buf = [0u8; 16];
|
||||
rng.fill_bytes(&mut buf);
|
||||
u128::from_le_bytes(buf)
|
||||
}
|
||||
|
||||
/// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `messages` is a slice of byte slices, one per signed message.
|
||||
/// * `signatures` is a slice of `Signature`s.
|
||||
/// * `verifying_keys` is a slice of `VerifyingKey`s.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * A `Result` whose `Ok` value is an empty tuple and whose `Err` value is a
|
||||
/// `SignatureError` containing a description of the internal error which
|
||||
/// occurred.
|
||||
///
|
||||
/// ## On Deterministic Nonces
|
||||
///
|
||||
/// The nonces for batch signature verification are derived purely from the inputs to this function
|
||||
/// themselves.
|
||||
///
|
||||
/// In any sigma protocol it is wise to include as much context pertaining
|
||||
/// to the public state in the protocol as possible, to avoid malleability
|
||||
/// attacks where an adversary alters publics in an algebraic manner that
|
||||
/// manages to satisfy the equations for the protocol in question.
|
||||
///
|
||||
/// For ed25519 batch verification we include the following as scalars in the protocol transcript:
|
||||
///
|
||||
/// * All of the computed `H(R||A||M)`s to the protocol transcript, and
|
||||
/// * All of the `s` components of each signature.
|
||||
///
|
||||
/// The former, while not quite as elegant as adding the `R`s, `A`s, and
|
||||
/// `M`s separately, saves us a bit of context hashing since the
|
||||
/// `H(R||A||M)`s need to be computed for the verification equation anyway.
|
||||
///
|
||||
/// The latter prevents a malleability attack wherein an adversary, without access
|
||||
/// to the signing key(s), can take any valid signature, `(s,R)`, and swap
|
||||
/// `s` with `s' = -z1`. This doesn't constitute a signature forgery, merely
|
||||
/// a vulnerability, as the resulting signature will not pass single
|
||||
/// signature verification. (Thanks to Github users @real_or_random and
|
||||
/// @jonasnick for pointing out this malleability issue.)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::{
|
||||
/// verify_batch, SigningKey, VerifyingKey, Signer, Signature,
|
||||
/// };
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut csprng = OsRng;
|
||||
/// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect();
|
||||
/// let msg: &[u8] = b"They're good dogs Brant";
|
||||
/// let messages: Vec<_> = (0..64).map(|_| msg).collect();
|
||||
/// let signatures: Vec<_> = signing_keys.iter().map(|key| key.sign(&msg)).collect();
|
||||
/// let verifying_keys: Vec<_> = signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
///
|
||||
/// let result = verify_batch(&messages, &signatures, &verifying_keys);
|
||||
/// assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_batch(
|
||||
messages: &[&[u8]],
|
||||
signatures: &[ed25519::Signature],
|
||||
verifying_keys: &[VerifyingKey],
|
||||
) -> Result<(), SignatureError> {
|
||||
// Return an Error if any of the vectors were not the same size as the others.
|
||||
if signatures.len() != messages.len()
|
||||
|| signatures.len() != verifying_keys.len()
|
||||
|| verifying_keys.len() != messages.len()
|
||||
{
|
||||
return Err(InternalError::ArrayLength {
|
||||
name_a: "signatures",
|
||||
length_a: signatures.len(),
|
||||
name_b: "messages",
|
||||
length_b: messages.len(),
|
||||
name_c: "verifying_keys",
|
||||
length_c: verifying_keys.len(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
// Make a transcript which logs all inputs to this function
|
||||
let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification");
|
||||
|
||||
// We make one optimization in the transcript: since we will end up computing H(R || A || M)
|
||||
// for each (R, A, M) triplet, we will feed _that_ into our transcript rather than each R, A, M
|
||||
// individually. Since R and A are fixed-length, this modification is secure so long as SHA-512
|
||||
// is collision-resistant.
|
||||
// It suffices to take `verifying_keys[i].as_bytes()` even though a `VerifyingKey` has two
|
||||
// fields, and `as_bytes()` only returns the bytes of the first. This is because of an
|
||||
// invariant guaranteed by `VerifyingKey`: the second field is always the (unique)
|
||||
// decompression of the first. Thus, the serialized first field is a unique representation of
|
||||
// the entire `VerifyingKey`.
|
||||
let hrams: Vec<[u8; 64]> = (0..signatures.len())
|
||||
.map(|i| {
|
||||
// Compute H(R || A || M), where
|
||||
// R = sig.R
|
||||
// A = verifying key
|
||||
// M = msg
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
h.update(signatures[i].r_bytes());
|
||||
h.update(verifying_keys[i].as_bytes());
|
||||
h.update(&messages[i]);
|
||||
*h.finalize().as_ref()
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Update transcript with the hashes above. This covers verifying_keys, messages, and the R
|
||||
// half of signatures
|
||||
for hram in hrams.iter() {
|
||||
transcript.append_message(b"hram", hram);
|
||||
}
|
||||
// Update transcript with the rest of the data. This covers the s half of the signatures
|
||||
for sig in signatures {
|
||||
transcript.append_message(b"sig.s", sig.s_bytes());
|
||||
}
|
||||
|
||||
// All function inputs have now been hashed into the transcript. Finalize it and use it as
|
||||
// randomness for the batch verification.
|
||||
let mut rng = transcript.build_rng().finalize(&mut ZeroRng);
|
||||
|
||||
// Convert all signatures to `InternalSignature`
|
||||
let signatures = signatures
|
||||
.iter()
|
||||
.map(InternalSignature::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
// Convert the H(R || A || M) values into scalars
|
||||
let hrams: Vec<Scalar> = hrams
|
||||
.iter()
|
||||
.map(Scalar::from_bytes_mod_order_wide)
|
||||
.collect();
|
||||
|
||||
// Select a random 128-bit scalar for each signature.
|
||||
let zs: Vec<Scalar> = signatures
|
||||
.iter()
|
||||
.map(|_| Scalar::from(gen_u128(&mut rng)))
|
||||
.collect();
|
||||
|
||||
// Compute the basepoint coefficient, ∑ s[i]z[i] (mod l)
|
||||
let B_coefficient: Scalar = signatures
|
||||
.iter()
|
||||
.map(|sig| sig.s)
|
||||
.zip(zs.iter())
|
||||
.map(|(s, z)| z * s)
|
||||
.sum();
|
||||
|
||||
// Multiply each H(R || A || M) by the random value
|
||||
let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z);
|
||||
|
||||
let Rs = signatures.iter().map(|sig| sig.R.decompress());
|
||||
let As = verifying_keys.iter().map(|pk| Some(pk.point));
|
||||
let B = once(Some(constants::ED25519_BASEPOINT_POINT));
|
||||
|
||||
// Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0
|
||||
let id = EdwardsPoint::optional_multiscalar_mul(
|
||||
once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams),
|
||||
B.chain(Rs).chain(As),
|
||||
)
|
||||
.ok_or(InternalError::Verify)?;
|
||||
|
||||
if id.is_identity() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InternalError::Verify.into())
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! Common constants such as buffer sizes for keypairs and signatures.
|
||||
|
||||
/// The length of a ed25519 `Signature`, in bytes.
|
||||
pub const SIGNATURE_LENGTH: usize = 64;
|
||||
|
||||
/// The length of a ed25519 `SecretKey`, in bytes.
|
||||
pub const SECRET_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// The length of an ed25519 `PublicKey`, in bytes.
|
||||
pub const PUBLIC_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// The length of an ed25519 `Keypair`, in bytes.
|
||||
pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH;
|
||||
|
||||
/// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes.
|
||||
const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes.
|
||||
const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32;
|
||||
|
||||
/// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes.
|
||||
pub const EXPANDED_SECRET_KEY_LENGTH: usize =
|
||||
EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH;
|
||||
@ -1,110 +0,0 @@
|
||||
use crate::{InternalError, SignatureError};
|
||||
|
||||
/// Ed25519 contexts as used by Ed25519ph.
|
||||
///
|
||||
/// Contexts are domain separator strings that can be used to isolate uses of
|
||||
/// the algorithm between different protocols (which is very hard to reliably do
|
||||
/// otherwise) and between different uses within the same protocol.
|
||||
///
|
||||
/// To create a context, call either of the following:
|
||||
///
|
||||
/// - [`SigningKey::with_context`](crate::SigningKey::with_context)
|
||||
/// - [`VerifyingKey::with_context`](crate::VerifyingKey::with_context)
|
||||
///
|
||||
/// For more information, see [RFC8032 § 8.3](https://www.rfc-editor.org/rfc/rfc8032#section-8.3).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
#[cfg_attr(all(feature = "digest", feature = "rand_core"), doc = "```")]
|
||||
#[cfg_attr(
|
||||
any(not(feature = "digest"), not(feature = "rand_core")),
|
||||
doc = "```ignore"
|
||||
)]
|
||||
/// # fn main() {
|
||||
/// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512};
|
||||
/// # use curve25519_dalek::digest::Digest;
|
||||
/// # use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::{DigestSigner, DigestVerifier};
|
||||
///
|
||||
/// # let mut csprng = OsRng;
|
||||
/// # let signing_key = SigningKey::generate(&mut csprng);
|
||||
/// # let verifying_key = signing_key.verifying_key();
|
||||
/// let context_str = b"Local Channel 3";
|
||||
/// let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
|
||||
///
|
||||
/// // Signer
|
||||
/// let signing_context = signing_key.with_context(context_str).unwrap();
|
||||
/// let signature = signing_context.sign_digest(prehashed_message.clone());
|
||||
///
|
||||
/// // Verifier
|
||||
/// let verifying_context = verifying_key.with_context(context_str).unwrap();
|
||||
/// let verified: bool = verifying_context
|
||||
/// .verify_digest(prehashed_message, &signature)
|
||||
/// .is_ok();
|
||||
///
|
||||
/// # assert!(verified);
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Context<'k, 'v, K> {
|
||||
/// Key this context is being used with.
|
||||
key: &'k K,
|
||||
|
||||
/// Context value: a bytestring no longer than 255 octets.
|
||||
value: &'v [u8],
|
||||
}
|
||||
|
||||
impl<'k, 'v, K> Context<'k, 'v, K> {
|
||||
/// Maximum length of the context value in octets.
|
||||
pub const MAX_LENGTH: usize = 255;
|
||||
|
||||
/// Create a new Ed25519ph context.
|
||||
pub(crate) fn new(key: &'k K, value: &'v [u8]) -> Result<Self, SignatureError> {
|
||||
if value.len() <= Self::MAX_LENGTH {
|
||||
Ok(Self { key, value })
|
||||
} else {
|
||||
Err(SignatureError::from(InternalError::PrehashedContextLength))
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the key.
|
||||
pub fn key(&self) -> &'k K {
|
||||
self.key
|
||||
}
|
||||
|
||||
/// Borrow the context string value.
|
||||
pub fn value(&self) -> &'v [u8] {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "digest"))]
|
||||
mod test {
|
||||
use crate::{Signature, SigningKey, VerifyingKey};
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use ed25519::signature::{DigestSigner, DigestVerifier};
|
||||
use rand::rngs::OsRng;
|
||||
use sha2::Sha512;
|
||||
|
||||
#[test]
|
||||
fn context_correctness() {
|
||||
let mut csprng = OsRng;
|
||||
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
|
||||
let context_str = b"Local Channel 3";
|
||||
let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
|
||||
|
||||
// Signer
|
||||
let signing_context = signing_key.with_context(context_str).unwrap();
|
||||
let signature: Signature = signing_context.sign_digest(prehashed_message.clone());
|
||||
|
||||
// Verifier
|
||||
let verifying_context = verifying_key.with_context(context_str).unwrap();
|
||||
let verified: bool = verifying_context
|
||||
.verify_digest(prehashed_message, &signature)
|
||||
.is_ok();
|
||||
|
||||
assert!(verified);
|
||||
}
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! Errors which may occur when parsing keys and/or signatures to or from wire formats.
|
||||
|
||||
// rustc seems to think the typenames in match statements (e.g. in
|
||||
// Display) should be snake cased, for some reason.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::fmt;
|
||||
use core::fmt::Display;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
/// Internal errors. Most application-level developers will likely not
|
||||
/// need to pay any attention to these.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) enum InternalError {
|
||||
PointDecompression,
|
||||
ScalarFormat,
|
||||
/// An error in the length of bytes handed to a constructor.
|
||||
///
|
||||
/// To use this, pass a string specifying the `name` of the type which is
|
||||
/// returning the error, and the `length` in bytes which its constructor
|
||||
/// expects.
|
||||
BytesLength {
|
||||
name: &'static str,
|
||||
length: usize,
|
||||
},
|
||||
/// The verification equation wasn't satisfied
|
||||
Verify,
|
||||
/// Two arrays did not match in size, making the called signature
|
||||
/// verification method impossible.
|
||||
#[cfg(feature = "batch")]
|
||||
ArrayLength {
|
||||
name_a: &'static str,
|
||||
length_a: usize,
|
||||
name_b: &'static str,
|
||||
length_b: usize,
|
||||
name_c: &'static str,
|
||||
length_c: usize,
|
||||
},
|
||||
/// An ed25519ph signature can only take up to 255 octets of context.
|
||||
#[cfg(feature = "digest")]
|
||||
PrehashedContextLength,
|
||||
/// A mismatched (public, secret) key pair.
|
||||
MismatchedKeypair,
|
||||
}
|
||||
|
||||
impl Display for InternalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
InternalError::PointDecompression => write!(f, "Cannot decompress Edwards point"),
|
||||
InternalError::ScalarFormat => write!(f, "Cannot use scalar with high-bit set"),
|
||||
InternalError::BytesLength { name: n, length: l } => {
|
||||
write!(f, "{} must be {} bytes in length", n, l)
|
||||
}
|
||||
InternalError::Verify => write!(f, "Verification equation was not satisfied"),
|
||||
#[cfg(feature = "batch")]
|
||||
InternalError::ArrayLength {
|
||||
name_a: na,
|
||||
length_a: la,
|
||||
name_b: nb,
|
||||
length_b: lb,
|
||||
name_c: nc,
|
||||
length_c: lc,
|
||||
} => write!(
|
||||
f,
|
||||
"Arrays must be the same length: {} has length {},
|
||||
{} has length {}, {} has length {}.",
|
||||
na, la, nb, lb, nc, lc
|
||||
),
|
||||
#[cfg(feature = "digest")]
|
||||
InternalError::PrehashedContextLength => write!(
|
||||
f,
|
||||
"An ed25519ph signature can only take up to 255 octets of context"
|
||||
),
|
||||
InternalError::MismatchedKeypair => write!(f, "Mismatched Keypair detected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for InternalError {}
|
||||
|
||||
/// Errors which may occur while processing signatures and keypairs.
|
||||
///
|
||||
/// This error may arise due to:
|
||||
///
|
||||
/// * Being given bytes with a length different to what was expected.
|
||||
///
|
||||
/// * A problem decompressing `r`, a curve point, in the `Signature`, or the
|
||||
/// curve point for a `PublicKey`.
|
||||
///
|
||||
/// * A problem with the format of `s`, a scalar, in the `Signature`. This
|
||||
/// is only raised if the high-bit of the scalar was set. (Scalars must
|
||||
/// only be constructed from 255-bit integers.)
|
||||
///
|
||||
/// * Failure of a signature to satisfy the verification equation.
|
||||
pub type SignatureError = ed25519::signature::Error;
|
||||
|
||||
impl From<InternalError> for SignatureError {
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn from(_err: InternalError) -> SignatureError {
|
||||
SignatureError::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn from(err: InternalError) -> SignatureError {
|
||||
SignatureError::from_source(err)
|
||||
}
|
||||
}
|
||||
@ -1,264 +0,0 @@
|
||||
//! Low-level interfaces to ed25519 functions
|
||||
//!
|
||||
//! # ⚠️ Warning: Hazmat
|
||||
//!
|
||||
//! These primitives are easy-to-misuse low-level interfaces.
|
||||
//!
|
||||
//! If you are an end user / non-expert in cryptography, **do not use any of these functions**.
|
||||
//! Failure to use them correctly can lead to catastrophic failures including **full private key
|
||||
//! recovery.**
|
||||
|
||||
// Permit dead code because 1) this module is only public when the `hazmat` feature is set, and 2)
|
||||
// even without `hazmat` we still need this module because this is where `ExpandedSecretKey` is
|
||||
// defined.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::{InternalError, SignatureError};
|
||||
|
||||
use curve25519_dalek::scalar::{clamp_integer, Scalar};
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
// These are used in the functions that are made public when the hazmat feature is set
|
||||
use crate::{Signature, VerifyingKey};
|
||||
use curve25519_dalek::digest::{generic_array::typenum::U64, Digest};
|
||||
|
||||
/// Contains the secret scalar and domain separator used for generating signatures.
|
||||
///
|
||||
/// This is used internally for signing.
|
||||
///
|
||||
/// In the usual Ed25519 signing algorithm, `scalar` and `hash_prefix` are defined such that
|
||||
/// `scalar || hash_prefix = H(sk)` where `sk` is the signing key and `H` is SHA-512.
|
||||
/// **WARNING:** Deriving the values for these fields in any other way can lead to full key
|
||||
/// recovery, as documented in [`raw_sign`] and [`raw_sign_prehashed`].
|
||||
///
|
||||
/// Instances of this secret are automatically overwritten with zeroes when they fall out of scope.
|
||||
pub struct ExpandedSecretKey {
|
||||
/// The secret scalar used for signing
|
||||
pub scalar: Scalar,
|
||||
/// The domain separator used when hashing the message to generate the pseudorandom `r` value
|
||||
pub hash_prefix: [u8; 32],
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Drop for ExpandedSecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.scalar.zeroize();
|
||||
self.hash_prefix.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl ZeroizeOnDrop for ExpandedSecretKey {}
|
||||
|
||||
// Some conversion methods for `ExpandedSecretKey`. The signing methods are defined in
|
||||
// `signing.rs`, since we need them even when `not(feature = "hazmat")`
|
||||
impl ExpandedSecretKey {
|
||||
/// Construct an `ExpandedSecretKey` from an array of 64 bytes. In the spec, the bytes are the
|
||||
/// output of a SHA-512 hash. This clamps the first 32 bytes and uses it as a scalar, and uses
|
||||
/// the second 32 bytes as a domain separator for hashing.
|
||||
pub fn from_bytes(bytes: &[u8; 64]) -> Self {
|
||||
// TODO: Use bytes.split_array_ref once it’s in MSRV.
|
||||
let mut scalar_bytes: [u8; 32] = [0u8; 32];
|
||||
let mut hash_prefix: [u8; 32] = [0u8; 32];
|
||||
scalar_bytes.copy_from_slice(&bytes[00..32]);
|
||||
hash_prefix.copy_from_slice(&bytes[32..64]);
|
||||
|
||||
// For signing, we'll need the integer, clamped, and converted to a Scalar. See
|
||||
// PureEdDSA.keygen in RFC 8032 Appendix A.
|
||||
let scalar = Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes));
|
||||
|
||||
ExpandedSecretKey {
|
||||
scalar,
|
||||
hash_prefix,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct an `ExpandedSecretKey` from a slice of 64 bytes.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose error value is an
|
||||
/// `SignatureError` describing the error that occurred, namely that the given slice's length
|
||||
/// is not 64.
|
||||
pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
|
||||
// Try to coerce bytes to a [u8; 64]
|
||||
bytes.try_into().map(Self::from_bytes).map_err(|_| {
|
||||
InternalError::BytesLength {
|
||||
name: "ExpandedSecretKey",
|
||||
length: 64,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for ExpandedSecretKey {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
Self::from_slice(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute an ordinary Ed25519 signature over the given message. `CtxDigest` is the digest used to
|
||||
/// calculate the pseudorandomness needed for signing. According to the Ed25519 spec, `CtxDigest =
|
||||
/// Sha512`.
|
||||
///
|
||||
/// # ⚠️ Unsafe
|
||||
///
|
||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||
/// `ExpandedSecretKey` can leak your signing key. See
|
||||
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
|
||||
pub fn raw_sign<CtxDigest>(
|
||||
esk: &ExpandedSecretKey,
|
||||
message: &[u8],
|
||||
verifying_key: &VerifyingKey,
|
||||
) -> Signature
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
esk.raw_sign::<CtxDigest>(message, verifying_key)
|
||||
}
|
||||
|
||||
/// Compute a signature over the given prehashed message, the Ed25519ph algorithm defined in
|
||||
/// [RFC8032 §5.1][rfc8032]. `MsgDigest` is the digest function used to hash the signed message.
|
||||
/// `CtxDigest` is the digest function used to calculate the pseudorandomness needed for signing.
|
||||
/// According to the Ed25519 spec, `MsgDigest = CtxDigest = Sha512`.
|
||||
///
|
||||
/// # ⚠️ Unsafe
|
||||
//
|
||||
/// Do NOT use this function unless you absolutely must. Using the wrong values in
|
||||
/// `ExpandedSecretKey` can leak your signing key. See
|
||||
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `esk` is the [`ExpandedSecretKey`] being used for signing
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// `scalar` and `hash_prefix` are usually selected such that `scalar || hash_prefix = H(sk)` where
|
||||
/// `sk` is the signing key
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the
|
||||
/// `prehashed_message` if the context was 255 bytes or less, otherwise
|
||||
/// a `SignatureError`.
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn raw_sign_prehashed<'a, CtxDigest, MsgDigest>(
|
||||
esk: &ExpandedSecretKey,
|
||||
prehashed_message: MsgDigest,
|
||||
verifying_key: &VerifyingKey,
|
||||
context: Option<&'a [u8]>,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
esk.raw_sign_prehashed::<CtxDigest, MsgDigest>(prehashed_message, verifying_key, context)
|
||||
}
|
||||
|
||||
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R
|
||||
/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing.
|
||||
/// According to the Ed25519 spec, `CtxDigest = Sha512`.
|
||||
pub fn raw_verify<CtxDigest>(
|
||||
vk: &VerifyingKey,
|
||||
message: &[u8],
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
vk.raw_verify::<CtxDigest>(message, signature)
|
||||
}
|
||||
|
||||
/// The batched Ed25519 verification check, rejecting non-canonical R values. `MsgDigest` is the
|
||||
/// digest used to hash the signed message. `CtxDigest` is the digest used to calculate the
|
||||
/// pseudorandomness needed for signing. According to the Ed25519 spec, `MsgDigest = CtxDigest =
|
||||
/// Sha512`.
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn raw_verify_prehashed<CtxDigest, MsgDigest>(
|
||||
vk: &VerifyingKey,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
vk.raw_verify_prehashed::<CtxDigest, MsgDigest>(prehashed_message, context, signature)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||
|
||||
// Pick distinct, non-spec 512-bit hash functions for message and sig-context hashing
|
||||
type CtxDigest = blake2::Blake2b512;
|
||||
type MsgDigest = sha3::Sha3_512;
|
||||
|
||||
impl ExpandedSecretKey {
|
||||
// Make a random expanded secret key for testing purposes. This is NOT how you generate
|
||||
// expanded secret keys IRL. They're the hash of a seed.
|
||||
fn random<R: RngCore + CryptoRng>(mut rng: R) -> Self {
|
||||
let mut bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
ExpandedSecretKey::from_bytes(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that raw_sign and raw_verify work when a non-spec CtxDigest is used
|
||||
#[test]
|
||||
fn sign_verify_nonspec() {
|
||||
// Generate the keypair
|
||||
let mut rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(&mut rng);
|
||||
let vk = VerifyingKey::from(&esk);
|
||||
|
||||
let msg = b"Then one day, a piano fell on my head";
|
||||
|
||||
// Sign and verify
|
||||
let sig = raw_sign::<CtxDigest>(&esk, msg, &vk);
|
||||
raw_verify::<CtxDigest>(&vk, msg, &sig).unwrap();
|
||||
}
|
||||
|
||||
// Check that raw_sign_prehashed and raw_verify_prehashed work when distinct, non-spec
|
||||
// MsgDigest and CtxDigest are used
|
||||
#[cfg(feature = "digest")]
|
||||
#[test]
|
||||
fn sign_verify_prehashed_nonspec() {
|
||||
use curve25519_dalek::digest::Digest;
|
||||
|
||||
// Generate the keypair
|
||||
let mut rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(&mut rng);
|
||||
let vk = VerifyingKey::from(&esk);
|
||||
|
||||
// Hash the message
|
||||
let msg = b"And then I got trampled by a herd of buffalo";
|
||||
let mut h = MsgDigest::new();
|
||||
h.update(msg);
|
||||
|
||||
let ctx_str = &b"consequences"[..];
|
||||
|
||||
// Sign and verify prehashed
|
||||
let sig = raw_sign_prehashed::<CtxDigest, MsgDigest>(&esk, h.clone(), &vk, Some(ctx_str))
|
||||
.unwrap();
|
||||
raw_verify_prehashed::<CtxDigest, MsgDigest>(&vk, h, Some(ctx_str), &sig).unwrap();
|
||||
}
|
||||
}
|
||||
@ -1,293 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! A Rust implementation of ed25519 key generation, signing, and verification.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! Creating an ed25519 signature on a message is simple.
|
||||
//!
|
||||
//! First, we need to generate a `SigningKey`, which includes both public and
|
||||
//! secret halves of an asymmetric key. To do so, we need a cryptographically
|
||||
//! secure pseudorandom number generator (CSPRNG). For this example, we'll use
|
||||
//! the operating system's builtin PRNG:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use ed25519_dalek::SigningKey;
|
||||
//! use ed25519_dalek::Signature;
|
||||
//!
|
||||
//! let mut csprng = OsRng;
|
||||
//! let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! We can now use this `signing_key` to sign a message:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! use ed25519_dalek::{Signature, Signer};
|
||||
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! let signature: Signature = signing_key.sign(message);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! As well as to verify that this is, indeed, a valid signature on
|
||||
//! that `message`:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer};
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! use ed25519_dalek::Verifier;
|
||||
//! assert!(signing_key.verify(message, &signature).is_ok());
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Anyone else, given the `public` half of the `signing_key` can also easily
|
||||
//! verify this signature:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
//! # use ed25519_dalek::Signature;
|
||||
//! # use ed25519_dalek::Signer;
|
||||
//! use ed25519_dalek::{VerifyingKey, Verifier};
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//!
|
||||
//! let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! assert!(verifying_key.verify(message, &signature).is_ok());
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Serialisation
|
||||
//!
|
||||
//! `VerifyingKey`s, `SecretKey`s, `SigningKey`s, and `Signature`s can be serialised
|
||||
//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptable and
|
||||
//! safe to transfer and/or store those bytes. (Of course, never transfer your
|
||||
//! secret key to anyone else, since they will only need the public key to
|
||||
//! verify your signatures!)
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
|
||||
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//!
|
||||
//! let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key.verifying_key().to_bytes();
|
||||
//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key.to_bytes();
|
||||
//! let signing_key_bytes: [u8; KEYPAIR_LENGTH] = signing_key.to_keypair_bytes();
|
||||
//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! And similarly, decoded from bytes with `::from_bytes()`:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # use core::convert::{TryFrom, TryInto};
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError};
|
||||
//! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
|
||||
//! # fn do_test() -> Result<(SigningKey, VerifyingKey, Signature), SignatureError> {
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key_orig: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature_orig: Signature = signing_key_orig.sign(message);
|
||||
//! # let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key_orig.verifying_key().to_bytes();
|
||||
//! # let signing_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key_orig.to_bytes();
|
||||
//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes();
|
||||
//! #
|
||||
//! let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&verifying_key_bytes)?;
|
||||
//! let signing_key: SigningKey = SigningKey::from_bytes(&signing_key_bytes);
|
||||
//! let signature: Signature = Signature::try_from(&signature_bytes[..])?;
|
||||
//! #
|
||||
//! # Ok((signing_key, verifying_key, signature))
|
||||
//! # }
|
||||
//! # fn main() {
|
||||
//! # do_test();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### PKCS#8 Key Encoding
|
||||
//!
|
||||
//! PKCS#8 is a private key format with support for multiple algorithms.
|
||||
//! It can be encoded as binary (DER) or text (PEM).
|
||||
//!
|
||||
//! You can recognize PEM-encoded PKCS#8 keys by the following:
|
||||
//!
|
||||
//! ```text
|
||||
//! -----BEGIN PRIVATE KEY-----
|
||||
//! ```
|
||||
//!
|
||||
//! To use PKCS#8, you need to enable the `pkcs8` crate feature.
|
||||
//!
|
||||
//! The following traits can be used to decode/encode [`SigningKey`] and
|
||||
//! [`VerifyingKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the
|
||||
//! toplevel of the crate:
|
||||
//!
|
||||
//! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8
|
||||
//! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8
|
||||
//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8
|
||||
//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8
|
||||
//!
|
||||
//! #### Example
|
||||
//!
|
||||
//! NOTE: this requires the `pem` crate feature.
|
||||
//!
|
||||
#![cfg_attr(feature = "pem", doc = "```")]
|
||||
#![cfg_attr(not(feature = "pem"), doc = "```ignore")]
|
||||
//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodePublicKey};
|
||||
//!
|
||||
//! let pem = "-----BEGIN PUBLIC KEY-----
|
||||
//! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
|
||||
//! -----END PUBLIC KEY-----";
|
||||
//!
|
||||
//! let verifying_key = VerifyingKey::from_public_key_pem(pem)
|
||||
//! .expect("invalid public key PEM");
|
||||
//! ```
|
||||
//!
|
||||
//! ### Using Serde
|
||||
//!
|
||||
//! If you prefer the bytes to be wrapped in another serialisation format, all
|
||||
//! types additionally come with built-in [serde](https://serde.rs) support by
|
||||
//! building `ed25519-dalek` via:
|
||||
//!
|
||||
//! ```bash
|
||||
//! $ cargo build --features="serde"
|
||||
//! ```
|
||||
//!
|
||||
//! They can be then serialised into any of the wire formats which serde supports.
|
||||
//! For example, using [bincode](https://github.com/TyOverby/bincode):
|
||||
//!
|
||||
#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
|
||||
#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
|
||||
//! use bincode::serialize;
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
|
||||
//!
|
||||
//! let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
|
||||
//! let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! After sending the `encoded_verifying_key` and `encoded_signature`, the
|
||||
//! recipient may deserialise them and verify:
|
||||
//!
|
||||
#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
|
||||
#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
|
||||
//! # use bincode::serialize;
|
||||
//! use bincode::deserialize;
|
||||
//!
|
||||
//! # let mut csprng = OsRng;
|
||||
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
|
||||
//! # let signature: Signature = signing_key.sign(message);
|
||||
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
|
||||
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
|
||||
//! # let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
|
||||
//! # let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
|
||||
//! let decoded_verifying_key: VerifyingKey = deserialize(&encoded_verifying_key).unwrap();
|
||||
//! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap();
|
||||
//!
|
||||
//! # assert_eq!(verifying_key, decoded_verifying_key);
|
||||
//! # assert_eq!(signature, decoded_signature);
|
||||
//! #
|
||||
//! let verified: bool = decoded_verifying_key.verify(&message, &decoded_signature).is_ok();
|
||||
//!
|
||||
//! assert!(verified);
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![no_std]
|
||||
#![warn(future_incompatible, rust_2018_idioms)]
|
||||
#![deny(missing_docs)] // refuse to compile if documentation is missing
|
||||
#![deny(clippy::unwrap_used)] // don't allow unwrap
|
||||
#![cfg_attr(not(test), forbid(unsafe_code))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
|
||||
#[cfg(feature = "batch")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
pub use ed25519;
|
||||
|
||||
#[cfg(feature = "batch")]
|
||||
mod batch;
|
||||
mod constants;
|
||||
#[cfg(feature = "digest")]
|
||||
mod context;
|
||||
mod errors;
|
||||
mod signature;
|
||||
mod signing;
|
||||
mod verifying;
|
||||
|
||||
#[cfg(feature = "hazmat")]
|
||||
pub mod hazmat;
|
||||
#[cfg(not(feature = "hazmat"))]
|
||||
mod hazmat;
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
pub use curve25519_dalek::digest::Digest;
|
||||
#[cfg(feature = "digest")]
|
||||
pub use sha2::Sha512;
|
||||
|
||||
#[cfg(feature = "batch")]
|
||||
pub use crate::batch::*;
|
||||
pub use crate::constants::*;
|
||||
#[cfg(feature = "digest")]
|
||||
pub use crate::context::Context;
|
||||
pub use crate::errors::*;
|
||||
pub use crate::signing::*;
|
||||
pub use crate::verifying::*;
|
||||
|
||||
// Re-export the `Signer` and `Verifier` traits from the `signature` crate
|
||||
#[cfg(feature = "digest")]
|
||||
pub use ed25519::signature::{DigestSigner, DigestVerifier};
|
||||
pub use ed25519::signature::{Signer, Verifier};
|
||||
pub use ed25519::Signature;
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
pub use ed25519::pkcs8;
|
||||
@ -1,178 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! An ed25519 signature.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::errors::*;
|
||||
|
||||
/// An ed25519 signature.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// These signatures, unlike the ed25519 signature reference implementation, are
|
||||
/// "detached"—that is, they do **not** include a copy of the message which has
|
||||
/// been signed.
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Copy, Eq, PartialEq)]
|
||||
pub(crate) struct InternalSignature {
|
||||
/// `R` is an `EdwardsPoint`, formed by using an hash function with
|
||||
/// 512-bits output to produce the digest of:
|
||||
///
|
||||
/// - the nonce half of the `ExpandedSecretKey`, and
|
||||
/// - the message to be signed.
|
||||
///
|
||||
/// This digest is then interpreted as a `Scalar` and reduced into an
|
||||
/// element in ℤ/lℤ. The scalar is then multiplied by the distinguished
|
||||
/// basepoint to produce `R`, and `EdwardsPoint`.
|
||||
pub(crate) R: CompressedEdwardsY,
|
||||
|
||||
/// `s` is a `Scalar`, formed by using an hash function with 512-bits output
|
||||
/// to produce the digest of:
|
||||
///
|
||||
/// - the `r` portion of this `Signature`,
|
||||
/// - the `PublicKey` which should be used to verify this `Signature`, and
|
||||
/// - the message to be signed.
|
||||
///
|
||||
/// This digest is then interpreted as a `Scalar` and reduced into an
|
||||
/// element in ℤ/lℤ.
|
||||
pub(crate) s: Scalar,
|
||||
}
|
||||
|
||||
impl Clone for InternalSignature {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InternalSignature {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that the scalar `s` of a signature is within the bounds [0, 2^253).
|
||||
///
|
||||
/// **Unsafe**: This version of `check_scalar` permits signature malleability. See README.
|
||||
#[cfg(feature = "legacy_compatibility")]
|
||||
#[inline(always)]
|
||||
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
|
||||
// The highest 3 bits must not be set. No other checking for the
|
||||
// remaining 2^253 - 2^252 + 27742317777372353535851937790883648493
|
||||
// potential non-reduced scalars is performed.
|
||||
//
|
||||
// This is compatible with ed25519-donna and libsodium when
|
||||
// -DED25519_COMPAT is NOT specified.
|
||||
if bytes[31] & 224 != 0 {
|
||||
return Err(InternalError::ScalarFormat.into());
|
||||
}
|
||||
|
||||
// You cannot do arithmetic with scalars construct with Scalar::from_bits. We only use this
|
||||
// scalar for EdwardsPoint::vartime_double_scalar_mul_basepoint, which is an accepted usecase.
|
||||
// The `from_bits` method is deprecated because it's unsafe. We know this.
|
||||
#[allow(deprecated)]
|
||||
Ok(Scalar::from_bits(bytes))
|
||||
}
|
||||
|
||||
/// Ensures that the scalar `s` of a signature is within the bounds [0, ℓ)
|
||||
#[cfg(not(feature = "legacy_compatibility"))]
|
||||
#[inline(always)]
|
||||
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
|
||||
match Scalar::from_canonical_bytes(bytes).into() {
|
||||
None => Err(InternalError::ScalarFormat.into()),
|
||||
Some(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
impl InternalSignature {
|
||||
/// Construct a `Signature` from a slice of bytes.
|
||||
///
|
||||
/// # Scalar Malleability Checking
|
||||
///
|
||||
/// As originally specified in the ed25519 paper (cf. the "Malleability"
|
||||
/// section of the README in this repo), no checks whatsoever were performed
|
||||
/// for signature malleability.
|
||||
///
|
||||
/// Later, a semi-functional, hacky check was added to most libraries to
|
||||
/// "ensure" that the scalar portion, `s`, of the signature was reduced `mod
|
||||
/// \ell`, the order of the basepoint:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if signature.s[31] & 224 != 0 {
|
||||
/// return Err();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This bit-twiddling ensures that the most significant three bits of the
|
||||
/// scalar are not set:
|
||||
///
|
||||
/// ```python,ignore
|
||||
/// >>> 0b00010000 & 224
|
||||
/// 0
|
||||
/// >>> 0b00100000 & 224
|
||||
/// 32
|
||||
/// >>> 0b01000000 & 224
|
||||
/// 64
|
||||
/// >>> 0b10000000 & 224
|
||||
/// 128
|
||||
/// ```
|
||||
///
|
||||
/// However, this check is hacky and insufficient to check that the scalar is
|
||||
/// fully reduced `mod \ell = 2^252 + 27742317777372353535851937790883648493` as
|
||||
/// it leaves us with a guanteed bound of 253 bits. This means that there are
|
||||
/// `2^253 - 2^252 + 2774231777737235353585193779088364849311` remaining scalars
|
||||
/// which could cause malleabilllity.
|
||||
///
|
||||
/// RFC8032 [states](https://tools.ietf.org/html/rfc8032#section-5.1.7):
|
||||
///
|
||||
/// > To verify a signature on a message M using public key A, [...]
|
||||
/// > first split the signature into two 32-octet halves. Decode the first
|
||||
/// > half as a point R, and the second half as an integer S, in the range
|
||||
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
|
||||
/// > decodings fail (including S being out of range), the signature is
|
||||
/// > invalid.
|
||||
///
|
||||
/// However, by the time this was standardised, most libraries in use were
|
||||
/// only checking the most significant three bits. (See also the
|
||||
/// documentation for [`crate::VerifyingKey::verify_strict`].)
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> {
|
||||
// TODO: Use bytes.split_array_ref once it’s in MSRV.
|
||||
let mut R_bytes: [u8; 32] = [0u8; 32];
|
||||
let mut s_bytes: [u8; 32] = [0u8; 32];
|
||||
R_bytes.copy_from_slice(&bytes[00..32]);
|
||||
s_bytes.copy_from_slice(&bytes[32..64]);
|
||||
|
||||
Ok(InternalSignature {
|
||||
R: CompressedEdwardsY(R_bytes),
|
||||
s: check_scalar(s_bytes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ed25519::Signature> for InternalSignature {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(sig: &ed25519::Signature) -> Result<InternalSignature, SignatureError> {
|
||||
InternalSignature::from_bytes(&sig.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InternalSignature> for ed25519::Signature {
|
||||
fn from(sig: InternalSignature) -> ed25519::Signature {
|
||||
ed25519::Signature::from_components(*sig.R.as_bytes(), *sig.s.as_bytes())
|
||||
}
|
||||
}
|
||||
@ -1,845 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! ed25519 signing keys.
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8;
|
||||
|
||||
#[cfg(any(test, feature = "rand_core"))]
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use curve25519_dalek::{
|
||||
digest::{generic_array::typenum::U64, Digest},
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
scalar::Scalar,
|
||||
};
|
||||
|
||||
use ed25519::signature::{KeypairRef, Signer, Verifier};
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
use crate::context::Context;
|
||||
#[cfg(feature = "digest")]
|
||||
use signature::DigestSigner;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
use crate::{
|
||||
constants::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH},
|
||||
errors::{InternalError, SignatureError},
|
||||
hazmat::ExpandedSecretKey,
|
||||
signature::InternalSignature,
|
||||
verifying::VerifyingKey,
|
||||
Signature,
|
||||
};
|
||||
|
||||
/// ed25519 secret key as defined in [RFC8032 § 5.1.5]:
|
||||
///
|
||||
/// > The private key is 32 octets (256 bits, corresponding to b) of
|
||||
/// > cryptographically secure random data.
|
||||
///
|
||||
/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5
|
||||
pub type SecretKey = [u8; SECRET_KEY_LENGTH];
|
||||
|
||||
/// ed25519 signing key which can be used to produce signatures.
|
||||
// Invariant: `public` is always the public key of `secret`. This prevents the signing function
|
||||
// oracle attack described in https://github.com/MystenLabs/ed25519-unsafe-libs
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SigningKey {
|
||||
/// The secret half of this signing key.
|
||||
pub(crate) secret_key: SecretKey,
|
||||
/// The public half of this signing key.
|
||||
pub(crate) verifying_key: VerifyingKey,
|
||||
}
|
||||
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate ed25519_dalek;
|
||||
/// #
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::SECRET_KEY_LENGTH;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
///
|
||||
/// # fn doctest() -> Result<SigningKey, SignatureError> {
|
||||
/// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [
|
||||
/// 157, 097, 177, 157, 239, 253, 090, 096,
|
||||
/// 186, 132, 074, 244, 146, 236, 044, 196,
|
||||
/// 068, 073, 197, 105, 123, 050, 105, 025,
|
||||
/// 112, 059, 172, 003, 028, 174, 127, 096, ];
|
||||
///
|
||||
/// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes);
|
||||
/// assert_eq!(signing_key.to_bytes(), secret_key_bytes);
|
||||
///
|
||||
/// # Ok(signing_key)
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let result = doctest();
|
||||
/// # assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
impl SigningKey {
|
||||
/// Construct a [`SigningKey`] from a [`SecretKey`]
|
||||
///
|
||||
#[inline]
|
||||
pub fn from_bytes(secret_key: &SecretKey) -> Self {
|
||||
let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key));
|
||||
Self {
|
||||
secret_key: *secret_key,
|
||||
verifying_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this [`SigningKey`] into a [`SecretKey`]
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> SecretKey {
|
||||
self.secret_key
|
||||
}
|
||||
|
||||
/// Construct a [`SigningKey`] from the bytes of a `VerifyingKey` and `SecretKey`.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the
|
||||
/// scalar for the secret key, and a compressed Edwards-Y coordinate of a
|
||||
/// point on curve25519, both as bytes. (As obtained from
|
||||
/// [`SigningKey::to_bytes`].)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA [`SigningKey`] or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
#[inline]
|
||||
pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result<SigningKey, SignatureError> {
|
||||
let (secret_key, verifying_key) = bytes.split_at(SECRET_KEY_LENGTH);
|
||||
let signing_key = SigningKey::try_from(secret_key)?;
|
||||
let verifying_key = VerifyingKey::try_from(verifying_key)?;
|
||||
|
||||
if signing_key.verifying_key() != verifying_key {
|
||||
return Err(InternalError::MismatchedKeypair.into());
|
||||
}
|
||||
|
||||
Ok(signing_key)
|
||||
}
|
||||
|
||||
/// Convert this signing key to a 64-byte keypair.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first
|
||||
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
|
||||
/// `PUBLIC_KEY_LENGTH` bytes is the `VerifyingKey` (the same as other
|
||||
/// libraries, such as [Adam Langley's ed25519 Golang
|
||||
/// implementation](https://github.com/agl/ed25519/)). It is guaranteed that
|
||||
/// the encoded public key is the one derived from the encoded secret key.
|
||||
pub fn to_keypair_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
|
||||
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
|
||||
|
||||
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret_key);
|
||||
bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.verifying_key.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Get the [`VerifyingKey`] for this [`SigningKey`].
|
||||
pub fn verifying_key(&self) -> VerifyingKey {
|
||||
self.verifying_key
|
||||
}
|
||||
|
||||
/// Create a signing context that can be used for Ed25519ph with
|
||||
/// [`DigestSigner`].
|
||||
#[cfg(feature = "digest")]
|
||||
pub fn with_context<'k, 'v>(
|
||||
&'k self,
|
||||
context_value: &'v [u8],
|
||||
) -> Result<Context<'k, 'v, Self>, SignatureError> {
|
||||
Context::new(self, context_value)
|
||||
}
|
||||
|
||||
/// Generate an ed25519 signing key.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
#[cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
/// # fn main() {
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::{Signature, SigningKey};
|
||||
///
|
||||
/// let mut csprng = OsRng;
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Input
|
||||
///
|
||||
/// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`.
|
||||
///
|
||||
/// The caller must also supply a hash function which implements the
|
||||
/// `Digest` and `Default` traits, and which returns 512 bits of output.
|
||||
/// The standard hash function used for most ed25519 libraries is SHA-512,
|
||||
/// which is available with `use sha2::Sha512` as in the example above.
|
||||
/// Other suitable hash functions include Keccak-512 and Blake2b-512.
|
||||
#[cfg(any(test, feature = "rand_core"))]
|
||||
pub fn generate<R: CryptoRngCore + ?Sized>(csprng: &mut R) -> SigningKey {
|
||||
let mut secret = SecretKey::default();
|
||||
csprng.fill_bytes(&mut secret);
|
||||
Self::from_bytes(&secret)
|
||||
}
|
||||
|
||||
/// Sign a `prehashed_message` with this [`SigningKey`] using the
|
||||
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
|
||||
/// function technically works, and is probably safe to use, with any secure hash function with
|
||||
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
|
||||
/// [`crate::Sha512`] for user convenience.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
|
||||
#[cfg_attr(
|
||||
any(not(feature = "rand_core"), not(feature = "digest")),
|
||||
doc = "```ignore"
|
||||
)]
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use sha2::Sha512;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut csprng = OsRng;
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// // Create a hash digest object which we'll feed the message into:
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
///
|
||||
/// prehashed.update(message);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// If you want, you can optionally pass a "context". It is generally a
|
||||
/// good idea to choose a context and try to make it unique to your project
|
||||
/// and this specific usage of signatures.
|
||||
///
|
||||
/// For example, without this, if you were to [convert your OpenPGP key
|
||||
/// to a Bitcoin key][terrible_idea] (just as an example, and also Don't
|
||||
/// Ever Do That) and someone tricked you into signing an "email" which was
|
||||
/// actually a Bitcoin transaction moving all your magic internet money to
|
||||
/// their address, it'd be a valid transaction.
|
||||
///
|
||||
/// By adding a context, this trick becomes impossible, because the context
|
||||
/// is concatenated into the hash, which is then signed. So, going with the
|
||||
/// previous example, if your bitcoin wallet used a context of
|
||||
/// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely
|
||||
/// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol",
|
||||
/// then the signatures produced by both could never match the other, even
|
||||
/// if they signed the exact same message with the same key.
|
||||
///
|
||||
/// Let's add a context for good measure (remember, you'll want to choose
|
||||
/// your own!):
|
||||
///
|
||||
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
|
||||
#[cfg_attr(
|
||||
any(not(feature = "rand_core"), not(feature = "digest")),
|
||||
doc = "```ignore"
|
||||
)]
|
||||
/// # use ed25519_dalek::Digest;
|
||||
/// # use ed25519_dalek::SigningKey;
|
||||
/// # use ed25519_dalek::Signature;
|
||||
/// # use ed25519_dalek::SignatureError;
|
||||
/// # use sha2::Sha512;
|
||||
/// # use rand::rngs::OsRng;
|
||||
/// #
|
||||
/// # fn do_test() -> Result<Signature, SignatureError> {
|
||||
/// # let mut csprng = OsRng;
|
||||
/// # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
/// # let mut prehashed: Sha512 = Sha512::new();
|
||||
/// # prehashed.update(message);
|
||||
/// #
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
|
||||
/// #
|
||||
/// # Ok(sig)
|
||||
/// # }
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
/// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py
|
||||
#[cfg(feature = "digest")]
|
||||
pub fn sign_prehashed<MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
ExpandedSecretKey::from(&self.secret_key).raw_sign_prehashed::<Sha512, MsgDigest>(
|
||||
prehashed_message,
|
||||
&self.verifying_key,
|
||||
context,
|
||||
)
|
||||
}
|
||||
|
||||
/// Verify a signature on a message with this signing key's public key.
|
||||
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify(message, signature)
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
/// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the `signature` was a valid signature created by this
|
||||
/// [`SigningKey`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
|
||||
/// function technically works, and is probably safe to use, with any secure hash function with
|
||||
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
|
||||
/// [`crate::Sha512`] for user convenience.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
|
||||
#[cfg_attr(
|
||||
any(not(feature = "rand_core"), not(feature = "digest")),
|
||||
doc = "```ignore"
|
||||
)]
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
/// use sha2::Sha512;
|
||||
/// use rand::rngs::OsRng;
|
||||
///
|
||||
/// # fn do_test() -> Result<(), SignatureError> {
|
||||
/// let mut csprng = OsRng;
|
||||
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
|
||||
///
|
||||
/// let mut prehashed: Sha512 = Sha512::new();
|
||||
/// prehashed.update(message);
|
||||
///
|
||||
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
|
||||
///
|
||||
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
|
||||
///
|
||||
/// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one:
|
||||
/// let mut prehashed_again: Sha512 = Sha512::default();
|
||||
/// prehashed_again.update(message);
|
||||
///
|
||||
/// let verified = signing_key.verifying_key().verify_prehashed(prehashed_again, Some(context), &sig);
|
||||
///
|
||||
/// assert!(verified.is_ok());
|
||||
///
|
||||
/// # verified
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # do_test();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
|
||||
#[cfg(feature = "digest")]
|
||||
pub fn verify_prehashed<MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
signature: &Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
self.verifying_key
|
||||
.verify_prehashed(prehashed_message, context, signature)
|
||||
}
|
||||
|
||||
/// Strictly verify a signature on a message with this signing key's public key.
|
||||
///
|
||||
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
|
||||
///
|
||||
/// This version of verification is technically non-RFC8032 compliant. The
|
||||
/// following explains why.
|
||||
///
|
||||
/// 1. Scalar Malleability
|
||||
///
|
||||
/// The authors of the RFC explicitly stated that verification of an ed25519
|
||||
/// signature must fail if the scalar `s` is not properly reduced mod \ell:
|
||||
///
|
||||
/// > To verify a signature on a message M using public key A, with F
|
||||
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
|
||||
/// > Ed25519ph is being used, C being the context, first split the
|
||||
/// > signature into two 32-octet halves. Decode the first half as a
|
||||
/// > point R, and the second half as an integer S, in the range
|
||||
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
|
||||
/// > decodings fail (including S being out of range), the signature is
|
||||
/// > invalid.)
|
||||
///
|
||||
/// All `verify_*()` functions within ed25519-dalek perform this check.
|
||||
///
|
||||
/// 2. Point malleability
|
||||
///
|
||||
/// The authors of the RFC added in a malleability check to step #3 in
|
||||
/// §5.1.7, for small torsion components in the `R` value of the signature,
|
||||
/// *which is not strictly required*, as they state:
|
||||
///
|
||||
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
|
||||
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
|
||||
///
|
||||
/// # History of Malleability Checks
|
||||
///
|
||||
/// As originally defined (cf. the "Malleability" section in the README of
|
||||
/// this repo), ed25519 signatures didn't consider *any* form of
|
||||
/// malleability to be an issue. Later the scalar malleability was
|
||||
/// considered important. Still later, particularly with interests in
|
||||
/// cryptocurrency design and in unique identities (e.g. for Signal users,
|
||||
/// Tor onion services, etc.), the group element malleability became a
|
||||
/// concern.
|
||||
///
|
||||
/// However, libraries had already been created to conform to the original
|
||||
/// definition. One well-used library in particular even implemented the
|
||||
/// group element malleability check, *but only for batch verification*!
|
||||
/// Which meant that even using the same library, a single signature could
|
||||
/// verify fine individually, but suddenly, when verifying it with a bunch
|
||||
/// of other signatures, the whole batch would fail!
|
||||
///
|
||||
/// # "Strict" Verification
|
||||
///
|
||||
/// This method performs *both* of the above signature malleability checks.
|
||||
///
|
||||
/// It must be done as a separate method because one doesn't simply get to
|
||||
/// change the definition of a cryptographic primitive ten years
|
||||
/// after-the-fact with zero consideration for backwards compatibility in
|
||||
/// hardware and protocols which have it already have the older definition
|
||||
/// baked in.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_strict(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify_strict(message, signature)
|
||||
}
|
||||
|
||||
/// Convert this signing key into a byte representation of a(n) (unreduced) Curve25519 scalar.
|
||||
///
|
||||
/// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The bytes output
|
||||
/// by this function are a valid secret key for the X25519 public key given by
|
||||
/// `self.verifying_key().to_montgomery()`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually
|
||||
/// long-term keys, while keys used for key exchange should rather be ephemeral. If you can
|
||||
/// help it, use a separate key for encryption.
|
||||
///
|
||||
/// For more information on the security of systems which use the same keys for both signing
|
||||
/// and Diffie-Hellman, see the paper
|
||||
/// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509).
|
||||
pub fn to_scalar(&self) -> Scalar {
|
||||
ExpandedSecretKey::from(&self.secret_key).scalar
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<VerifyingKey> for SigningKey {
|
||||
fn as_ref(&self) -> &VerifyingKey {
|
||||
&self.verifying_key
|
||||
}
|
||||
}
|
||||
|
||||
impl KeypairRef for SigningKey {
|
||||
type VerifyingKey = VerifyingKey;
|
||||
}
|
||||
|
||||
impl Signer<Signature> for SigningKey {
|
||||
/// Sign a message with this signing key's secret key.
|
||||
fn try_sign(&self, message: &[u8]) -> Result<Signature, SignatureError> {
|
||||
let expanded: ExpandedSecretKey = (&self.secret_key).into();
|
||||
Ok(expanded.raw_sign::<Sha512>(message, &self.verifying_key))
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
|
||||
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
|
||||
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
|
||||
#[cfg(feature = "digest")]
|
||||
impl<D> DigestSigner<D, Signature> for SigningKey
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
|
||||
self.sign_prehashed(msg_digest, None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`Some`]
|
||||
/// containing `self.value()`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
|
||||
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
|
||||
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
|
||||
#[cfg(feature = "digest")]
|
||||
impl<D> DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
|
||||
where
|
||||
D: Digest<OutputSize = U64>,
|
||||
{
|
||||
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
|
||||
self.key().sign_prehashed(msg_digest, Some(self.value()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<Signature> for SigningKey {
|
||||
/// Verify a signature on a message with this signing key's public key.
|
||||
fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
||||
self.verifying_key.verify(message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretKey> for SigningKey {
|
||||
#[inline]
|
||||
fn from(secret: SecretKey) -> Self {
|
||||
Self::from_bytes(&secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SecretKey> for SigningKey {
|
||||
#[inline]
|
||||
fn from(secret: &SecretKey) -> Self {
|
||||
Self::from_bytes(secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for SigningKey {
|
||||
type Error = SignatureError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<SigningKey, SignatureError> {
|
||||
SecretKey::try_from(bytes)
|
||||
.map(|bytes| Self::from_bytes(&bytes))
|
||||
.map_err(|_| {
|
||||
InternalError::BytesLength {
|
||||
name: "SecretKey",
|
||||
length: SECRET_KEY_LENGTH,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Drop for SigningKey {
|
||||
fn drop(&mut self) {
|
||||
self.secret_key.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl ZeroizeOnDrop for SigningKey {}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePrivateKey for SigningKey {
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
|
||||
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
SigningKey::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&pkcs8::KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
|
||||
let signing_key = SigningKey::from_bytes(&pkcs8_key.secret_key);
|
||||
|
||||
// Validate the public key in the PKCS#8 document if present
|
||||
if let Some(public_bytes) = &pkcs8_key.public_key {
|
||||
let expected_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref())
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?;
|
||||
|
||||
if signing_key.verifying_key() != expected_verifying_key {
|
||||
return Err(pkcs8::Error::KeyMalformed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(signing_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<SigningKey> for pkcs8::KeypairBytes {
|
||||
fn from(signing_key: SigningKey) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes::from(&signing_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<&SigningKey> for pkcs8::KeypairBytes {
|
||||
fn from(signing_key: &SigningKey) -> pkcs8::KeypairBytes {
|
||||
pkcs8::KeypairBytes {
|
||||
secret_key: signing_key.to_bytes(),
|
||||
public_key: Some(pkcs8::PublicKeyBytes(signing_key.verifying_key.to_bytes())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
|
||||
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SigningKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_bytes(&self.secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'d> Deserialize<'d> for SigningKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
struct SigningKeyVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for SigningKeyVisitor {
|
||||
type Value = SigningKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
write!(formatter, concat!("An ed25519 signing (private) key"))
|
||||
}
|
||||
|
||||
fn visit_borrowed_bytes<E: serde::de::Error>(
|
||||
self,
|
||||
bytes: &'de [u8],
|
||||
) -> Result<Self::Value, E> {
|
||||
SigningKey::try_from(bytes.as_ref()).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
|
||||
let remaining = (0..)
|
||||
.map(|_| seq.next_element::<u8>())
|
||||
.take_while(|el| matches!(el, Ok(Some(_))))
|
||||
.count();
|
||||
|
||||
if remaining > 0 {
|
||||
return Err(serde::de::Error::invalid_length(
|
||||
32 + remaining,
|
||||
&"expected 32 bytes",
|
||||
));
|
||||
}
|
||||
|
||||
SigningKey::try_from(bytes).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(SigningKeyVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// The spec-compliant way to define an expanded secret key. This computes `SHA512(sk)`, clamps the
|
||||
/// first 32 bytes and uses it as a scalar, and uses the second 32 bytes as a domain separator for
|
||||
/// hashing.
|
||||
impl From<&SecretKey> for ExpandedSecretKey {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
|
||||
let hash = Sha512::default().chain_update(secret_key).finalize();
|
||||
ExpandedSecretKey::from_bytes(hash.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Signing functions. These are pub(crate) so that the `hazmat` module can use them
|
||||
//
|
||||
|
||||
impl ExpandedSecretKey {
|
||||
/// The plain, non-prehashed, signing function for Ed25519. `CtxDigest` is the digest used to
|
||||
/// calculate the pseudorandomness needed for signing. According to the spec, `CtxDigest =
|
||||
/// Sha512`, and `self` is derived via the method defined in `impl From<&SigningKey> for
|
||||
/// ExpandedSecretKey`.
|
||||
///
|
||||
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
|
||||
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn raw_sign<CtxDigest>(
|
||||
&self,
|
||||
message: &[u8],
|
||||
verifying_key: &VerifyingKey,
|
||||
) -> Signature
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let mut h = CtxDigest::new();
|
||||
|
||||
h.update(self.hash_prefix);
|
||||
h.update(message);
|
||||
|
||||
let r = Scalar::from_hash(h);
|
||||
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
|
||||
|
||||
h = CtxDigest::new();
|
||||
h.update(R.as_bytes());
|
||||
h.update(verifying_key.as_bytes());
|
||||
h.update(message);
|
||||
|
||||
let k = Scalar::from_hash(h);
|
||||
let s: Scalar = (k * self.scalar) + r;
|
||||
|
||||
InternalSignature { R, s }.into()
|
||||
}
|
||||
|
||||
/// The prehashed signing function for Ed25519 (i.e., Ed25519ph). `CtxDigest` is the digest
|
||||
/// function used to calculate the pseudorandomness needed for signing. `MsgDigest` is the
|
||||
/// digest function used to hash the signed message. According to the spec, `MsgDigest =
|
||||
/// CtxDigest = Sha512`, and `self` is derived via the method defined in `impl
|
||||
/// From<&SigningKey> for ExpandedSecretKey`.
|
||||
///
|
||||
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
|
||||
/// change how the `ExpandedSecretKey` is calculated and which `CtxDigest` function to use.
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn raw_sign_prehashed<'a, CtxDigest, MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
verifying_key: &VerifyingKey,
|
||||
context: Option<&'a [u8]>,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let mut prehash: [u8; 64] = [0u8; 64];
|
||||
|
||||
let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string.
|
||||
|
||||
if ctx.len() > 255 {
|
||||
return Err(SignatureError::from(InternalError::PrehashedContextLength));
|
||||
}
|
||||
|
||||
let ctx_len: u8 = ctx.len() as u8;
|
||||
|
||||
// Get the result of the pre-hashed message.
|
||||
prehash.copy_from_slice(prehashed_message.finalize().as_slice());
|
||||
|
||||
// This is the dumbest, ten-years-late, non-admission of fucking up the
|
||||
// domain separation I have ever seen. Why am I still required to put
|
||||
// the upper half "prefix" of the hashed "secret key" in here? Why
|
||||
// can't the user just supply their own nonce and decide for themselves
|
||||
// whether or not they want a deterministic signature scheme? Why does
|
||||
// the message go into what's ostensibly the signature domain separation
|
||||
// hash? Why wasn't there always a way to provide a context string?
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// This is a really fucking stupid bandaid, and the damned scheme is
|
||||
// still bleeding from malleability, for fuck's sake.
|
||||
let mut h = CtxDigest::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update([1]) // Ed25519ph
|
||||
.chain_update([ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(self.hash_prefix)
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
let r = Scalar::from_hash(h);
|
||||
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
|
||||
|
||||
h = CtxDigest::new()
|
||||
.chain_update(b"SigEd25519 no Ed25519 collisions")
|
||||
.chain_update([1]) // Ed25519ph
|
||||
.chain_update([ctx_len])
|
||||
.chain_update(ctx)
|
||||
.chain_update(R.as_bytes())
|
||||
.chain_update(verifying_key.as_bytes())
|
||||
.chain_update(&prehash[..]);
|
||||
|
||||
let k = Scalar::from_hash(h);
|
||||
let s: Scalar = (k * self.scalar) + r;
|
||||
|
||||
Ok(InternalSignature { R, s }.into())
|
||||
}
|
||||
}
|
||||
@ -1,676 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! ed25519 public keys.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
use curve25519_dalek::{
|
||||
digest::{generic_array::typenum::U64, Digest},
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
montgomery::MontgomeryPoint,
|
||||
scalar::Scalar,
|
||||
};
|
||||
|
||||
use ed25519::signature::Verifier;
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
use crate::context::Context;
|
||||
#[cfg(feature = "digest")]
|
||||
use signature::DigestVerifier;
|
||||
|
||||
use crate::{
|
||||
constants::PUBLIC_KEY_LENGTH,
|
||||
errors::{InternalError, SignatureError},
|
||||
hazmat::ExpandedSecretKey,
|
||||
signature::InternalSignature,
|
||||
signing::SigningKey,
|
||||
};
|
||||
|
||||
/// An ed25519 public key.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The `Eq` and `Hash` impls here use the compressed Edwards y encoding, _not_ the algebraic
|
||||
/// representation. This means if this `VerifyingKey` is non-canonically encoded, it will be
|
||||
/// considered unequal to the other equivalent encoding, despite the two representing the same
|
||||
/// point. More encoding details can be found
|
||||
/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am).
|
||||
/// If you want to make sure that signatures produced with respect to those sorts of public keys
|
||||
/// are rejected, use [`VerifyingKey::verify_strict`].
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
#[derive(Copy, Clone, Default, Eq)]
|
||||
pub struct VerifyingKey {
|
||||
/// Serialized compressed Edwards-y point.
|
||||
pub(crate) compressed: CompressedEdwardsY,
|
||||
|
||||
/// Decompressed Edwards point used for curve arithmetic operations.
|
||||
pub(crate) point: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl Debug for VerifyingKey {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for VerifyingKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for VerifyingKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<VerifyingKey> for VerifyingKey {
|
||||
fn eq(&self, other: &VerifyingKey) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ExpandedSecretKey> for VerifyingKey {
|
||||
/// Derive this public key from its corresponding `ExpandedSecretKey`.
|
||||
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
|
||||
VerifyingKey::from(EdwardsPoint::mul_base(&expanded_secret_key.scalar))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SigningKey> for VerifyingKey {
|
||||
fn from(signing_key: &SigningKey) -> VerifyingKey {
|
||||
signing_key.verifying_key()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EdwardsPoint> for VerifyingKey {
|
||||
fn from(point: EdwardsPoint) -> VerifyingKey {
|
||||
VerifyingKey {
|
||||
point,
|
||||
compressed: point.compress(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VerifyingKey {
|
||||
/// Convert this public key to a byte array.
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||
self.compressed.to_bytes()
|
||||
}
|
||||
|
||||
/// View this public key as a byte array.
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
|
||||
&(self.compressed).0
|
||||
}
|
||||
|
||||
/// Construct a `VerifyingKey` from a slice of bytes.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// The caller is responsible for ensuring that the bytes passed into this
|
||||
/// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY`
|
||||
/// and that said compressed point is actually a point on the curve.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::VerifyingKey;
|
||||
/// use ed25519_dalek::PUBLIC_KEY_LENGTH;
|
||||
/// use ed25519_dalek::SignatureError;
|
||||
///
|
||||
/// # fn doctest() -> Result<VerifyingKey, SignatureError> {
|
||||
/// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [
|
||||
/// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58,
|
||||
/// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26];
|
||||
///
|
||||
/// let public_key = VerifyingKey::from_bytes(&public_key_bytes)?;
|
||||
/// #
|
||||
/// # Ok(public_key)
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # doctest();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value
|
||||
/// is a `SignatureError` describing the error that occurred.
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result<VerifyingKey, SignatureError> {
|
||||
let compressed = CompressedEdwardsY(*bytes);
|
||||
let point = compressed
|
||||
.decompress()
|
||||
.ok_or(InternalError::PointDecompression)?;
|
||||
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
Ok(VerifyingKey { compressed, point })
|
||||
}
|
||||
|
||||
/// Create a verifying context that can be used for Ed25519ph with
|
||||
/// [`DigestVerifier`].
|
||||
#[cfg(feature = "digest")]
|
||||
pub fn with_context<'k, 'v>(
|
||||
&'k self,
|
||||
context_value: &'v [u8],
|
||||
) -> Result<Context<'k, 'v, Self>, SignatureError> {
|
||||
Context::new(self, context_value)
|
||||
}
|
||||
|
||||
/// Returns whether this is a _weak_ public key, i.e., if this public key has low order.
|
||||
///
|
||||
/// A weak public key can be used to generate a signature that's valid for almost every
|
||||
/// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this
|
||||
/// property before verification, then use this method.
|
||||
pub fn is_weak(&self) -> bool {
|
||||
self.point.is_small_order()
|
||||
}
|
||||
|
||||
// A helper function that computes `H(R || A || M)` where `H` is the 512-bit hash function
|
||||
// given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519). If `context.is_some()`,
|
||||
// this does the prehashed variant of the computation using its contents.
|
||||
#[allow(non_snake_case)]
|
||||
fn compute_challenge<CtxDigest>(
|
||||
context: Option<&[u8]>,
|
||||
R: &CompressedEdwardsY,
|
||||
A: &CompressedEdwardsY,
|
||||
M: &[u8],
|
||||
) -> Scalar
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let mut h = CtxDigest::new();
|
||||
if let Some(c) = context {
|
||||
h.update(b"SigEd25519 no Ed25519 collisions");
|
||||
h.update([1]); // Ed25519ph
|
||||
h.update([c.len() as u8]);
|
||||
h.update(c);
|
||||
}
|
||||
h.update(R.as_bytes());
|
||||
h.update(A.as_bytes());
|
||||
h.update(M);
|
||||
|
||||
Scalar::from_hash(h)
|
||||
}
|
||||
|
||||
// Helper function for verification. Computes the _expected_ R component of the signature. The
|
||||
// caller compares this to the real R component. If `context.is_some()`, this does the
|
||||
// prehashed variant of the computation using its contents.
|
||||
// Note that this returns the compressed form of R and the caller does a byte comparison. This
|
||||
// means that all our verification functions do not accept non-canonically encoded R values.
|
||||
// See the validation criteria blog post for more details:
|
||||
// https://hdevalence.ca/blog/2020-10-04-its-25519am
|
||||
#[allow(non_snake_case)]
|
||||
fn recompute_R<CtxDigest>(
|
||||
&self,
|
||||
context: Option<&[u8]>,
|
||||
signature: &InternalSignature,
|
||||
M: &[u8],
|
||||
) -> CompressedEdwardsY
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let k = Self::compute_challenge::<CtxDigest>(context, &signature.R, &self.compressed, M);
|
||||
let minus_A: EdwardsPoint = -self.point;
|
||||
// Recall the (non-batched) verification equation: -[k]A + [s]B = R
|
||||
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress()
|
||||
}
|
||||
|
||||
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R values. (see
|
||||
/// [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the pseudorandomness
|
||||
/// needed for signing. According to the spec, `CtxDigest = Sha512`.
|
||||
///
|
||||
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
|
||||
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn raw_verify<CtxDigest>(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let signature = InternalSignature::try_from(signature)?;
|
||||
|
||||
let expected_R = self.recompute_R::<CtxDigest>(None, &signature, message);
|
||||
if expected_R == signature.R {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InternalError::Verify.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// The prehashed non-batched Ed25519 verification check, rejecting non-canonical R values.
|
||||
/// (see [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the
|
||||
/// pseudorandomness needed for signing. `MsgDigest` is the digest used to hash the signed
|
||||
/// message. According to the spec, `MsgDigest = CtxDigest = Sha512`.
|
||||
///
|
||||
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
|
||||
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn raw_verify_prehashed<CtxDigest, MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let signature = InternalSignature::try_from(signature)?;
|
||||
|
||||
let ctx: &[u8] = context.unwrap_or(b"");
|
||||
debug_assert!(
|
||||
ctx.len() <= 255,
|
||||
"The context must not be longer than 255 octets."
|
||||
);
|
||||
|
||||
let message = prehashed_message.finalize();
|
||||
let expected_R = self.recompute_R::<CtxDigest>(Some(ctx), &signature, &message);
|
||||
|
||||
if expected_R == signature.R {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InternalError::Verify.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
/// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the `signature` was a valid signature created by this
|
||||
/// [`SigningKey`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
|
||||
/// function technically works, and is probably safe to use, with any secure hash function with
|
||||
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
|
||||
/// [`crate::Sha512`] for user convenience.
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_prehashed<MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
self.raw_verify_prehashed::<Sha512, MsgDigest>(prehashed_message, context, signature)
|
||||
}
|
||||
|
||||
/// Strictly verify a signature on a message with this keypair's public key.
|
||||
///
|
||||
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
|
||||
///
|
||||
/// This version of verification is technically non-RFC8032 compliant. The
|
||||
/// following explains why.
|
||||
///
|
||||
/// 1. Scalar Malleability
|
||||
///
|
||||
/// The authors of the RFC explicitly stated that verification of an ed25519
|
||||
/// signature must fail if the scalar `s` is not properly reduced mod $\ell$:
|
||||
///
|
||||
/// > To verify a signature on a message M using public key A, with F
|
||||
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
|
||||
/// > Ed25519ph is being used, C being the context, first split the
|
||||
/// > signature into two 32-octet halves. Decode the first half as a
|
||||
/// > point R, and the second half as an integer S, in the range
|
||||
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
|
||||
/// > decodings fail (including S being out of range), the signature is
|
||||
/// > invalid.)
|
||||
///
|
||||
/// All `verify_*()` functions within ed25519-dalek perform this check.
|
||||
///
|
||||
/// 2. Point malleability
|
||||
///
|
||||
/// The authors of the RFC added in a malleability check to step #3 in
|
||||
/// §5.1.7, for small torsion components in the `R` value of the signature,
|
||||
/// *which is not strictly required*, as they state:
|
||||
///
|
||||
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
|
||||
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
|
||||
///
|
||||
/// # History of Malleability Checks
|
||||
///
|
||||
/// As originally defined (cf. the "Malleability" section in the README of
|
||||
/// this repo), ed25519 signatures didn't consider *any* form of
|
||||
/// malleability to be an issue. Later the scalar malleability was
|
||||
/// considered important. Still later, particularly with interests in
|
||||
/// cryptocurrency design and in unique identities (e.g. for Signal users,
|
||||
/// Tor onion services, etc.), the group element malleability became a
|
||||
/// concern.
|
||||
///
|
||||
/// However, libraries had already been created to conform to the original
|
||||
/// definition. One well-used library in particular even implemented the
|
||||
/// group element malleability check, *but only for batch verification*!
|
||||
/// Which meant that even using the same library, a single signature could
|
||||
/// verify fine individually, but suddenly, when verifying it with a bunch
|
||||
/// of other signatures, the whole batch would fail!
|
||||
///
|
||||
/// # "Strict" Verification
|
||||
///
|
||||
/// This method performs *both* of the above signature malleability checks.
|
||||
///
|
||||
/// It must be done as a separate method because one doesn't simply get to
|
||||
/// change the definition of a cryptographic primitive ten years
|
||||
/// after-the-fact with zero consideration for backwards compatibility in
|
||||
/// hardware and protocols which have it already have the older definition
|
||||
/// baked in.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_strict(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
let signature = InternalSignature::try_from(signature)?;
|
||||
|
||||
let signature_R = signature
|
||||
.R
|
||||
.decompress()
|
||||
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
|
||||
|
||||
// Logical OR is fine here as we're not trying to be constant time.
|
||||
if signature_R.is_small_order() || self.point.is_small_order() {
|
||||
return Err(InternalError::Verify.into());
|
||||
}
|
||||
|
||||
let expected_R = self.recompute_R::<Sha512>(None, &signature, message);
|
||||
if expected_R == signature.R {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InternalError::Verify.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm,
|
||||
/// using strict signture checking as defined by [`Self::verify_strict`].
|
||||
///
|
||||
/// # Inputs
|
||||
///
|
||||
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
|
||||
/// output which has had the message to be signed previously fed into its
|
||||
/// state.
|
||||
/// * `context` is an optional context string, up to 255 bytes inclusive,
|
||||
/// which may be used to provide additional domain separation. If not
|
||||
/// set, this will default to an empty string.
|
||||
/// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `true` if the `signature` was a valid signature created by this
|
||||
/// [`SigningKey`] on the `prehashed_message`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
|
||||
/// function technically works, and is probably safe to use, with any secure hash function with
|
||||
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
|
||||
/// [`crate::Sha512`] for user convenience.
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_prehashed_strict<MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
context: Option<&[u8]>,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
let signature = InternalSignature::try_from(signature)?;
|
||||
|
||||
let ctx: &[u8] = context.unwrap_or(b"");
|
||||
debug_assert!(
|
||||
ctx.len() <= 255,
|
||||
"The context must not be longer than 255 octets."
|
||||
);
|
||||
|
||||
let signature_R = signature
|
||||
.R
|
||||
.decompress()
|
||||
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
|
||||
|
||||
// Logical OR is fine here as we're not trying to be constant time.
|
||||
if signature_R.is_small_order() || self.point.is_small_order() {
|
||||
return Err(InternalError::Verify.into());
|
||||
}
|
||||
|
||||
let message = prehashed_message.finalize();
|
||||
let expected_R = self.recompute_R::<Sha512>(Some(ctx), &signature, &message);
|
||||
|
||||
if expected_R == signature.R {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InternalError::Verify.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this verifying key into Montgomery form.
|
||||
///
|
||||
/// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The output of
|
||||
/// this function is a valid X25519 public key whose secret key is `sk.to_scalar_bytes()`,
|
||||
/// where `sk` is a valid signing key for this `VerifyingKey`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually
|
||||
/// long-term keys, while keys used for key exchange should rather be ephemeral. If you can
|
||||
/// help it, use a separate key for encryption.
|
||||
///
|
||||
/// For more information on the security of systems which use the same keys for both signing
|
||||
/// and Diffie-Hellman, see the paper
|
||||
/// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509).
|
||||
pub fn to_montgomery(&self) -> MontgomeryPoint {
|
||||
self.point.to_montgomery()
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<ed25519::Signature> for VerifyingKey {
|
||||
/// Verify a signature on a message with this keypair's public key.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
|
||||
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
|
||||
self.raw_verify::<Sha512>(message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`None`].
|
||||
#[cfg(feature = "digest")]
|
||||
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for VerifyingKey
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
fn verify_digest(
|
||||
&self,
|
||||
msg_digest: MsgDigest,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.verify_prehashed(msg_digest, None, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`]
|
||||
/// containing `self.value()`.
|
||||
#[cfg(feature = "digest")]
|
||||
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for Context<'_, '_, VerifyingKey>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
{
|
||||
fn verify_digest(
|
||||
&self,
|
||||
msg_digest: MsgDigest,
|
||||
signature: &ed25519::Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.key()
|
||||
.verify_prehashed(msg_digest, Some(self.value()), signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for VerifyingKey {
|
||||
type Error = SignatureError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let bytes = bytes.try_into().map_err(|_| InternalError::BytesLength {
|
||||
name: "VerifyingKey",
|
||||
length: PUBLIC_KEY_LENGTH,
|
||||
})?;
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePublicKey for VerifyingKey {
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
|
||||
pkcs8::PublicKeyBytes::from(self).to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::PublicKeyBytes> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
|
||||
VerifyingKey::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
|
||||
VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<VerifyingKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes::from(&verifying_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl From<&VerifyingKey> for pkcs8::PublicKeyBytes {
|
||||
fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes {
|
||||
pkcs8::PublicKeyBytes(verifying_key.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for VerifyingKey {
|
||||
type Error = pkcs8::spki::Error;
|
||||
|
||||
fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
|
||||
pkcs8::PublicKeyBytes::try_from(public_key)?.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for VerifyingKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_bytes(&self.as_bytes()[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'d> Deserialize<'d> for VerifyingKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'d>,
|
||||
{
|
||||
struct VerifyingKeyVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor {
|
||||
type Value = VerifyingKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
write!(formatter, concat!("An ed25519 verifying (public) key"))
|
||||
}
|
||||
|
||||
fn visit_borrowed_bytes<E: serde::de::Error>(
|
||||
self,
|
||||
bytes: &'de [u8],
|
||||
) -> Result<Self::Value, E> {
|
||||
VerifyingKey::try_from(bytes.as_ref()).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
|
||||
let remaining = (0..)
|
||||
.map(|_| seq.next_element::<u8>())
|
||||
.take_while(|el| matches!(el, Ok(Some(_))))
|
||||
.count();
|
||||
|
||||
if remaining > 0 {
|
||||
return Err(serde::de::Error::invalid_length(
|
||||
32 + remaining,
|
||||
&"expected 32 bytes",
|
||||
));
|
||||
}
|
||||
|
||||
VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(VerifyingKeyVisitor)
|
||||
}
|
||||
}
|
||||
@ -1,660 +0,0 @@
|
||||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-dalek.
|
||||
// Copyright (c) 2017-2019 isis lovecruft
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
|
||||
//! Integration tests for ed25519-dalek.
|
||||
|
||||
use curve25519_dalek;
|
||||
|
||||
use ed25519_dalek::*;
|
||||
|
||||
use hex::FromHex;
|
||||
#[cfg(feature = "digest")]
|
||||
use hex_literal::hex;
|
||||
|
||||
#[cfg(test)]
|
||||
mod vectors {
|
||||
use super::*;
|
||||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_POINT,
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
scalar::Scalar,
|
||||
traits::IsIdentity,
|
||||
};
|
||||
use sha2::{digest::Digest, Sha512};
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
ops::Neg,
|
||||
};
|
||||
|
||||
// TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang
|
||||
// package. It is a selection of test cases from
|
||||
// http://ed25519.cr.yp.to/python/sign.input
|
||||
#[test]
|
||||
fn against_reference_implementation() {
|
||||
// TestGolden
|
||||
let mut line: String;
|
||||
let mut lineno: usize = 0;
|
||||
|
||||
let f = File::open("TESTVECTORS");
|
||||
if f.is_err() {
|
||||
println!(
|
||||
"This test is only available when the code has been cloned \
|
||||
from the git repository, since the TESTVECTORS file is large \
|
||||
and is therefore not included within the distributed crate."
|
||||
);
|
||||
panic!();
|
||||
}
|
||||
let file = BufReader::new(f.unwrap());
|
||||
|
||||
for l in file.lines() {
|
||||
lineno += 1;
|
||||
line = l.unwrap();
|
||||
|
||||
let parts: Vec<&str> = line.split(':').collect();
|
||||
assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno);
|
||||
|
||||
let sec_bytes: Vec<u8> = FromHex::from_hex(&parts[0]).unwrap();
|
||||
let pub_bytes: Vec<u8> = FromHex::from_hex(&parts[1]).unwrap();
|
||||
let msg_bytes: Vec<u8> = FromHex::from_hex(&parts[2]).unwrap();
|
||||
let sig_bytes: Vec<u8> = FromHex::from_hex(&parts[3]).unwrap();
|
||||
|
||||
let sec_bytes = &sec_bytes[..SECRET_KEY_LENGTH].try_into().unwrap();
|
||||
let pub_bytes = &pub_bytes[..PUBLIC_KEY_LENGTH].try_into().unwrap();
|
||||
|
||||
let signing_key = SigningKey::from_bytes(sec_bytes);
|
||||
let expected_verifying_key = VerifyingKey::from_bytes(pub_bytes).unwrap();
|
||||
assert_eq!(expected_verifying_key, signing_key.verifying_key());
|
||||
|
||||
// The signatures in the test vectors also include the message
|
||||
// at the end, but we just want R and S.
|
||||
let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap();
|
||||
let sig2: Signature = signing_key.sign(&msg_bytes);
|
||||
|
||||
assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno);
|
||||
assert!(
|
||||
signing_key.verify(&msg_bytes, &sig2).is_ok(),
|
||||
"Signature verification failed on line {}",
|
||||
lineno
|
||||
);
|
||||
assert!(
|
||||
expected_verifying_key
|
||||
.verify_strict(&msg_bytes, &sig2)
|
||||
.is_ok(),
|
||||
"Signature strict verification failed on line {}",
|
||||
lineno
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// From https://tools.ietf.org/html/rfc8032#section-7.3
|
||||
#[cfg(feature = "digest")]
|
||||
#[test]
|
||||
fn ed25519ph_rf8032_test_vector_prehash() {
|
||||
let sec_bytes = hex!("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42");
|
||||
let pub_bytes = hex!("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf");
|
||||
let msg_bytes = hex!("616263");
|
||||
let sig_bytes = hex!("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406");
|
||||
|
||||
let signing_key = SigningKey::from_bytes(&sec_bytes);
|
||||
let expected_verifying_key = VerifyingKey::from_bytes(&pub_bytes).unwrap();
|
||||
assert_eq!(expected_verifying_key, signing_key.verifying_key());
|
||||
let sig1 = Signature::try_from(&sig_bytes[..]).unwrap();
|
||||
|
||||
let mut prehash_for_signing = Sha512::default();
|
||||
let mut prehash_for_verifying = Sha512::default();
|
||||
|
||||
prehash_for_signing.update(&msg_bytes[..]);
|
||||
prehash_for_verifying.update(&msg_bytes[..]);
|
||||
|
||||
let sig2: Signature = signing_key
|
||||
.sign_prehashed(prehash_for_signing, None)
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
sig1 == sig2,
|
||||
"Original signature from test vectors doesn't equal signature produced:\
|
||||
\noriginal:\n{:?}\nproduced:\n{:?}",
|
||||
sig1,
|
||||
sig2
|
||||
);
|
||||
assert!(
|
||||
signing_key
|
||||
.verify_prehashed(prehash_for_verifying.clone(), None, &sig2)
|
||||
.is_ok(),
|
||||
"Could not verify ed25519ph signature!"
|
||||
);
|
||||
assert!(
|
||||
expected_verifying_key
|
||||
.verify_prehashed_strict(prehash_for_verifying, None, &sig2)
|
||||
.is_ok(),
|
||||
"Could not strict-verify ed25519ph signature!"
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// The remaining items in this mod are for the repudiation tests
|
||||
//
|
||||
|
||||
// Taken from curve25519_dalek::constants::EIGHT_TORSION[4]
|
||||
const EIGHT_TORSION_4: [u8; 32] = [
|
||||
236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127,
|
||||
];
|
||||
|
||||
// Computes the prehashed or non-prehashed challenge, depending on whether context is given
|
||||
fn compute_challenge(
|
||||
message: &[u8],
|
||||
pub_key: &EdwardsPoint,
|
||||
signature_r: &EdwardsPoint,
|
||||
context: Option<&[u8]>,
|
||||
) -> Scalar {
|
||||
let mut h = Sha512::default();
|
||||
if let Some(c) = context {
|
||||
h.update(b"SigEd25519 no Ed25519 collisions");
|
||||
h.update(&[1]);
|
||||
h.update(&[c.len() as u8]);
|
||||
h.update(c);
|
||||
}
|
||||
h.update(&signature_r.compress().as_bytes());
|
||||
h.update(&pub_key.compress().as_bytes()[..]);
|
||||
h.update(&message);
|
||||
Scalar::from_hash(h)
|
||||
}
|
||||
|
||||
fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec<u8> {
|
||||
[&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat()
|
||||
}
|
||||
|
||||
const WEAK_PUBKEY: CompressedEdwardsY = CompressedEdwardsY(EIGHT_TORSION_4);
|
||||
|
||||
// Pick a random Scalar
|
||||
fn non_null_scalar() -> Scalar {
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
let mut s_candidate = Scalar::random(&mut rng);
|
||||
while s_candidate == Scalar::ZERO {
|
||||
s_candidate = Scalar::random(&mut rng);
|
||||
}
|
||||
s_candidate
|
||||
}
|
||||
|
||||
fn pick_r(s: Scalar) -> EdwardsPoint {
|
||||
let r0 = s * ED25519_BASEPOINT_POINT;
|
||||
// Pick a torsion point of order 2
|
||||
r0 + WEAK_PUBKEY.decompress().unwrap().neg()
|
||||
}
|
||||
|
||||
// Tests that verify_strict() rejects small-order pubkeys. We test this by explicitly
|
||||
// constructing a pubkey-signature pair that verifies with respect to two distinct messages.
|
||||
// This should be accepted by verify(), but rejected by verify_strict().
|
||||
#[test]
|
||||
fn repudiation() {
|
||||
let message1 = b"Send 100 USD to Alice";
|
||||
let message2 = b"Send 100000 USD to Alice";
|
||||
|
||||
let mut s: Scalar = non_null_scalar();
|
||||
let pubkey = WEAK_PUBKEY.decompress().unwrap();
|
||||
let mut r = pick_r(s);
|
||||
|
||||
// Find an R such that
|
||||
// H(R || A || M₁) · A == A == H(R || A || M₂) · A
|
||||
// This happens with high probability when A is low order.
|
||||
while !(pubkey.neg() + compute_challenge(message1, &pubkey, &r, None) * pubkey)
|
||||
.is_identity()
|
||||
|| !(pubkey.neg() + compute_challenge(message2, &pubkey, &r, None) * pubkey)
|
||||
.is_identity()
|
||||
{
|
||||
// We pick an s and let R = sB - A where B is the basepoint
|
||||
s = non_null_scalar();
|
||||
r = pick_r(s);
|
||||
}
|
||||
|
||||
// At this point, both verification equations hold:
|
||||
// sB = R + H(R || A || M₁) · A
|
||||
// = R + H(R || A || M₂) · A
|
||||
// Check that this is true
|
||||
let signature = serialize_signature(&r, &s);
|
||||
let vk = VerifyingKey::from_bytes(&pubkey.compress().as_bytes()).unwrap();
|
||||
let sig = Signature::try_from(&signature[..]).unwrap();
|
||||
assert!(vk.verify(message1, &sig).is_ok());
|
||||
assert!(vk.verify(message2, &sig).is_ok());
|
||||
|
||||
// Check that this public key appears as weak
|
||||
assert!(vk.is_weak());
|
||||
|
||||
// Now check that the sigs fail under verify_strict. This is because verify_strict rejects
|
||||
// small order pubkeys.
|
||||
assert!(vk.verify_strict(message1, &sig).is_err());
|
||||
assert!(vk.verify_strict(message2, &sig).is_err());
|
||||
}
|
||||
|
||||
// Identical to repudiation() above, but testing verify_prehashed against
|
||||
// verify_prehashed_strict. See comments above for a description of what's happening.
|
||||
#[cfg(feature = "digest")]
|
||||
#[test]
|
||||
fn repudiation_prehash() {
|
||||
let message1 = Sha512::new().chain_update(b"Send 100 USD to Alice");
|
||||
let message2 = Sha512::new().chain_update(b"Send 100000 USD to Alice");
|
||||
let message1_bytes = message1.clone().finalize();
|
||||
let message2_bytes = message2.clone().finalize();
|
||||
|
||||
let mut s: Scalar = non_null_scalar();
|
||||
let pubkey = WEAK_PUBKEY.decompress().unwrap();
|
||||
let mut r = pick_r(s);
|
||||
let context_str = Some(&b"edtest"[..]);
|
||||
|
||||
while !(pubkey.neg()
|
||||
+ compute_challenge(&message1_bytes, &pubkey, &r, context_str) * pubkey)
|
||||
.is_identity()
|
||||
|| !(pubkey.neg()
|
||||
+ compute_challenge(&message2_bytes, &pubkey, &r, context_str) * pubkey)
|
||||
.is_identity()
|
||||
{
|
||||
s = non_null_scalar();
|
||||
r = pick_r(s);
|
||||
}
|
||||
|
||||
// Check that verify_prehashed succeeds on both sigs
|
||||
let signature = serialize_signature(&r, &s);
|
||||
let vk = VerifyingKey::from_bytes(&pubkey.compress().as_bytes()).unwrap();
|
||||
let sig = Signature::try_from(&signature[..]).unwrap();
|
||||
assert!(vk
|
||||
.verify_prehashed(message1.clone(), context_str, &sig)
|
||||
.is_ok());
|
||||
assert!(vk
|
||||
.verify_prehashed(message2.clone(), context_str, &sig)
|
||||
.is_ok());
|
||||
|
||||
// Check that verify_prehashed_strict fails on both sigs
|
||||
assert!(vk
|
||||
.verify_prehashed_strict(message1.clone(), context_str, &sig)
|
||||
.is_err());
|
||||
assert!(vk
|
||||
.verify_prehashed_strict(message2.clone(), context_str, &sig)
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand_core")]
|
||||
mod integrations {
|
||||
use super::*;
|
||||
use rand::rngs::OsRng;
|
||||
#[cfg(feature = "digest")]
|
||||
use sha2::Sha512;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn sign_verify() {
|
||||
// TestSignVerify
|
||||
let signing_key: SigningKey;
|
||||
let good_sig: Signature;
|
||||
let bad_sig: Signature;
|
||||
|
||||
let good: &[u8] = "test message".as_bytes();
|
||||
let bad: &[u8] = "wrong message".as_bytes();
|
||||
|
||||
let mut csprng = OsRng;
|
||||
|
||||
signing_key = SigningKey::generate(&mut csprng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
good_sig = signing_key.sign(&good);
|
||||
bad_sig = signing_key.sign(&bad);
|
||||
|
||||
// Check that an honestly generated public key is not weak
|
||||
assert!(!verifying_key.is_weak());
|
||||
|
||||
assert!(
|
||||
signing_key.verify(&good, &good_sig).is_ok(),
|
||||
"Verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key.verify_strict(&good, &good_sig).is_ok(),
|
||||
"Strict verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
signing_key.verify(&good, &bad_sig).is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key.verify_strict(&good, &bad_sig).is_err(),
|
||||
"Strict verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
signing_key.verify(&bad, &good_sig).is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key.verify_strict(&bad, &good_sig).is_err(),
|
||||
"Strict verification of a signature on a different message passed!"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
#[test]
|
||||
fn ed25519ph_sign_verify() {
|
||||
let signing_key: SigningKey;
|
||||
let good_sig: Signature;
|
||||
let bad_sig: Signature;
|
||||
|
||||
let good: &[u8] = b"test message";
|
||||
let bad: &[u8] = b"wrong message";
|
||||
|
||||
let mut csprng = OsRng;
|
||||
|
||||
// ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes
|
||||
let mut prehashed_good1: Sha512 = Sha512::default();
|
||||
prehashed_good1.update(good);
|
||||
let mut prehashed_good2: Sha512 = Sha512::default();
|
||||
prehashed_good2.update(good);
|
||||
let mut prehashed_good3: Sha512 = Sha512::default();
|
||||
prehashed_good3.update(good);
|
||||
|
||||
let mut prehashed_bad1: Sha512 = Sha512::default();
|
||||
prehashed_bad1.update(bad);
|
||||
let mut prehashed_bad2: Sha512 = Sha512::default();
|
||||
prehashed_bad2.update(bad);
|
||||
|
||||
let context: &[u8] = b"testing testing 1 2 3";
|
||||
|
||||
signing_key = SigningKey::generate(&mut csprng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
good_sig = signing_key
|
||||
.sign_prehashed(prehashed_good1, Some(context))
|
||||
.unwrap();
|
||||
bad_sig = signing_key
|
||||
.sign_prehashed(prehashed_bad1, Some(context))
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_good2.clone(), Some(context), &good_sig)
|
||||
.is_ok(),
|
||||
"Verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key
|
||||
.verify_prehashed_strict(prehashed_good2, Some(context), &good_sig)
|
||||
.is_ok(),
|
||||
"Strict verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_good3.clone(), Some(context), &bad_sig)
|
||||
.is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key
|
||||
.verify_prehashed_strict(prehashed_good3, Some(context), &bad_sig)
|
||||
.is_err(),
|
||||
"Strict verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
signing_key
|
||||
.verify_prehashed(prehashed_bad2.clone(), Some(context), &good_sig)
|
||||
.is_err(),
|
||||
"Verification of a signature on a different message passed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key
|
||||
.verify_prehashed_strict(prehashed_bad2, Some(context), &good_sig)
|
||||
.is_err(),
|
||||
"Strict verification of a signature on a different message passed!"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "batch")]
|
||||
#[test]
|
||||
fn verify_batch_seven_signatures() {
|
||||
let messages: [&[u8]; 7] = [
|
||||
b"Watch closely everyone, I'm going to show you how to kill a god.",
|
||||
b"I'm not a cryptographer I just encrypt a lot.",
|
||||
b"Still not a cryptographer.",
|
||||
b"This is a test of the tsunami alert system. This is only a test.",
|
||||
b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.",
|
||||
b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.",
|
||||
b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ];
|
||||
let mut csprng = OsRng;
|
||||
let mut signing_keys: Vec<SigningKey> = Vec::new();
|
||||
let mut signatures: Vec<Signature> = Vec::new();
|
||||
|
||||
for i in 0..messages.len() {
|
||||
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
signatures.push(signing_key.sign(&messages[i]));
|
||||
signing_keys.push(signing_key);
|
||||
}
|
||||
let verifying_keys: Vec<VerifyingKey> =
|
||||
signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
let result = verify_batch(&messages, &signatures, &verifying_keys);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_key_hash_trait_check() {
|
||||
let mut csprng = OsRng {};
|
||||
let secret: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let public_from_secret: VerifyingKey = (&secret).into();
|
||||
|
||||
let mut m = HashMap::new();
|
||||
m.insert(public_from_secret, "Example_Public_Key");
|
||||
|
||||
m.insert(public_from_secret, "Updated Value");
|
||||
|
||||
let (k, &v) = m.get_key_value(&public_from_secret).unwrap();
|
||||
assert_eq!(k, &public_from_secret);
|
||||
assert_eq!(v, "Updated Value");
|
||||
assert_eq!(m.len(), 1usize);
|
||||
|
||||
let second_secret: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let public_from_second_secret: VerifyingKey = (&second_secret).into();
|
||||
assert_ne!(public_from_secret, public_from_second_secret);
|
||||
m.insert(public_from_second_secret, "Second public key");
|
||||
|
||||
let (k, &v) = m.get_key_value(&public_from_second_secret).unwrap();
|
||||
assert_eq!(k, &public_from_second_secret);
|
||||
assert_eq!(v, "Second public key");
|
||||
assert_eq!(m.len(), 2usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(crate = "serde")]
|
||||
struct Demo {
|
||||
signing_key: SigningKey,
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
mod serialisation {
|
||||
use super::*;
|
||||
|
||||
// The size for bincode to serialize the length of a byte array.
|
||||
static BINCODE_INT_LENGTH: usize = 8;
|
||||
|
||||
static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [
|
||||
130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, 014, 041,
|
||||
166, 120, 108, 035, 254, 077, 160, 083, 172, 058, 219, 042, 086, 120,
|
||||
];
|
||||
|
||||
static SECRET_KEY_BYTES: [u8; SECRET_KEY_LENGTH] = [
|
||||
062, 070, 027, 163, 092, 182, 011, 003, 077, 234, 098, 004, 011, 127, 079, 228, 243, 187,
|
||||
150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054,
|
||||
];
|
||||
|
||||
/// Signature with the above signing_key of a blank message.
|
||||
static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [
|
||||
010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123,
|
||||
080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007,
|
||||
053, 185, 227, 229, 079, 108, 213, 080, 124, 252, 084, 167, 216, 085, 134, 144, 129, 149,
|
||||
041, 081, 063, 120, 126, 100, 092, 059, 050, 011,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signature_bincode() {
|
||||
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
|
||||
let encoded_signature: Vec<u8> = bincode::serialize(&signature).unwrap();
|
||||
let decoded_signature: Signature = bincode::deserialize(&encoded_signature).unwrap();
|
||||
|
||||
assert_eq!(signature, decoded_signature);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signature_json() {
|
||||
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
|
||||
let encoded_signature = serde_json::to_string(&signature).unwrap();
|
||||
let decoded_signature: Signature = serde_json::from_str(&encoded_signature).unwrap();
|
||||
|
||||
assert_eq!(signature, decoded_signature);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_verifying_key_bincode() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_verifying_key: Vec<u8> = bincode::serialize(&verifying_key).unwrap();
|
||||
let decoded_verifying_key: VerifyingKey =
|
||||
bincode::deserialize(&encoded_verifying_key).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&PUBLIC_KEY_BYTES[..],
|
||||
&encoded_verifying_key[encoded_verifying_key.len() - PUBLIC_KEY_LENGTH..]
|
||||
);
|
||||
assert_eq!(verifying_key, decoded_verifying_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_verifying_key_json() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
let encoded_verifying_key = serde_json::to_string(&verifying_key).unwrap();
|
||||
let decoded_verifying_key: VerifyingKey =
|
||||
serde_json::from_str(&encoded_verifying_key).unwrap();
|
||||
|
||||
assert_eq!(verifying_key, decoded_verifying_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_verifying_key_json_too_long() {
|
||||
// derived from `serialize_deserialize_verifying_key_json` test
|
||||
// trailing zero elements makes key too long (34 bytes)
|
||||
let encoded_verifying_key_too_long = "[130,39,155,15,62,76,188,63,124,122,26,251,233,253,225,220,14,41,166,120,108,35,254,77,160,83,172,58,219,42,86,120,0,0]";
|
||||
let de_err = serde_json::from_str::<VerifyingKey>(&encoded_verifying_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
de_err.contains("invalid length 34"),
|
||||
"expected invalid length error, got: {de_err}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_verifying_key_json_too_short() {
|
||||
// derived from `serialize_deserialize_verifying_key_json` test
|
||||
let encoded_verifying_key_too_long = "[130,39,155,15]";
|
||||
let de_err = serde_json::from_str::<VerifyingKey>(&encoded_verifying_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
de_err.contains("invalid length 4"),
|
||||
"expected invalid length error, got: {de_err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signing_key_bincode() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
let encoded_signing_key: Vec<u8> = bincode::serialize(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap();
|
||||
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signing_key_json() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
let encoded_signing_key = serde_json::to_string(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap();
|
||||
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signing_key_json_too_long() {
|
||||
// derived from `serialize_deserialize_signing_key_json` test
|
||||
// trailing zero elements makes key too long (34 bytes)
|
||||
let encoded_signing_key_too_long = "[62,70,27,163,92,182,11,3,77,234,98,4,11,127,79,228,243,187,150,73,201,137,76,22,85,251,152,2,241,42,72,54,0,0]";
|
||||
let de_err = serde_json::from_str::<SigningKey>(&encoded_signing_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
de_err.contains("invalid length 34"),
|
||||
"expected invalid length error, got: {de_err}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signing_key_json_too_short() {
|
||||
// derived from `serialize_deserialize_signing_key_json` test
|
||||
let encoded_signing_key_too_long = "[62,70,27,163]";
|
||||
let de_err = serde_json::from_str::<SigningKey>(&encoded_signing_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
de_err.contains("invalid length 4"),
|
||||
"expected invalid length error, got: {de_err}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_signing_key_toml() {
|
||||
let demo = Demo {
|
||||
signing_key: SigningKey::from_bytes(&SECRET_KEY_BYTES),
|
||||
};
|
||||
|
||||
println!("\n\nWrite to toml");
|
||||
let demo_toml = toml::to_string(&demo).unwrap();
|
||||
println!("{}", demo_toml);
|
||||
let demo_toml_rebuild: Result<Demo, _> = toml::from_str(&demo_toml);
|
||||
println!("{:?}", demo_toml_rebuild);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_verifying_key_size() {
|
||||
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&verifying_key).unwrap() as usize,
|
||||
BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_signature_size() {
|
||||
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&signature).unwrap() as usize,
|
||||
SIGNATURE_LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_signing_key_size() {
|
||||
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
|
||||
assert_eq!(
|
||||
bincode::serialized_size(&signing_key).unwrap() as usize,
|
||||
BINCODE_INT_LENGTH + SECRET_KEY_LENGTH
|
||||
);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,71 +0,0 @@
|
||||
//! PKCS#8 private key and SPKI public key tests.
|
||||
//!
|
||||
//! These are standard formats for storing public and private keys, defined in
|
||||
//! RFC5958 (PKCS#8) and RFC5280 (SPKI).
|
||||
|
||||
#![cfg(feature = "pkcs8")]
|
||||
|
||||
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey};
|
||||
use ed25519_dalek::{SigningKey, VerifyingKey};
|
||||
use hex_literal::hex;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER.
|
||||
const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER.
|
||||
const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der");
|
||||
|
||||
/// Ed25519 SubjectVerifyingKeyInfo encoded as ASN.1 DER.
|
||||
const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der");
|
||||
|
||||
/// Secret key bytes.
|
||||
///
|
||||
/// Extracted with:
|
||||
/// $ openssl asn1parse -inform der -in tests/examples/pkcs8-v1.der
|
||||
const SK_BYTES: [u8; 32] = hex!("D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842");
|
||||
|
||||
/// Public key bytes.
|
||||
const PK_BYTES: [u8; 32] = hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
|
||||
|
||||
#[test]
|
||||
fn decode_pkcs8_v1() {
|
||||
let keypair = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_pkcs8_v2() {
|
||||
let keypair = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap();
|
||||
assert_eq!(SK_BYTES, keypair.to_bytes());
|
||||
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_verifying_key() {
|
||||
let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
|
||||
assert_eq!(PK_BYTES, verifying_key.to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn encode_pkcs8() {
|
||||
let keypair = SigningKey::from_bytes(&SK_BYTES);
|
||||
let pkcs8_key = keypair.to_pkcs8_der().unwrap();
|
||||
|
||||
let keypair2 = SigningKey::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap();
|
||||
assert_eq!(keypair.to_bytes(), keypair2.to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn encode_verifying_key() {
|
||||
let verifying_key = VerifyingKey::from_bytes(&PK_BYTES).unwrap();
|
||||
let verifying_key_der = verifying_key.to_public_key_der().unwrap();
|
||||
|
||||
let verifying_key2 = VerifyingKey::from_public_key_der(verifying_key_der.as_bytes()).unwrap();
|
||||
assert_eq!(verifying_key, verifying_key2);
|
||||
}
|
||||
@ -1,231 +0,0 @@
|
||||
use ed25519::signature::Verifier;
|
||||
use ed25519_dalek::{Signature, VerifyingKey};
|
||||
|
||||
use serde::{de::Error as SError, Deserialize, Deserializer};
|
||||
use std::{collections::BTreeSet as Set, fs::File};
|
||||
|
||||
/// The set of edge cases that [`VerifyingKey::verify()`] permits.
|
||||
const VERIFY_ALLOWED_EDGECASES: &[Flag] = &[
|
||||
Flag::LowOrderA,
|
||||
Flag::LowOrderR,
|
||||
Flag::NonCanonicalA,
|
||||
Flag::LowOrderComponentA,
|
||||
Flag::LowOrderComponentR,
|
||||
// `ReencodedK` is not actually permitted by `verify()`, but it looks that way in the tests
|
||||
// because it sometimes occurs with a low-order A. 1/8 of the time, the resulting signature
|
||||
// will be identical the one made with a normal k. find_validation_criteria shows that indeed
|
||||
// this occurs 10/58 of the time
|
||||
Flag::ReencodedK,
|
||||
];
|
||||
|
||||
/// The set of edge cases that [`VerifyingKey::verify_strict()`] permits
|
||||
const VERIFY_STRICT_ALLOWED_EDGECASES: &[Flag] =
|
||||
&[Flag::LowOrderComponentA, Flag::LowOrderComponentR];
|
||||
|
||||
/// Each variant describes a specfiic edge case that can occur in an Ed25519 signature. Refer to
|
||||
/// the test vector [README][] for more info.
|
||||
///
|
||||
/// [README]: https://github.com/C2SP/CCTV/blob/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors/README.md
|
||||
#[derive(Deserialize, Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
|
||||
enum Flag {
|
||||
#[serde(rename = "low_order")]
|
||||
LowOrder,
|
||||
#[serde(rename = "low_order_A")]
|
||||
LowOrderA,
|
||||
#[serde(rename = "low_order_R")]
|
||||
LowOrderR,
|
||||
#[serde(rename = "non_canonical_A")]
|
||||
NonCanonicalA,
|
||||
#[serde(rename = "non_canonical_R")]
|
||||
NonCanonicalR,
|
||||
#[serde(rename = "low_order_component_A")]
|
||||
LowOrderComponentA,
|
||||
#[serde(rename = "low_order_component_R")]
|
||||
LowOrderComponentR,
|
||||
#[serde(rename = "low_order_residue")]
|
||||
LowOrderResidue,
|
||||
#[serde(rename = "reencoded_k")]
|
||||
ReencodedK,
|
||||
}
|
||||
|
||||
/// This is an intermediate representation between JSON and TestVector
|
||||
#[derive(Deserialize)]
|
||||
struct IntermediateTestVector {
|
||||
number: usize,
|
||||
#[serde(deserialize_with = "bytes_from_hex", rename = "key")]
|
||||
pubkey: Vec<u8>,
|
||||
#[serde(deserialize_with = "bytes_from_hex")]
|
||||
sig: Vec<u8>,
|
||||
msg: String,
|
||||
flags: Option<Set<Flag>>,
|
||||
}
|
||||
|
||||
/// The test vector struct from [CCTV][]. `sig` may or may not be a valid signature of `msg` with
|
||||
/// respect to `pubkey`, depending on the verification function's validation criteria. `flags`
|
||||
/// describes all the edge cases which this test vector falls into.
|
||||
///
|
||||
/// [CCTV]: https://github.com/C2SP/CCTV/tree/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors
|
||||
struct TestVector {
|
||||
number: usize,
|
||||
pubkey: VerifyingKey,
|
||||
sig: Signature,
|
||||
msg: Vec<u8>,
|
||||
flags: Set<Flag>,
|
||||
}
|
||||
|
||||
impl From<IntermediateTestVector> for TestVector {
|
||||
fn from(tv: IntermediateTestVector) -> Self {
|
||||
let number = tv.number;
|
||||
let pubkey = {
|
||||
let mut buf = [0u8; 32];
|
||||
buf.copy_from_slice(&tv.pubkey);
|
||||
VerifyingKey::from_bytes(&buf).unwrap()
|
||||
};
|
||||
let sig = {
|
||||
let mut buf = [0u8; 64];
|
||||
buf.copy_from_slice(&tv.sig);
|
||||
Signature::from_bytes(&buf)
|
||||
};
|
||||
let msg = tv.msg.as_bytes().to_vec();
|
||||
|
||||
// Unwrap the Option<Set<Flag>>
|
||||
let flags = tv.flags.unwrap_or_else(Default::default);
|
||||
|
||||
Self {
|
||||
number,
|
||||
pubkey,
|
||||
sig,
|
||||
msg,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tells serde how to deserialize bytes from hex
|
||||
fn bytes_from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut hex_str = String::deserialize(deserializer)?;
|
||||
// Prepend a 0 if it's not even length
|
||||
if hex_str.len() % 2 == 1 {
|
||||
hex_str.insert(0, '0');
|
||||
}
|
||||
hex::decode(hex_str).map_err(|e| SError::custom(format!("{:?}", e)))
|
||||
}
|
||||
|
||||
fn get_test_vectors() -> impl Iterator<Item = TestVector> {
|
||||
let f = File::open("VALIDATIONVECTORS").expect(
|
||||
"This test is only available when the code has been cloned from the git repository, since
|
||||
the VALIDATIONVECTORS file is large and is therefore not included within the distributed \
|
||||
crate.",
|
||||
);
|
||||
|
||||
serde_json::from_reader::<_, Vec<IntermediateTestVector>>(f)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(TestVector::from)
|
||||
}
|
||||
|
||||
/// Tests that the verify() and verify_strict() functions succeed only on test cases whose flags
|
||||
/// (i.e., edge cases it falls into) are a subset of VERIFY_ALLOWED_EDGECASES and
|
||||
/// VERIFY_STRICT_ALLOWED_EDGECASES, respectively
|
||||
#[test]
|
||||
fn check_validation_criteria() {
|
||||
let verify_allowed_edgecases = Set::from_iter(VERIFY_ALLOWED_EDGECASES.to_vec());
|
||||
let verify_strict_allowed_edgecases = Set::from_iter(VERIFY_STRICT_ALLOWED_EDGECASES.to_vec());
|
||||
|
||||
for TestVector {
|
||||
number,
|
||||
pubkey,
|
||||
msg,
|
||||
sig,
|
||||
flags,
|
||||
} in get_test_vectors()
|
||||
{
|
||||
// If all the verify-permitted flags here are ones we permit, then verify() should succeed.
|
||||
// Otherwise, it should not.
|
||||
let success = pubkey.verify(&msg, &sig).is_ok();
|
||||
if flags.is_subset(&verify_allowed_edgecases) {
|
||||
assert!(success, "verify() expected success in testcase #{number}",);
|
||||
} else {
|
||||
assert!(!success, "verify() expected failure in testcase #{number}",);
|
||||
}
|
||||
|
||||
// If all the verify_strict-permitted flags here are ones we permit, then verify_strict()
|
||||
// should succeed. Otherwise, it should not.
|
||||
let success = pubkey.verify_strict(&msg, &sig).is_ok();
|
||||
if flags.is_subset(&verify_strict_allowed_edgecases) {
|
||||
assert!(
|
||||
success,
|
||||
"verify_strict() expected success in testcase #{number}",
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
!success,
|
||||
"verify_strict() expected failure in testcase #{number}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints the flags that are consistently permitted by verify() and verify_strict()
|
||||
#[test]
|
||||
fn find_validation_criteria() {
|
||||
let mut verify_allowed_edgecases = Set::new();
|
||||
let mut verify_strict_allowed_edgecases = Set::new();
|
||||
|
||||
// Counts the number of times a signature with a re-encoded k and a low-order A verified. This
|
||||
// happens with 1/8 probability, assuming the usual verification equation(s).
|
||||
let mut num_lucky_reencoded_k = 0;
|
||||
let mut num_reencoded_k = 0;
|
||||
|
||||
for TestVector {
|
||||
number: _,
|
||||
pubkey,
|
||||
msg,
|
||||
sig,
|
||||
flags,
|
||||
} in get_test_vectors()
|
||||
{
|
||||
// If verify() was a success, add all the associated flags to verify-permitted set
|
||||
let success = pubkey.verify(&msg, &sig).is_ok();
|
||||
|
||||
// If this is ReencodedK && LowOrderA, log some statistics
|
||||
if flags.contains(&Flag::ReencodedK) && flags.contains(&Flag::LowOrderA) {
|
||||
num_reencoded_k += 1;
|
||||
num_lucky_reencoded_k += success as u8;
|
||||
}
|
||||
|
||||
if success {
|
||||
for flag in &flags {
|
||||
// Don't count re-encoded k when A is low-order. This is because the
|
||||
// re-encoded k might be a multiple of 8 by accident
|
||||
if *flag == Flag::ReencodedK && flags.contains(&Flag::LowOrderA) {
|
||||
continue;
|
||||
} else {
|
||||
verify_allowed_edgecases.insert(*flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If verify_strict() was a success, add all the associated flags to
|
||||
// verify_strict-permitted set
|
||||
let success = pubkey.verify_strict(&msg, &sig).is_ok();
|
||||
if success {
|
||||
for flag in &flags {
|
||||
verify_strict_allowed_edgecases.insert(*flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("VERIFY_ALLOWED_EDGECASES: {:?}", verify_allowed_edgecases);
|
||||
println!(
|
||||
"VERIFY_STRICT_ALLOWED_EDGECASES: {:?}",
|
||||
verify_strict_allowed_edgecases
|
||||
);
|
||||
println!(
|
||||
"re-encoded k && low-order A yielded a valid signature {}/{} of the time",
|
||||
num_lucky_reencoded_k, num_reencoded_k
|
||||
);
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
//! Tests for converting Ed25519 keys into X25519 (Montgomery form) keys.
|
||||
|
||||
use curve25519_dalek::scalar::{clamp_integer, Scalar};
|
||||
use ed25519_dalek::SigningKey;
|
||||
use hex_literal::hex;
|
||||
use sha2::{Digest, Sha512};
|
||||
|
||||
/// Helper function to return the bytes corresponding to the input bytes after being clamped and
|
||||
/// reduced mod 2^255 - 19
|
||||
fn clamp_and_reduce(bytes: &[u8]) -> [u8; 32] {
|
||||
assert_eq!(bytes.len(), 32);
|
||||
Scalar::from_bytes_mod_order(clamp_integer(bytes.try_into().unwrap())).to_bytes()
|
||||
}
|
||||
|
||||
/// Tests that X25519 Diffie-Hellman works when using keys converted from Ed25519.
|
||||
// TODO: generate test vectors using another implementation of Ed25519->X25519
|
||||
#[test]
|
||||
fn ed25519_to_x25519_dh() {
|
||||
// Keys from RFC8032 test vectors (from section 7.1)
|
||||
let ed25519_secret_key_a =
|
||||
hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
|
||||
let ed25519_secret_key_b =
|
||||
hex!("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb");
|
||||
|
||||
let ed25519_signing_key_a = SigningKey::from_bytes(&ed25519_secret_key_a);
|
||||
let ed25519_signing_key_b = SigningKey::from_bytes(&ed25519_secret_key_b);
|
||||
|
||||
let scalar_a = ed25519_signing_key_a.to_scalar();
|
||||
let scalar_b = ed25519_signing_key_b.to_scalar();
|
||||
|
||||
// Compare the scalar bytes to the first 32 bytes of SHA-512(secret_key). We have to clamp and
|
||||
// reduce the SHA-512 output because that's what the spec does before using the scalars for
|
||||
// anything.
|
||||
assert_eq!(
|
||||
scalar_a.to_bytes(),
|
||||
clamp_and_reduce(&Sha512::digest(ed25519_secret_key_a)[..32]),
|
||||
);
|
||||
assert_eq!(
|
||||
scalar_b.to_bytes(),
|
||||
clamp_and_reduce(&Sha512::digest(ed25519_secret_key_b)[..32]),
|
||||
);
|
||||
|
||||
let x25519_public_key_a = ed25519_signing_key_a.verifying_key().to_montgomery();
|
||||
let x25519_public_key_b = ed25519_signing_key_b.verifying_key().to_montgomery();
|
||||
|
||||
assert_eq!(
|
||||
x25519_public_key_a.to_bytes(),
|
||||
hex!("d85e07ec22b0ad881537c2f44d662d1a143cf830c57aca4305d85c7a90f6b62e")
|
||||
);
|
||||
assert_eq!(
|
||||
x25519_public_key_b.to_bytes(),
|
||||
hex!("25c704c594b88afc00a76b69d1ed2b984d7e22550f3ed0802d04fbcd07d38d47")
|
||||
);
|
||||
|
||||
let expected_shared_secret =
|
||||
hex!("5166f24a6918368e2af831a4affadd97af0ac326bdf143596c045967cc00230e");
|
||||
|
||||
assert_eq!(
|
||||
(x25519_public_key_a * scalar_b).to_bytes(),
|
||||
expected_shared_secret
|
||||
);
|
||||
assert_eq!(
|
||||
(x25519_public_key_b * scalar_a).to_bytes(),
|
||||
expected_shared_secret
|
||||
);
|
||||
}
|
||||
65
src/backend/mod.rs
Normal file
65
src/backend/mod.rs
Normal file
@ -0,0 +1,65 @@
|
||||
// -*- 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>
|
||||
|
||||
//! 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.
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "u32_backend",
|
||||
feature = "u64_backend",
|
||||
feature = "fiat_u32_backend",
|
||||
feature = "fiat_u64_backend",
|
||||
feature = "simd_backend",
|
||||
)))]
|
||||
compile_error!(
|
||||
"no curve25519-dalek backend cargo feature enabled! \
|
||||
please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend, simd_backend"
|
||||
);
|
||||
|
||||
pub mod serial;
|
||||
|
||||
#[cfg(any(
|
||||
all(
|
||||
feature = "simd_backend",
|
||||
any(target_feature = "avx2", target_feature = "avx512ifma")
|
||||
),
|
||||
all(feature = "nightly", rustdoc)
|
||||
))]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
doc(cfg(any(all(
|
||||
feature = "simd_backend",
|
||||
any(target_feature = "avx2", target_feature = "avx512ifma")
|
||||
))))
|
||||
)]
|
||||
pub mod vector;
|
||||
@ -129,14 +129,13 @@ use core::ops::{Add, Neg, Sub};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::constants;
|
||||
use constants;
|
||||
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::field::FieldElement;
|
||||
use crate::traits::ValidityCheck;
|
||||
use edwards::EdwardsPoint;
|
||||
use field::FieldElement;
|
||||
use traits::ValidityCheck;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Internal point representations
|
||||
@ -149,7 +148,6 @@ use crate::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,
|
||||
@ -182,12 +180,11 @@ 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();
|
||||
@ -202,15 +199,13 @@ 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();
|
||||
@ -224,25 +219,25 @@ impl Zeroize for ProjectiveNielsPoint {
|
||||
// Constructors
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
use crate::traits::Identity;
|
||||
use 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -255,10 +250,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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,7 +330,7 @@ impl ProjectivePoint {
|
||||
/// \\( \mathbb P\^3 \\) model.
|
||||
///
|
||||
/// This costs \\(3 \mathrm M + 1 \mathrm S\\).
|
||||
pub fn as_extended(&self) -> EdwardsPoint {
|
||||
pub fn to_extended(&self) -> EdwardsPoint {
|
||||
EdwardsPoint {
|
||||
X: &self.X * &self.Z,
|
||||
Y: &self.Y * &self.Z,
|
||||
@ -350,7 +345,7 @@ impl CompletedPoint {
|
||||
/// \\) model to the \\( \mathbb P\^2 \\) model.
|
||||
///
|
||||
/// This costs \\(3 \mathrm M \\).
|
||||
pub fn as_projective(&self) -> ProjectivePoint {
|
||||
pub fn to_projective(&self) -> ProjectivePoint {
|
||||
ProjectivePoint {
|
||||
X: &self.X * &self.T,
|
||||
Y: &self.Y * &self.Z,
|
||||
@ -362,7 +357,7 @@ impl CompletedPoint {
|
||||
/// \\) model to the \\( \mathbb P\^3 \\) model.
|
||||
///
|
||||
/// This costs \\(4 \mathrm M \\).
|
||||
pub fn as_extended(&self) -> EdwardsPoint {
|
||||
pub fn to_extended(&self) -> EdwardsPoint {
|
||||
EdwardsPoint {
|
||||
X: &self.X * &self.T,
|
||||
Y: &self.Y * &self.Z,
|
||||
@ -378,21 +373,20 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -412,19 +406,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,19 +428,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -456,18 +450,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -477,18 +471,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -501,11 +495,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -514,10 +508,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,31 +522,22 @@ 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
|
||||
)
|
||||
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
|
||||
)
|
||||
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
|
||||
)
|
||||
write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
|
||||
&self.y_plus_x, &self.y_minus_x, &self.xy2d)
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,3 +547,5 @@ impl Debug for ProjectiveNielsPoint {
|
||||
&self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ use core::ops::{Sub, SubAssign};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use fiat_crypto::curve25519_32::*;
|
||||
@ -63,7 +62,6 @@ impl Debug for FieldElement2625 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement2625 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -179,22 +177,30 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
}
|
||||
|
||||
impl FieldElement2625 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement2625 = FieldElement2625([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625([
|
||||
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);
|
||||
@ -227,10 +233,10 @@ impl FieldElement2625 {
|
||||
|
||||
/// Serialize this `FieldElement51` to a 32-byte array. The
|
||||
/// encoding is canonical.
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
let mut bytes = [0u8; 32];
|
||||
fiat_25519_to_bytes(&mut bytes, &self.0);
|
||||
bytes
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// Compute `self^2`.
|
||||
@ -23,7 +23,6 @@ use core::ops::{Sub, SubAssign};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use fiat_crypto::curve25519_64::*;
|
||||
@ -52,7 +51,6 @@ impl Debug for FieldElement51 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement51 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -158,18 +156,26 @@ impl ConditionallySelectable for FieldElement51 {
|
||||
}
|
||||
|
||||
impl FieldElement51 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement51 = FieldElement51([0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement51 = FieldElement51([1, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51([
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
]);
|
||||
/// 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)]
|
||||
@ -203,10 +209,10 @@ impl FieldElement51 {
|
||||
|
||||
/// Serialize this `FieldElement51` to a 32-byte array. The
|
||||
/// encoding is canonical.
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
let mut bytes = [0u8; 32];
|
||||
fiat_25519_to_bytes(&mut bytes, &self.0);
|
||||
bytes
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// Given `k > 0`, return `self^(2^k)`.
|
||||
55
src/backend/serial/mod.rs
Normal file
55
src/backend/serial/mod.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// -*- 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`](self::curve_models) and
|
||||
//! [`scalar_mul`](self::scalar_mul) documentation for more
|
||||
//! information.
|
||||
//!
|
||||
//! When the vector backend is enabled, the field and scalar
|
||||
//! implementations are still used for non-vectorized operations.
|
||||
//!
|
||||
//! Note: at this time the `u32` and `u64` backends cannot be built
|
||||
//! together.
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "u32_backend",
|
||||
feature = "u64_backend",
|
||||
feature = "fiat_u32_backend",
|
||||
feature = "fiat_u64_backend"
|
||||
)))]
|
||||
compile_error!(
|
||||
"no curve25519-dalek backend cargo feature enabled! \
|
||||
please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend"
|
||||
);
|
||||
|
||||
#[cfg(feature = "u32_backend")]
|
||||
pub mod u32;
|
||||
|
||||
#[cfg(feature = "u64_backend")]
|
||||
pub mod u64;
|
||||
|
||||
#[cfg(feature = "fiat_u32_backend")]
|
||||
pub mod fiat_u32;
|
||||
|
||||
#[cfg(feature = "fiat_u64_backend")]
|
||||
pub mod fiat_u64;
|
||||
|
||||
pub mod curve_models;
|
||||
|
||||
#[cfg(not(all(
|
||||
feature = "simd_backend",
|
||||
any(target_feature = "avx2", target_feature = "avx512ifma")
|
||||
)))]
|
||||
pub mod scalar_mul;
|
||||
@ -17,10 +17,8 @@
|
||||
//! 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 crate::edwards::EdwardsPoint;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::VartimeMultiscalarMul;
|
||||
use edwards::EdwardsPoint;
|
||||
use scalar::Scalar;
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
/// Implements a version of Pippenger's algorithm.
|
||||
///
|
||||
@ -61,6 +61,7 @@ use crate::traits::VartimeMultiscalarMul;
|
||||
/// 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;
|
||||
|
||||
@ -70,7 +71,7 @@ impl VartimeMultiscalarMul for Pippenger {
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||
{
|
||||
use crate::traits::Identity;
|
||||
use traits::Identity;
|
||||
|
||||
let mut scalars = scalars.into_iter();
|
||||
let size = scalars.by_ref().size_hint().0;
|
||||
@ -92,11 +93,12 @@ 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().as_radix_2w(w));
|
||||
let scalars = scalars
|
||||
.map(|s| s.borrow().to_radix_2w(w));
|
||||
|
||||
let points = points
|
||||
.into_iter()
|
||||
.map(|p| p.map(|P| P.as_projective_niels()));
|
||||
.map(|p| p.map(|P| P.to_projective_niels()));
|
||||
|
||||
let scalars_points = scalars
|
||||
.zip(points)
|
||||
@ -111,8 +113,8 @@ impl VartimeMultiscalarMul for Pippenger {
|
||||
|
||||
let mut columns = (0..digits_count).rev().map(|digit_index| {
|
||||
// Clear the buckets when processing another digit.
|
||||
for bucket in &mut buckets {
|
||||
*bucket = EdwardsPoint::identity();
|
||||
for i in 0..buckets_count {
|
||||
buckets[i] = EdwardsPoint::identity();
|
||||
}
|
||||
|
||||
// Iterate over pairs of (point, scalar)
|
||||
@ -122,16 +124,12 @@ 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;
|
||||
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 => {}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,15 +155,18 @@ impl VartimeMultiscalarMul for Pippenger {
|
||||
// `unwrap()` always succeeds because we know we have more than zero digits.
|
||||
let hi_column = columns.next().unwrap();
|
||||
|
||||
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 crate::constants;
|
||||
use crate::scalar::Scalar;
|
||||
use constants;
|
||||
use scalar::Scalar;
|
||||
|
||||
#[test]
|
||||
fn test_vartime_pippenger() {
|
||||
@ -195,7 +196,7 @@ mod test {
|
||||
|
||||
assert_eq!(subject.compress(), control.compress());
|
||||
|
||||
n /= 2;
|
||||
n = n / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,21 +11,20 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::backend::serial::curve_models::{
|
||||
use backend::serial::curve_models::{
|
||||
AffineNielsPoint, CompletedPoint, ProjectiveNielsPoint, ProjectivePoint,
|
||||
};
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::Identity;
|
||||
use crate::traits::VartimePrecomputedMultiscalarMul;
|
||||
use crate::window::{NafLookupTable5, NafLookupTable8};
|
||||
use edwards::EdwardsPoint;
|
||||
use scalar::Scalar;
|
||||
use traits::Identity;
|
||||
use traits::VartimePrecomputedMultiscalarMul;
|
||||
use window::{NafLookupTable5, NafLookupTable8};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub struct VartimePrecomputedStraus {
|
||||
static_lookup_tables: Vec<NafLookupTable8<AffineNielsPoint>>,
|
||||
}
|
||||
@ -87,34 +86,25 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
|
||||
|
||||
for i in 0..dp {
|
||||
let t_ij = dynamic_nafs[i][j];
|
||||
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 => {}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[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.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 => {}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
S = R.as_projective();
|
||||
S = R.to_projective();
|
||||
}
|
||||
|
||||
Some(S.as_extended())
|
||||
Some(S.to_extended())
|
||||
}
|
||||
}
|
||||
@ -13,15 +13,15 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::MultiscalarMul;
|
||||
use crate::traits::VartimeMultiscalarMul;
|
||||
use edwards::EdwardsPoint;
|
||||
use scalar::Scalar;
|
||||
use traits::MultiscalarMul;
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
|
||||
/// Perform multiscalar multiplication by the interleaved window
|
||||
/// method, also known as Straus' method (since it was apparently
|
||||
@ -107,9 +107,11 @@ impl MultiscalarMul for Straus {
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
use crate::backend::serial::curve_models::ProjectiveNielsPoint;
|
||||
use crate::traits::Identity;
|
||||
use crate::window::LookupTable;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use backend::serial::curve_models::ProjectiveNielsPoint;
|
||||
use window::LookupTable;
|
||||
use traits::Identity;
|
||||
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
@ -119,11 +121,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.
|
||||
#[cfg_attr(not(feature = "zeroize"), allow(unused_mut))]
|
||||
let mut scalar_digits: Vec<_> = scalars
|
||||
let scalar_digits_vec: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|s| s.borrow().as_radix_16())
|
||||
.map(|s| s.borrow().to_radix_16())
|
||||
.collect();
|
||||
let scalar_digits = Zeroizing::new(scalar_digits_vec);
|
||||
|
||||
let mut Q = EdwardsPoint::identity();
|
||||
for j in (0..64).rev() {
|
||||
@ -133,13 +135,10 @@ 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).as_extended();
|
||||
Q = (&Q + &R_i).to_extended();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
zeroize::Zeroize::zeroize(&mut scalar_digits);
|
||||
|
||||
Q
|
||||
}
|
||||
}
|
||||
@ -162,11 +161,9 @@ impl VartimeMultiscalarMul for Straus {
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
||||
{
|
||||
use crate::backend::serial::curve_models::{
|
||||
CompletedPoint, ProjectiveNielsPoint, ProjectivePoint,
|
||||
};
|
||||
use crate::traits::Identity;
|
||||
use crate::window::NafLookupTable5;
|
||||
use backend::serial::curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
||||
use window::NafLookupTable5;
|
||||
use traits::Identity;
|
||||
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
@ -184,18 +181,16 @@ impl VartimeMultiscalarMul for Straus {
|
||||
let mut t: CompletedPoint = r.double();
|
||||
|
||||
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
||||
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 => {}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
r = t.as_projective();
|
||||
r = t.to_projective();
|
||||
}
|
||||
|
||||
Some(r.as_extended())
|
||||
Some(r.to_extended())
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::backend::serial::curve_models::ProjectiveNielsPoint;
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::Identity;
|
||||
use crate::window::LookupTable;
|
||||
use traits::Identity;
|
||||
use scalar::Scalar;
|
||||
use edwards::EdwardsPoint;
|
||||
use backend::serial::curve_models::ProjectiveNielsPoint;
|
||||
use 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);
|
||||
@ -16,8 +15,7 @@ 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`.
|
||||
// This decomposition requires s < 2^255, which is guaranteed by Scalar invariant #1.
|
||||
let scalar_digits = scalar.as_radix_16();
|
||||
let scalar_digits = scalar.to_radix_16();
|
||||
// Compute s*P as
|
||||
//
|
||||
// s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63)
|
||||
@ -32,17 +30,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.as_projective(); // tmp2 = (prev) in P2 coords
|
||||
tmp2 = tmp1.to_projective(); // tmp2 = (prev) in P2 coords
|
||||
tmp1 = tmp2.double(); // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2 = tmp1.as_projective(); // tmp2 = 2*(prev) in P2 coords
|
||||
tmp2 = tmp1.to_projective(); // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1 = tmp2.double(); // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2 = tmp1.as_projective(); // tmp2 = 4*(prev) in P2 coords
|
||||
tmp2 = tmp1.to_projective(); // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1 = tmp2.double(); // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2 = tmp1.as_projective(); // tmp2 = 8*(prev) in P2 coords
|
||||
tmp2 = tmp1.to_projective(); // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1 = tmp2.double(); // tmp1 = 16*(prev) in P1xP1 coords
|
||||
tmp3 = tmp1.as_extended(); // tmp3 = 16*(prev) in P3 coords
|
||||
tmp3 = tmp1.to_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.as_extended()
|
||||
tmp1.to_extended()
|
||||
}
|
||||
62
src/backend/serial/scalar_mul/vartime_double_base.rs
Normal file
62
src/backend/serial/scalar_mul/vartime_double_base.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// -*- 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 constants;
|
||||
use traits::Identity;
|
||||
use scalar::Scalar;
|
||||
use edwards::EdwardsPoint;
|
||||
use backend::serial::curve_models::{ProjectiveNielsPoint, ProjectivePoint};
|
||||
use 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);
|
||||
let b_naf = b.non_adjacent_form(8);
|
||||
|
||||
// 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);
|
||||
let table_B = &constants::AFFINE_ODD_MULTIPLES_OF_BASEPOINT;
|
||||
|
||||
let mut r = ProjectivePoint::identity();
|
||||
loop {
|
||||
let mut t = r.double();
|
||||
|
||||
if a_naf[i] > 0 {
|
||||
t = &t.to_extended() + &table_A.select(a_naf[i] as usize);
|
||||
} else if a_naf[i] < 0 {
|
||||
t = &t.to_extended() - &table_A.select(-a_naf[i] as usize);
|
||||
}
|
||||
|
||||
if b_naf[i] > 0 {
|
||||
t = &t.to_extended() + &table_B.select(b_naf[i] as usize);
|
||||
} else if b_naf[i] < 0 {
|
||||
t = &t.to_extended() - &table_B.select(-b_naf[i] as usize);
|
||||
}
|
||||
|
||||
r = t.to_projective();
|
||||
|
||||
if i == 0 {
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
r.to_extended()
|
||||
}
|
||||
4789
src/backend/serial/u32/constants.rs
Normal file
4789
src/backend/serial/u32/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,6 @@ 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
|
||||
@ -51,7 +50,7 @@ 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 {
|
||||
@ -59,7 +58,6 @@ impl Debug for FieldElement2625 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement2625 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -100,8 +98,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,19 +120,14 @@ 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:
|
||||
//
|
||||
@ -185,16 +177,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]?
|
||||
//
|
||||
@ -284,16 +276,6 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
}
|
||||
|
||||
impl FieldElement2625 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement2625 = FieldElement2625([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625([
|
||||
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.
|
||||
@ -312,9 +294,27 @@ 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,7 +328,6 @@ 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;
|
||||
@ -340,11 +339,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;
|
||||
}
|
||||
}
|
||||
@ -361,7 +360,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,
|
||||
@ -372,16 +371,8 @@ 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,
|
||||
])
|
||||
}
|
||||
|
||||
@ -396,8 +387,7 @@ impl FieldElement2625 {
|
||||
/// encoding of every field element should decode, re-encode to
|
||||
/// the canonical encoding, and check that the input was
|
||||
/// canonical.
|
||||
#[rustfmt::skip] // keep alignment of h[*] values
|
||||
pub fn from_bytes(data: &[u8; 32]) -> FieldElement2625 {
|
||||
pub fn from_bytes(data: &[u8; 32]) -> FieldElement2625 { //FeFromBytes
|
||||
#[inline]
|
||||
fn load3(b: &[u8]) -> u64 {
|
||||
(b[0] as u64) | ((b[1] as u64) << 8) | ((b[2] as u64) << 16)
|
||||
@ -426,24 +416,15 @@ impl FieldElement2625 {
|
||||
|
||||
/// Serialize this `FieldElement51` to a 32-byte array. The
|
||||
/// encoding is canonical.
|
||||
#[allow(clippy::identity_op)]
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_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.
|
||||
//
|
||||
@ -465,40 +446,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] &= LOW_26_BITS;
|
||||
h[0] = h[0] & LOW_26_BITS;
|
||||
h[2] += h[1] >> 25;
|
||||
h[1] &= LOW_25_BITS;
|
||||
h[1] = h[1] & LOW_25_BITS;
|
||||
h[3] += h[2] >> 26;
|
||||
h[2] &= LOW_26_BITS;
|
||||
h[2] = h[2] & LOW_26_BITS;
|
||||
h[4] += h[3] >> 25;
|
||||
h[3] &= LOW_25_BITS;
|
||||
h[3] = h[3] & LOW_25_BITS;
|
||||
h[5] += h[4] >> 26;
|
||||
h[4] &= LOW_26_BITS;
|
||||
h[4] = h[4] & LOW_26_BITS;
|
||||
h[6] += h[5] >> 25;
|
||||
h[5] &= LOW_25_BITS;
|
||||
h[5] = h[5] & LOW_25_BITS;
|
||||
h[7] += h[6] >> 26;
|
||||
h[6] &= LOW_26_BITS;
|
||||
h[6] = h[6] & LOW_26_BITS;
|
||||
h[8] += h[7] >> 25;
|
||||
h[7] &= LOW_25_BITS;
|
||||
h[7] = h[7] & LOW_25_BITS;
|
||||
h[9] += h[8] >> 26;
|
||||
h[8] &= LOW_26_BITS;
|
||||
h[8] = 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] &= LOW_25_BITS;
|
||||
debug_assert!( (h[9] >> 25) == 0 || (h[9] >> 25) == 1);
|
||||
h[9] = h[9] & LOW_25_BITS;
|
||||
|
||||
let mut s = [0u8; 32];
|
||||
s[0] = (h[0] >> 0) as u8;
|
||||
@ -540,46 +521,43 @@ 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
|
||||
}
|
||||
@ -592,8 +570,8 @@ impl FieldElement2625 {
|
||||
/// Compute `2*self^2`.
|
||||
pub fn square2(&self) -> FieldElement2625 {
|
||||
let mut coeffs = self.square_inner();
|
||||
for coeff in &mut coeffs {
|
||||
*coeff += *coeff;
|
||||
for i in 0..self.0.len() {
|
||||
coeffs[i] += coeffs[i];
|
||||
}
|
||||
FieldElement2625::reduce(coeffs)
|
||||
}
|
||||
@ -13,14 +13,12 @@
|
||||
use core::fmt::Debug;
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::constants;
|
||||
use constants;
|
||||
|
||||
/// The `Scalar29` struct represents an element in \\(\mathbb{Z} / \ell\mathbb{Z}\\) as 9 29-bit
|
||||
/// limbs
|
||||
#[derive(Copy, Clone)]
|
||||
/// The `Scalar29` struct represents an element in ℤ/lℤ as 9 29-bit limbs
|
||||
#[derive(Copy,Clone)]
|
||||
pub struct Scalar29(pub [u32; 9]);
|
||||
|
||||
impl Debug for Scalar29 {
|
||||
@ -29,7 +27,6 @@ impl Debug for Scalar29 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for Scalar29 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -56,11 +53,12 @@ fn m(x: u32, y: u32) -> u64 {
|
||||
}
|
||||
|
||||
impl Scalar29 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: Scalar29 = Scalar29([0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// Return the zero scalar.
|
||||
pub fn 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 {
|
||||
@ -71,23 +69,22 @@ 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 {
|
||||
@ -97,8 +94,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;
|
||||
@ -126,50 +123,48 @@ impl Scalar29 {
|
||||
}
|
||||
|
||||
/// Pack the limbs of this `Scalar29` into 32 bytes.
|
||||
#[rustfmt::skip] // keep alignment of s[*] calculations
|
||||
#[allow(clippy::identity_op)]
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_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
|
||||
@ -185,7 +180,7 @@ impl Scalar29 {
|
||||
|
||||
/// Compute `a - b` (mod l).
|
||||
pub fn sub(a: &Scalar29, b: &Scalar29) -> Scalar29 {
|
||||
let mut difference = Scalar29::ZERO;
|
||||
let mut difference = Scalar29::zero();
|
||||
let mask = (1u32 << 29) - 1;
|
||||
|
||||
// a - b
|
||||
@ -210,27 +205,26 @@ 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
|
||||
@ -241,70 +235,68 @@ 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)]
|
||||
@ -377,12 +369,11 @@ impl Scalar29 {
|
||||
|
||||
/// Puts a Scalar29 in to Montgomery form, i.e. computes `a*R (mod l)`
|
||||
#[inline(never)]
|
||||
pub fn as_montgomery(&self) -> Scalar29 {
|
||||
pub fn to_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 {
|
||||
@ -402,65 +393,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() {
|
||||
@ -513,7 +504,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]);
|
||||
}
|
||||
7759
src/backend/serial/u64/constants.rs
Normal file
7759
src/backend/serial/u64/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@ 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
|
||||
@ -40,7 +39,7 @@ 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 {
|
||||
@ -48,7 +47,6 @@ impl Debug for FieldElement51 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement51 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -110,8 +108,6 @@ 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.
|
||||
@ -141,11 +137,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
|
||||
//
|
||||
@ -202,7 +198,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] += carry * 19;
|
||||
out[0] = out[0] + carry * 19;
|
||||
|
||||
// Now out[1] < 2^51 + 2^(64 -51) = 2^51 + 2^13 < 2^(51 + epsilon).
|
||||
out[1] += out[0] >> 51;
|
||||
@ -255,19 +251,6 @@ impl ConditionallySelectable for FieldElement51 {
|
||||
}
|
||||
|
||||
impl FieldElement51 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement51 = FieldElement51([0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement51 = FieldElement51([1, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51([
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
]);
|
||||
|
||||
/// Invert the sign of this field element
|
||||
pub fn negate(&mut self) {
|
||||
// See commentary in the Sub impl
|
||||
@ -281,6 +264,21 @@ 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 {
|
||||
@ -330,7 +328,6 @@ 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)
|
||||
@ -360,8 +357,7 @@ impl FieldElement51 {
|
||||
|
||||
/// Serialize this `FieldElement51` to a 32-byte array. The
|
||||
/// encoding is canonical.
|
||||
#[rustfmt::skip] // keep alignment of s[*] calculations
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
// Let h = limbs[0] + limbs[1]*2^51 + ... + limbs[4]*2^204.
|
||||
//
|
||||
// Write h = pq + r with 0 <= r < p.
|
||||
@ -388,56 +384,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] &= 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;
|
||||
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;
|
||||
// ... but instead of carrying (limbs[4] >> 51) = 2^255q
|
||||
// into another limb, discard it, subtracting the value
|
||||
limbs[4] &= low_51_bit_mask;
|
||||
limbs[4] = 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);
|
||||
@ -446,16 +442,13 @@ 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;
|
||||
|
||||
@ -537,7 +530,7 @@ impl FieldElement51 {
|
||||
// a[0] + carry * 19 < 2^51 + 19 * 2^59.33 < 2^63.58
|
||||
//
|
||||
// and there is no overflow.
|
||||
a[0] += carry * 19;
|
||||
a[0] = a[0] + carry * 19;
|
||||
|
||||
// Now a[1] < 2^51 + 2^(64 -51) = 2^51 + 2^13 < 2^(51 + epsilon).
|
||||
a[1] += a[0] >> 51;
|
||||
@ -545,7 +538,7 @@ impl FieldElement51 {
|
||||
|
||||
// Now all a[i] < 2^(51 + epsilon) and a = self^(2^k).
|
||||
|
||||
k -= 1;
|
||||
k = k - 1;
|
||||
if k == 0 {
|
||||
break;
|
||||
}
|
||||
@ -14,14 +14,13 @@
|
||||
use core::fmt::Debug;
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::constants;
|
||||
use 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 {
|
||||
@ -30,7 +29,6 @@ impl Debug for Scalar52 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for Scalar52 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
@ -57,11 +55,12 @@ fn m(x: u64, y: u64) -> u128 {
|
||||
}
|
||||
|
||||
impl Scalar52 {
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: Scalar52 = Scalar52([0, 0, 0, 0, 0]);
|
||||
/// Return the zero scalar
|
||||
pub fn 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 {
|
||||
@ -72,19 +71,18 @@ 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 {
|
||||
@ -94,19 +92,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
|
||||
@ -115,21 +113,19 @@ impl Scalar52 {
|
||||
}
|
||||
|
||||
/// Pack the limbs of this `Scalar52` into 32 bytes
|
||||
#[rustfmt::skip] // keep alignment of s[*] calculations
|
||||
#[allow(clippy::identity_op)]
|
||||
pub fn as_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_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;
|
||||
@ -158,7 +154,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
|
||||
@ -174,7 +170,7 @@ impl Scalar52 {
|
||||
|
||||
/// Compute `a - b` (mod l)
|
||||
pub fn sub(a: &Scalar52, b: &Scalar52) -> Scalar52 {
|
||||
let mut difference = Scalar52::ZERO;
|
||||
let mut difference = Scalar52::zero();
|
||||
let mask = (1u64 << 52) - 1;
|
||||
|
||||
// a - b
|
||||
@ -197,56 +193,53 @@ 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)]
|
||||
@ -260,20 +253,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)
|
||||
@ -305,12 +298,11 @@ impl Scalar52 {
|
||||
|
||||
/// Puts a Scalar52 in to Montgomery form, i.e. computes `a*R (mod l)`
|
||||
#[inline(never)]
|
||||
pub fn as_montgomery(&self) -> Scalar52 {
|
||||
pub fn to_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];
|
||||
@ -321,6 +313,7 @@ impl Scalar52 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@ -331,95 +324,55 @@ 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() {
|
||||
@ -472,7 +425,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]);
|
||||
}
|
||||
3428
src/backend/vector/avx2/constants.rs
Normal file
3428
src/backend/vector/avx2/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -41,15 +41,10 @@ use core::ops::{Add, Neg, Sub};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
use edwards;
|
||||
use window::{LookupTable, NafLookupTable5, NafLookupTable8};
|
||||
|
||||
use crate::edwards;
|
||||
use crate::window::{LookupTable, NafLookupTable5};
|
||||
|
||||
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
|
||||
use crate::window::NafLookupTable8;
|
||||
|
||||
use crate::traits::Identity;
|
||||
use traits::Identity;
|
||||
|
||||
use super::constants;
|
||||
use super::field::{FieldElement2625x4, Lanes, Shuffle};
|
||||
@ -64,14 +59,12 @@ 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();
|
||||
@ -84,7 +77,6 @@ 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))
|
||||
@ -95,21 +87,18 @@ 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 {
|
||||
@ -145,7 +134,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);
|
||||
|
||||
@ -195,7 +184,6 @@ 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;
|
||||
@ -214,21 +202,18 @@ 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))
|
||||
@ -239,7 +224,6 @@ impl ConditionallySelectable for CachedPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> Neg for &'a CachedPoint {
|
||||
type Output = CachedPoint;
|
||||
/// Lazily negate the point.
|
||||
@ -254,7 +238,6 @@ impl<'a> Neg for &'a CachedPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
@ -292,7 +275,6 @@ impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
@ -306,7 +288,6 @@ impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
|
||||
fn from(point: &'a edwards::EdwardsPoint) -> Self {
|
||||
let P = ExtendedPoint::from(*point);
|
||||
@ -318,7 +299,6 @@ impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
|
||||
fn from(point: &'a edwards::EdwardsPoint) -> Self {
|
||||
let A = ExtendedPoint::from(*point);
|
||||
@ -332,8 +312,6 @@ impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "precomputed-tables", feature = "alloc"))]
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
|
||||
fn from(point: &'a edwards::EdwardsPoint) -> Self {
|
||||
let A = ExtendedPoint::from(*point);
|
||||
@ -347,21 +325,19 @@ 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 crate::backend::serial::u64::field::FieldElement51;
|
||||
use 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.as_bytes());
|
||||
println!("{} = {:?}", stringify!($x), $x.to_bytes());
|
||||
};
|
||||
}
|
||||
|
||||
@ -444,8 +420,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() {
|
||||
use crate::constants;
|
||||
use crate::scalar::Scalar;
|
||||
use constants;
|
||||
use scalar::Scalar;
|
||||
|
||||
println!("Testing id +- id");
|
||||
let P = edwards::EdwardsPoint::identity();
|
||||
@ -464,7 +440,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);
|
||||
}
|
||||
|
||||
@ -473,7 +449,7 @@ mod test {
|
||||
|
||||
macro_rules! print_var {
|
||||
($x:ident) => {
|
||||
println!("{} = {:?}", stringify!($x), $x.as_bytes());
|
||||
println!("{} = {:?}", stringify!($x), $x.to_bytes());
|
||||
};
|
||||
}
|
||||
|
||||
@ -531,8 +507,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn vector_doubling_vs_serial_doubling_vs_edwards_extendedpoint() {
|
||||
use crate::constants;
|
||||
use crate::scalar::Scalar;
|
||||
use constants;
|
||||
use scalar::Scalar;
|
||||
|
||||
println!("Testing [2]id");
|
||||
let P = edwards::EdwardsPoint::identity();
|
||||
@ -543,18 +519,16 @@ 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 crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE;
|
||||
use crate::constants;
|
||||
use constants;
|
||||
use backend::vector::avx2::constants::{BASEPOINT_ODD_LOOKUP_TABLE};
|
||||
|
||||
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,15 +40,11 @@ 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 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;
|
||||
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;
|
||||
|
||||
/// Unpack 32-bit lanes into 64-bit lanes:
|
||||
/// ```ascii,no_run
|
||||
@ -59,17 +55,16 @@ use curve25519_dalek_derive::unsafe_target_feature;
|
||||
/// (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 = u32x8::splat(0);
|
||||
let zero = i32x8::new(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
unsafe {
|
||||
use core::arch::x86_64::_mm256_unpackhi_epi32;
|
||||
use core::arch::x86_64::_mm256_unpacklo_epi32;
|
||||
a = _mm256_unpacklo_epi32(src.into(), zero.into()).into();
|
||||
b = _mm256_unpackhi_epi32(src.into(), zero.into()).into();
|
||||
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, b)
|
||||
}
|
||||
@ -83,7 +78,6 @@ 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 {
|
||||
@ -93,13 +87,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(), 0b11_01_10_00);
|
||||
let y_shuffled = _mm256_shuffle_epi32(y.into(), 0b10_00_11_01);
|
||||
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);
|
||||
|
||||
// x' = (a0, b0, 0, 0, c0, d0, 0, 0)
|
||||
// y' = ( 0, 0, a1, b1, 0, 0, c1, d1)
|
||||
|
||||
_mm256_blend_epi32(x_shuffled, y_shuffled, 0b11001100).into()
|
||||
return _mm256_blend_epi32(x_shuffled, y_shuffled, 0b11001100).into_bits();
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +103,6 @@ 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,
|
||||
@ -127,7 +120,6 @@ 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,
|
||||
@ -155,7 +147,6 @@ 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,
|
||||
@ -173,7 +164,11 @@ 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]);
|
||||
@ -184,24 +179,20 @@ 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);
|
||||
@ -239,7 +230,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(), c.into()).into()
|
||||
_mm256_permutevar8x32_epi32(x.into_bits(), c.into_bits()).into_bits()
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,29 +276,37 @@ 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(), y.into(), C_LANES as i32).into(),
|
||||
Lanes::D => _mm256_blend_epi32(x.into(), y.into(), D_LANES as i32).into(),
|
||||
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::AD => {
|
||||
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | D_LANES) as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | D_LANES) as i32)
|
||||
.into_bits()
|
||||
}
|
||||
Lanes::AB => {
|
||||
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | B_LANES) as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | B_LANES) as i32)
|
||||
.into_bits()
|
||||
}
|
||||
Lanes::AC => {
|
||||
_mm256_blend_epi32(x.into(), y.into(), (A_LANES | C_LANES) as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (A_LANES | C_LANES) as i32)
|
||||
.into_bits()
|
||||
}
|
||||
Lanes::CD => {
|
||||
_mm256_blend_epi32(x.into(), y.into(), (C_LANES | D_LANES) as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (C_LANES | D_LANES) as i32)
|
||||
.into_bits()
|
||||
}
|
||||
Lanes::BC => {
|
||||
_mm256_blend_epi32(x.into(), y.into(), (B_LANES | C_LANES) as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), y.into_bits(), (B_LANES | C_LANES) as i32)
|
||||
.into_bits()
|
||||
}
|
||||
Lanes::ABCD => _mm256_blend_epi32(
|
||||
x.into(),
|
||||
y.into(),
|
||||
x.into_bits(),
|
||||
y.into_bits(),
|
||||
(A_LANES | B_LANES | C_LANES | D_LANES) as i32,
|
||||
)
|
||||
.into(),
|
||||
).into_bits(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,6 +320,11 @@ 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)
|
||||
@ -331,7 +335,6 @@ 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,
|
||||
@ -340,7 +343,6 @@ 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;
|
||||
@ -410,7 +412,7 @@ impl FieldElement2625x4 {
|
||||
/// The coefficients of the result are bounded with \\( b < 0.0002 \\).
|
||||
#[inline]
|
||||
pub fn reduce(&self) -> FieldElement2625x4 {
|
||||
let shifts = u32x8::new(26, 26, 25, 25, 26, 26, 25, 25);
|
||||
let shifts = i32x8::new(26, 26, 25, 25, 26, 26, 25, 25);
|
||||
let masks = u32x8::new(
|
||||
(1 << 26) - 1,
|
||||
(1 << 26) - 1,
|
||||
@ -430,11 +432,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_shuffle_epi32;
|
||||
use core::arch::x86_64::_mm256_srlv_epi32;
|
||||
use core::arch::x86_64::_mm256_shuffle_epi32;
|
||||
|
||||
let c = _mm256_srlv_epi32(v.into(), shifts.into());
|
||||
_mm256_shuffle_epi32(c, 0b01_00_11_10).into()
|
||||
let c = _mm256_srlv_epi32(v.into_bits(), shifts.into_bits());
|
||||
_mm256_shuffle_epi32(c, 0b01_00_11_10).into_bits()
|
||||
}
|
||||
};
|
||||
|
||||
@ -455,7 +457,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(), v_hi.into(), 0b11_00_11_00).into()
|
||||
_mm256_blend_epi32(v_lo.into_bits(), v_hi.into_bits(), 0b11_00_11_00).into_bits()
|
||||
}
|
||||
};
|
||||
|
||||
@ -485,21 +487,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(), 0b11_01_10_00);
|
||||
let c9_spread = _mm256_shuffle_epi32(c98.into_bits(), 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());
|
||||
let c9_19_spread = _mm256_mul_epu32(c9_spread, u64x4::splat(19).into_bits());
|
||||
|
||||
// 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()
|
||||
_mm256_shuffle_epi32(c9_19_spread, 0b11_01_10_00).into_bits()
|
||||
};
|
||||
|
||||
// Add the final carryin.
|
||||
v[0] += c9_19;
|
||||
v[0] = v[0] + c9_19;
|
||||
|
||||
// Each output coefficient has exactly one carryin, which is
|
||||
// bounded by 2^11.25, so they are bounded as
|
||||
@ -517,7 +519,6 @@ 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);
|
||||
@ -528,12 +529,12 @@ impl FieldElement2625x4 {
|
||||
debug_assert!(i < 9);
|
||||
if i % 2 == 0 {
|
||||
// Even limbs have 26 bits
|
||||
z[i + 1] += z[i].shr::<26>();
|
||||
z[i] &= LOW_26_BITS;
|
||||
z[i + 1] = z[i + 1] + (z[i] >> 26);
|
||||
z[i] = z[i] & LOW_26_BITS;
|
||||
} else {
|
||||
// Odd limbs have 25 bits
|
||||
z[i + 1] += z[i].shr::<25>();
|
||||
z[i] &= LOW_25_BITS;
|
||||
z[i + 1] = z[i + 1] + (z[i] >> 25);
|
||||
z[i] = z[i] & LOW_25_BITS;
|
||||
}
|
||||
};
|
||||
|
||||
@ -555,17 +556,20 @@ 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].shr::<25>();
|
||||
z[9] &= LOW_25_BITS;
|
||||
let c = z[9] >> 25;
|
||||
z[9] = z[9] & LOW_25_BITS;
|
||||
let mut c0: u64x4 = c & LOW_26_BITS; // c0 < 2^26;
|
||||
let mut c1: u64x4 = c.shr::<26>(); // c1 < 2^(39-26) = 2^13;
|
||||
let mut c1: u64x4 = c >> 26; // c1 < 2^(39-26) = 2^13;
|
||||
|
||||
let x19 = u64x4::splat(19);
|
||||
c0 = u32x8::from(c0).mul32(u32x8::from(x19));
|
||||
c1 = u32x8::from(c1).mul32(u32x8::from(x19));
|
||||
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
|
||||
}
|
||||
|
||||
z[0] += c0; // z0 < 2^26 + 2^30.25 < 2^30.33
|
||||
z[1] += c1; // z1 < 2^25 + 2^17.25 < 2^25.0067
|
||||
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
|
||||
carry(&mut z, 0); // z0 < 2^26, z1 < 2^25.0067 + 2^4.33 = 2^25.007
|
||||
|
||||
// The output coefficients are bounded with
|
||||
@ -576,11 +580,11 @@ impl FieldElement2625x4 {
|
||||
//
|
||||
// So the packed result is bounded with b = 0.007.
|
||||
FieldElement2625x4([
|
||||
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()),
|
||||
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()),
|
||||
])
|
||||
}
|
||||
|
||||
@ -593,16 +597,17 @@ 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 {
|
||||
x.mul32(y)
|
||||
use core::arch::x86_64::_mm256_mul_epu32;
|
||||
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn m_lo(x: u32x8, y: u32x8) -> u32x8 {
|
||||
x.mul32(y).into()
|
||||
use core::arch::x86_64::_mm256_mul_epu32;
|
||||
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
|
||||
}
|
||||
|
||||
let v19 = u32x8::new(19, 0, 19, 0, 19, 0, 19, 0);
|
||||
@ -613,31 +618,31 @@ impl FieldElement2625x4 {
|
||||
let (x6, x7) = unpack_pair(self.0[3]);
|
||||
let (x8, x9) = unpack_pair(self.0[4]);
|
||||
|
||||
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 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 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)).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) ;
|
||||
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);
|
||||
|
||||
// The biggest z_i is bounded as z_i < 249*2^(51 + 2*b);
|
||||
// if b < 1.5 we get z_i < 4485585228861014016.
|
||||
@ -662,7 +667,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(), (p - x).into(), D_LANES64 as i32).into()
|
||||
_mm256_blend_epi32(x.into_bits(), (p - x).into_bits(), D_LANES64 as i32).into_bits()
|
||||
}
|
||||
};
|
||||
|
||||
@ -681,7 +686,6 @@ impl FieldElement2625x4 {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl Neg for FieldElement2625x4 {
|
||||
type Output = FieldElement2625x4;
|
||||
|
||||
@ -705,12 +709,10 @@ 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.
|
||||
@ -726,7 +728,6 @@ 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.
|
||||
@ -736,30 +737,33 @@ 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 {
|
||||
let consts = u32x8::new(scalars.0, 0, scalars.1, 0, scalars.2, 0, scalars.3, 0);
|
||||
unsafe {
|
||||
use core::arch::x86_64::_mm256_mul_epu32;
|
||||
|
||||
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 consts = u32x8::new(scalars.0, 0, scalars.1, 0, scalars.2, 0, scalars.3, 0);
|
||||
|
||||
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),
|
||||
])
|
||||
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(),
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
|
||||
type Output = FieldElement2625x4;
|
||||
/// Multiply `self` by `rhs`.
|
||||
@ -774,17 +778,17 @@ impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
|
||||
///
|
||||
/// The coefficients of the result are bounded with \\( b < 0.007 \\).
|
||||
///
|
||||
#[rustfmt::skip] // keep alignment of z* calculations
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b FieldElement2625x4) -> FieldElement2625x4 {
|
||||
#[inline(always)]
|
||||
fn m(x: u32x8, y: u32x8) -> u64x4 {
|
||||
x.mul32(y)
|
||||
use core::arch::x86_64::_mm256_mul_epu32;
|
||||
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn m_lo(x: u32x8, y: u32x8) -> u32x8 {
|
||||
x.mul32(y).into()
|
||||
use core::arch::x86_64::_mm256_mul_epu32;
|
||||
unsafe { _mm256_mul_epu32(x.into_bits(), y.into_bits()).into_bits() }
|
||||
}
|
||||
|
||||
let (x0, x1) = unpack_pair(self.0[0]);
|
||||
@ -817,16 +821,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:
|
||||
@ -870,16 +874,15 @@ 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,12 +9,13 @@
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
#![doc = include_str!("../../../../docs/avx2-notes.md")]
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
doc(include = "../../../../docs/avx2-notes.md")
|
||||
)]
|
||||
|
||||
pub(crate) mod field;
|
||||
|
||||
pub(crate) mod edwards;
|
||||
|
||||
pub(crate) mod constants;
|
||||
|
||||
pub(crate) use self::edwards::{CachedPoint, ExtendedPoint};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user