Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c6d347563 | ||
|
|
fa03398012 | ||
|
|
5312a0311e | ||
|
|
b4f9e4df92 | ||
|
|
415892acf1 | ||
|
|
56bf398d0c | ||
|
|
9252fa5c0d | ||
|
|
1efe6a93b1 | ||
|
|
cc3421a22f | ||
|
|
858c4ca8ae | ||
|
|
31ccb67050 | ||
|
|
19c7f4a5d5 | ||
|
|
a62e4a5c57 | ||
|
|
17eab3d6c1 | ||
|
|
50401ab430 | ||
|
|
4ac84dd066 | ||
|
|
ff1c309b23 | ||
|
|
ba737a3790 | ||
|
|
0b45e00ad5 | ||
|
|
a12ab4e584 | ||
|
|
7051bd8dcf | ||
|
|
ba7a073487 | ||
|
|
a2ff6ba9e4 | ||
|
|
f08bbb7f57 | ||
|
|
04f811ad21 | ||
|
|
ac51ef6ecf | ||
|
|
89aabac235 | ||
|
|
72761ca6b4 | ||
|
|
3c85f778b3 | ||
|
|
78a86f1c49 | ||
|
|
f4cd43f606 | ||
|
|
81d0756bdc | ||
|
|
cd9378e6fd | ||
|
|
8a41a29939 | ||
|
|
b92421916d | ||
|
|
598695c400 | ||
|
|
e6675c67ce | ||
|
|
0cd099a9fb | ||
|
|
76a8b2a081 | ||
|
|
533b53a0ec | ||
|
|
c157a1ed6d | ||
|
|
e94a5fe5ab | ||
|
|
9db51a6bf7 | ||
|
|
8ed1666b97 | ||
|
|
1ec4a36a80 | ||
|
|
a3a08b01ab | ||
|
|
135476c9f5 | ||
|
|
5c5a32057c | ||
|
|
c8d1d400f1 | ||
|
|
60dd3100c0 | ||
|
|
594b1f9ffe | ||
|
|
c058cd9057 | ||
|
|
8e0cef5b72 | ||
|
|
4373695c50 | ||
|
|
098658dc8b | ||
|
|
b93ace8c7f | ||
|
|
c66973c823 | ||
|
|
bf2c4eea23 | ||
|
|
6dd17b2836 | ||
|
|
42b55fd117 | ||
|
|
345364d4ec |
40
.github/workflows/curve25519-dalek.yml
vendored
40
.github/workflows/curve25519-dalek.yml
vendored
@ -3,10 +3,14 @@ name: curve25519 Rust
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
paths: 'curve25519-dalek/**'
|
||||
paths:
|
||||
- 'curve25519-dalek/**'
|
||||
- '.github/workflows/curve25519-dalek.yml'
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
paths: 'curve25519-dalek/**'
|
||||
paths:
|
||||
- 'curve25519-dalek/**'
|
||||
- '.github/workflows/curve25519-dalek.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@ -39,6 +43,31 @@ jobs:
|
||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
||||
run: cargo test --target ${{ matrix.target }}
|
||||
|
||||
# Default no_std test only tests using serial across all crates
|
||||
build-nostd-fiat:
|
||||
name: Build fiat on no_std target (thumbv7em-none-eabi)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- crate: curve25519-dalek
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: thumbv7em-none-eabi
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
# No default features build
|
||||
- name: no_std fiat / no feat ${{ matrix.crate }}
|
||||
env:
|
||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
||||
run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features
|
||||
- name: no_std fiat / cargo hack ${{ matrix.crate }}
|
||||
env:
|
||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
||||
run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,getrandom
|
||||
|
||||
test-serial:
|
||||
name: Test serial backend
|
||||
runs-on: ubuntu-latest
|
||||
@ -98,17 +127,16 @@ jobs:
|
||||
# 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
|
||||
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,group-bits --target x86_64-unknown-linux-gnu
|
||||
|
||||
msrv:
|
||||
name: Current MSRV is 1.60.0
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# First run `cargo +nightly -Z minimal-verisons check` in order to get a
|
||||
# Cargo.lock with the oldest possible deps
|
||||
# Re-resolve Cargo.lock with minimal versions
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
- run: cargo update -Z minimal-versions
|
||||
# Now check that `cargo build` works with respect to the oldest possible
|
||||
# deps and the stated MSRV
|
||||
- uses: dtolnay/rust-toolchain@1.60.0
|
||||
|
||||
5
.github/workflows/ed25519-dalek.yml
vendored
5
.github/workflows/ed25519-dalek.yml
vendored
@ -24,10 +24,9 @@ jobs:
|
||||
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
|
||||
# Re-resolve Cargo.lock with minimal versions
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
- run: cargo update -Z minimal-versions
|
||||
# Now check that `cargo build` works with respect to the oldest possible
|
||||
# deps and the stated MSRV
|
||||
- uses: dtolnay/rust-toolchain@1.60.0
|
||||
|
||||
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
|
||||
27
.github/workflows/workspace.yml
vendored
27
.github/workflows/workspace.yml
vendored
@ -57,15 +57,38 @@ jobs:
|
||||
- name: Build default (host native) bench
|
||||
run: cargo build --benches
|
||||
|
||||
# Test no_std with serial (default)
|
||||
build-nostd-serial:
|
||||
name: Build serial on no_std target (thumbv7em-none-eabi)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- crate: curve25519-dalek
|
||||
- crate: ed25519-dalek
|
||||
- crate: x25519-dalek
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: thumbv7em-none-eabi
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
# No default features build
|
||||
- name: no_std / no feat ${{ matrix.crate }}
|
||||
run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features
|
||||
- name: no_std / cargo hack ${{ matrix.crate }}
|
||||
run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,getrandom
|
||||
|
||||
clippy:
|
||||
name: Check that clippy is happy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: dtolnay/rust-toolchain@1.73.0
|
||||
with:
|
||||
components: clippy
|
||||
- run: cargo clippy --target x86_64-unknown-linux-gnu
|
||||
- run: cargo clippy --target x86_64-unknown-linux-gnu --all-features
|
||||
|
||||
rustfmt:
|
||||
name: Check formatting
|
||||
|
||||
5
.github/workflows/x25519-dalek.yml
vendored
5
.github/workflows/x25519-dalek.yml
vendored
@ -24,10 +24,9 @@ jobs:
|
||||
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
|
||||
# Re-resolve Cargo.lock with minimal versions
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo -Z minimal-versions check --no-default-features --features serde
|
||||
- run: cargo update -Z minimal-versions
|
||||
# Now check that `cargo build` works with respect to the oldest possible
|
||||
# deps and the stated MSRV
|
||||
- uses: dtolnay/rust-toolchain@1.60.0
|
||||
|
||||
@ -8,13 +8,12 @@
|
||||
# Dalek elliptic curve cryptography
|
||||
|
||||
This repo contains pure-Rust crates for elliptic curve cryptography:
|
||||
[![curve25519 Rust]()](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml)
|
||||
|
||||
| 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`](./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) |
|
||||
|
||||
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.
|
||||
|
||||
|
||||
8
curve25519-dalek-derive/CHANGELOG.md
Normal file
8
curve25519-dalek-derive/CHANGELOG.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
Entries are listed in reverse chronological order per undeprecated
|
||||
major series.
|
||||
|
||||
### 0.1.1
|
||||
|
||||
* Copied over license files from [original](https://github.com/koute/unsafe_target_feature/tree/389ae00d34cf0ff589cb8d9b38a85ae1b05ebfdc) repo
|
||||
@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
documentation = "https://docs.rs/curve25519-dalek-derive"
|
||||
license = "MIT/Apache-2.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
description = "curve25519-dalek Derives"
|
||||
|
||||
|
||||
201
curve25519-dalek-derive/LICENSE-APACHE
Normal file
201
curve25519-dalek-derive/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
23
curve25519-dalek-derive/LICENSE-MIT
Normal file
23
curve25519-dalek-derive/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@ -44,6 +44,7 @@ to build out more elaborate abstractions it starts to become painful to use.
|
||||
}
|
||||
|
||||
struct AVX;
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl Backend for AVX {
|
||||
#[target_feature(enable = "avx")]
|
||||
unsafe fn sum(xs: &[u32]) -> u32 {
|
||||
@ -53,6 +54,7 @@ to build out more elaborate abstractions it starts to become painful to use.
|
||||
}
|
||||
|
||||
struct AVX2;
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl Backend for AVX2 {
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn sum(xs: &[u32]) -> u32 {
|
||||
@ -87,6 +89,7 @@ fn func() {}
|
||||
|
||||
```rust
|
||||
// It works, but must be `unsafe`
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn func() {}
|
||||
```
|
||||
@ -95,6 +98,7 @@ unsafe fn func() {}
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
// No `unsafe` on the function itself!
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[unsafe_target_feature("avx2")]
|
||||
fn func() {}
|
||||
```
|
||||
@ -119,6 +123,7 @@ use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
struct S;
|
||||
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl core::ops::Add for S {
|
||||
type Output = S;
|
||||
@ -135,6 +140,7 @@ impl core::ops::Add for S {
|
||||
```rust
|
||||
use curve25519_dalek_derive::unsafe_target_feature_specialize;
|
||||
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))]
|
||||
mod simd {
|
||||
#[for_target_feature("sse2")]
|
||||
@ -149,6 +155,7 @@ mod simd {
|
||||
pub fn func() { /* ... */ }
|
||||
}
|
||||
|
||||
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn entry_point() {
|
||||
#[cfg(nightly)]
|
||||
if std::is_x86_feature_detected!("avx512ifma") {
|
||||
@ -179,8 +186,8 @@ fn entry_point() {
|
||||
|
||||
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>)
|
||||
* Apache License, Version 2.0, [LICENSE-APACHE](LICENSE-APACHE)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||
|
||||
at your option.
|
||||
|
||||
|
||||
@ -23,10 +23,6 @@ where
|
||||
a - b
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[cfg(feature = "dummy")]
|
||||
fn function_with_cfg() {}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[rustfmt::skip]
|
||||
fn function_with_rustfmt_skip() {}
|
||||
@ -45,9 +41,6 @@ impl Struct {
|
||||
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>
|
||||
@ -83,9 +76,7 @@ impl<T: Copy + core::ops::Sub> StructWithGenericsNoWhere<T> {
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[allow(dead_code)]
|
||||
impl<'a> From<&'a Struct> for () {
|
||||
fn from(_: &'a Struct) -> Self {
|
||||
()
|
||||
}
|
||||
fn from(_: &'a Struct) -> Self {}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
@ -95,10 +86,8 @@ mod inner {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", disabled))]
|
||||
#[unsafe_target_feature_specialize("sse2", "avx2")]
|
||||
mod inner_spec {
|
||||
use std;
|
||||
|
||||
#[for_target_feature("sse2")]
|
||||
const CONST: u32 = 1;
|
||||
|
||||
|
||||
@ -5,6 +5,29 @@ major series.
|
||||
|
||||
## 4.x series
|
||||
|
||||
### 4.1.3
|
||||
|
||||
* Security: Fix timing leak in Scalar subtraction on u32, u64, fiat_u32, and fiat_u64 backends
|
||||
* Fix assorted new warnings and lints from rustc and clippy
|
||||
|
||||
### 4.1.2
|
||||
|
||||
* Fix nightly SIMD build
|
||||
|
||||
### 4.1.1
|
||||
|
||||
* Mark `constants::BASEPOINT_ORDER` deprecated from pub API
|
||||
* Add implementation for `PrimeFieldBits`, behind the `group-bits` feature flag.
|
||||
|
||||
### 4.1.0
|
||||
|
||||
* Add arbitrary integer multiplication with `MontgomeryPoint::mul_bits_be`
|
||||
* Add implementations of the `ff` and `group` traits, behind the `group` feature flag
|
||||
* Adapt to new types introduced in `fiat-crypto` 0.2 in `fiat` backend
|
||||
* Fix `no_std` for `fiat` backend
|
||||
* Mark `Scalar::clamp_integer` as `#[must_use]`
|
||||
* Various documentation fixes
|
||||
|
||||
### 4.0.0
|
||||
|
||||
#### Breaking changes
|
||||
@ -109,7 +132,7 @@ major series.
|
||||
|
||||
### 2.1.2
|
||||
|
||||
* Multiple documenation typo fixes.
|
||||
* Multiple documentation typo fixes.
|
||||
* Fix `alloc` feature working with stable rust.
|
||||
|
||||
### 2.1.1
|
||||
|
||||
@ -4,14 +4,14 @@ name = "curve25519-dalek"
|
||||
# - update CHANGELOG
|
||||
# - update README if required by semver
|
||||
# - if README was updated, also update module documentation in src/lib.rs
|
||||
version = "4.0.0"
|
||||
version = "4.1.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.60.0"
|
||||
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
||||
readme = "README.md"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek"
|
||||
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
documentation = "https://docs.rs/curve25519-dalek"
|
||||
categories = ["cryptography", "no-std"]
|
||||
@ -27,18 +27,17 @@ rustdoc-args = [
|
||||
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
|
||||
"--cfg", "docsrs",
|
||||
]
|
||||
features = ["serde", "rand_core", "digest", "legacy_compatibility"]
|
||||
features = ["serde", "rand_core", "digest", "legacy_compatibility", "group-bits"]
|
||||
|
||||
[dev-dependencies]
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
bincode = "1"
|
||||
criterion = { version = "0.4.0", features = ["html_reports"] }
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
hex = "0.4.2"
|
||||
rand = "0.8"
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
|
||||
[build-dependencies]
|
||||
platforms = "3.0.2"
|
||||
rustc_version = "0.4.0"
|
||||
|
||||
[[bench]]
|
||||
@ -48,6 +47,8 @@ required-features = ["alloc", "rand_core"]
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
ff = { version = "0.13", default-features = false, optional = true }
|
||||
group = { version = "0.13", default-features = false, optional = true }
|
||||
rand_core = { version = "0.6.4", default-features = false, optional = true }
|
||||
digest = { version = "0.10", default-features = false, optional = true }
|
||||
subtle = { version = "2.3.0", default-features = false }
|
||||
@ -58,13 +59,15 @@ zeroize = { version = "1", default-features = false, optional = true }
|
||||
cpufeatures = "0.2.6"
|
||||
|
||||
[target.'cfg(curve25519_dalek_backend = "fiat")'.dependencies]
|
||||
fiat-crypto = "0.1.19"
|
||||
fiat-crypto = { version = "0.2.1", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["alloc", "precomputed-tables", "zeroize", "lizard"]
|
||||
alloc = ["zeroize?/alloc"]
|
||||
precomputed-tables = []
|
||||
legacy_compatibility = []
|
||||
group = ["dep:group", "rand_core"]
|
||||
group-bits = ["group", "ff/bits"]
|
||||
lizard = ["digest"]
|
||||
|
||||
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
|
||||
|
||||
@ -38,6 +38,12 @@ your project's `Cargo.toml`:
|
||||
curve25519-dalek = "4"
|
||||
```
|
||||
|
||||
If opting into [SemVer-exempted features](#public-api-semver-exemptions) a range
|
||||
can be used to scope the tested compatible version range e.g.:
|
||||
```toml
|
||||
curve25519-dalek = ">= 4.0, < 4.2"
|
||||
```
|
||||
|
||||
## Feature Flags
|
||||
|
||||
| Feature | Default? | Description |
|
||||
@ -49,6 +55,7 @@ curve25519-dalek = "4"
|
||||
| `digest` | | Enables `RistrettoPoint::{from_hash, hash_from_bytes}` and `Scalar::{from_hash, hash_from_bytes}`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
|
||||
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
|
||||
| `legacy_compatibility`| | Enables `Scalar::from_bits`, which allows the user to build unreduced scalars whose arithmetic is broken. Do not use this unless you know what you're doing. |
|
||||
| `group` | | Enables external `group` and `ff` crate traits |
|
||||
|
||||
To disable the default features when using `curve25519-dalek` as a dependency,
|
||||
add `default-features = false` to the dependency in your `Cargo.toml`. To
|
||||
@ -187,12 +194,12 @@ 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
|
||||
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 |
|
||||
| Releases | Public API Component(s) | Policy |
|
||||
| :--- | :--- | :--- |
|
||||
| 4.x | Dependencies `group`, `digest` and `rand_core` | Minor SemVer bump |
|
||||
|
||||
# Safety
|
||||
|
||||
@ -302,8 +309,8 @@ 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
|
||||
[ed25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek
|
||||
[x25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek
|
||||
[docs]: https://docs.rs/curve25519-dalek/
|
||||
[contributing]: https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md
|
||||
[criterion]: https://github.com/japaric/criterion.rs
|
||||
|
||||
@ -7,7 +7,6 @@ use criterion::{
|
||||
};
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
use curve25519_dalek::field::FieldElement;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
|
||||
static BATCH_SIZES: [usize; 5] = [1, 2, 4, 8, 16];
|
||||
@ -148,14 +147,14 @@ mod multiscalar_benches {
|
||||
let static_size = total_size;
|
||||
|
||||
let static_points = construct_points(static_size);
|
||||
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
|
||||
let precomp = VartimeEdwardsPrecomputation::new(static_points);
|
||||
// Rerandomize the scalars for every call to prevent
|
||||
// false timings from better caching (e.g., the CPU
|
||||
// cache lifts exactly the right table entries for the
|
||||
// benchmark into the highest cache levels).
|
||||
b.iter_batched(
|
||||
|| construct_scalars(static_size),
|
||||
|scalars| precomp.vartime_multiscalar_mul(&scalars),
|
||||
|scalars| precomp.vartime_multiscalar_mul(scalars),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
},
|
||||
@ -183,7 +182,7 @@ mod multiscalar_benches {
|
||||
|
||||
let static_points = construct_points(static_size);
|
||||
let dynamic_points = construct_points(dynamic_size);
|
||||
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
|
||||
let precomp = VartimeEdwardsPrecomputation::new(static_points);
|
||||
// Rerandomize the scalars for every call to prevent
|
||||
// false timings from better caching (e.g., the CPU
|
||||
// cache lifts exactly the right table entries for the
|
||||
@ -245,11 +244,8 @@ mod ristretto_benches {
|
||||
}
|
||||
|
||||
fn elligator<M: Measurement>(c: &mut BenchmarkGroup<M>) {
|
||||
let fe_bytes = [0u8; 32];
|
||||
let fe = FieldElement::from_bytes(&fe_bytes);
|
||||
|
||||
c.bench_function("RistrettoPoint Elligator", |b| {
|
||||
b.iter(|| RistrettoPoint::elligator_ristretto_flavor(&fe));
|
||||
b.iter(|| RistrettoPoint::from_uniform_bytes_single_elligator(&[0u8; 32]));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -9,17 +9,31 @@ enum DalekBits {
|
||||
Dalek64,
|
||||
}
|
||||
|
||||
use std::fmt::Formatter;
|
||||
|
||||
impl std::fmt::Display for DalekBits {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let w_bits = match self {
|
||||
DalekBits::Dalek32 => "32",
|
||||
DalekBits::Dalek64 => "64",
|
||||
};
|
||||
write!(f, "{}", w_bits)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") {
|
||||
Ok(arch) => arch,
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
let curve25519_dalek_bits = match std::env::var("CARGO_CFG_CURVE25519_DALEK_BITS").as_deref() {
|
||||
Ok("32") => DalekBits::Dalek32,
|
||||
Ok("64") => DalekBits::Dalek64,
|
||||
_ => deterministic::determine_curve25519_dalek_bits(),
|
||||
_ => deterministic::determine_curve25519_dalek_bits(&target_arch),
|
||||
};
|
||||
|
||||
match curve25519_dalek_bits {
|
||||
DalekBits::Dalek64 => println!("cargo:rustc-cfg=curve25519_dalek_bits=\"64\""),
|
||||
DalekBits::Dalek32 => println!("cargo:rustc-cfg=curve25519_dalek_bits=\"32\""),
|
||||
}
|
||||
println!("cargo:rustc-cfg=curve25519_dalek_bits=\"{curve25519_dalek_bits}\"");
|
||||
|
||||
if rustc_version::version_meta()
|
||||
.expect("failed to detect rustc version")
|
||||
@ -36,11 +50,6 @@ fn main() {
|
||||
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() {
|
||||
@ -74,11 +83,12 @@ 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 Rust non-cargo build tooling needs to set CARGO_CFG_TARGET_POINTER_WIDTH
|
||||
static ERR_MSG_NO_POINTER_WIDTH: &str =
|
||||
"Standard Cargo TARGET_POINTER_WIDTH environment variable is not set.";
|
||||
|
||||
// Custom Non-Rust standard target platforms require explicit settings.
|
||||
static ERR_MSG_NO_PLATFORM: &str = "Unknown Rust target platform.";
|
||||
// When either non-32 or 64 TARGET_POINTER_WIDTH detected
|
||||
static ERR_MSG_UNKNOWN_POINTER_WIDTH: &str = "Unknown TARGET_POINTER_WIDTH detected.";
|
||||
|
||||
// Warning when the curve25519_dalek_bits cannot be determined
|
||||
fn determine_curve25519_dalek_bits_warning(cause: &str) {
|
||||
@ -86,41 +96,30 @@ mod deterministic {
|
||||
}
|
||||
|
||||
// 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,
|
||||
pub(super) fn determine_curve25519_dalek_bits(target_arch: &String) -> DalekBits {
|
||||
let target_pointer_width = match std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH") {
|
||||
Ok(pw) => pw,
|
||||
Err(_) => {
|
||||
determine_curve25519_dalek_bits_warning(ERR_MSG_NO_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);
|
||||
determine_curve25519_dalek_bits_warning(ERR_MSG_NO_POINTER_WIDTH);
|
||||
return DalekBits::Dalek32;
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(clippy::match_single_binding)]
|
||||
match platform.target_arch {
|
||||
match &target_arch {
|
||||
//Issues: 449 and 456
|
||||
//TODO: When adding arch defaults use proper types not String match
|
||||
//TODO(Arm): Needs tests + benchmarks to back this up
|
||||
//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,
|
||||
_ => match target_pointer_width.as_ref() {
|
||||
"64" => DalekBits::Dalek64,
|
||||
"32" => DalekBits::Dalek32,
|
||||
// Intended default solely for non-32/64 target pointer widths
|
||||
// Otherwise known target platforms only.
|
||||
_ => DalekBits::Dalek32,
|
||||
_ => {
|
||||
determine_curve25519_dalek_bits_warning(ERR_MSG_UNKNOWN_POINTER_WIDTH);
|
||||
DalekBits::Dalek32
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,7 +351,7 @@ This computation requires 25 `vpmadd52luq` and 25 `vpmadd52huq`
|
||||
operations. For 256-bit vectors, IFMA operations execute on an
|
||||
i3-8121U with latency 4 cycles, throughput 0.5 cycles, so executing 50
|
||||
instructions requires 25 cycles' worth of throughput. Accumulating
|
||||
terms with coefficient \\(1\\) and \\(2\\) seperately means that the
|
||||
terms with coefficient \\(1\\) and \\(2\\) separately means that the
|
||||
longest dependency chain has length 5, so the critical path has length
|
||||
20 cycles and the bottleneck is throughput.
|
||||
|
||||
|
||||
@ -87,24 +87,24 @@ where
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
Avx2(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,
|
||||
vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
|
||||
),
|
||||
Scalar(self::serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus),
|
||||
Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus),
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -119,12 +119,12 @@ impl VartimePrecomputedStraus {
|
||||
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)),
|
||||
VartimePrecomputedStraus::Avx2(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)),
|
||||
VartimePrecomputedStraus::Avx512ifma(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))
|
||||
VartimePrecomputedStraus::Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(static_points))
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,19 +179,16 @@ where
|
||||
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,
|
||||
)
|
||||
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)
|
||||
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)
|
||||
serial::scalar_mul::straus::Straus::multiscalar_mul::<I, J>(scalars, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,21 +206,19 @@ where
|
||||
match get_selected_backend() {
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 => {
|
||||
self::vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
|
||||
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::<
|
||||
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,
|
||||
)
|
||||
serial::scalar_mul::straus::Straus::optional_multiscalar_mul::<I, J>(scalars, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,12 +227,12 @@ where
|
||||
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),
|
||||
BackendKind::Avx2 => 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)
|
||||
vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
|
||||
}
|
||||
BackendKind::Serial => self::serial::scalar_mul::variable_base::mul(point, scalar),
|
||||
BackendKind::Serial => serial::scalar_mul::variable_base::mul(point, scalar),
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,11 +241,11 @@ pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint
|
||||
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),
|
||||
BackendKind::Avx2 => 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)
|
||||
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),
|
||||
BackendKind::Serial => serial::scalar_mul::vartime_double_base::mul(a, A, b),
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,7 +527,7 @@ impl<'a> Neg for &'a AffineNielsPoint {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
impl Debug for ProjectivePoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
|
||||
@ -537,7 +537,7 @@ impl Debug for ProjectivePoint {
|
||||
}
|
||||
|
||||
impl Debug for CompletedPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
|
||||
@ -547,7 +547,7 @@ impl Debug for CompletedPoint {
|
||||
}
|
||||
|
||||
impl Debug for AffineNielsPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
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}}",
|
||||
@ -557,7 +557,7 @@ impl Debug for AffineNielsPoint {
|
||||
}
|
||||
|
||||
impl Debug for ProjectiveNielsPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}",
|
||||
&self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
|
||||
}
|
||||
|
||||
@ -55,73 +55,78 @@ use fiat_crypto::curve25519_32::*;
|
||||
/// The backend-specific type `FieldElement2625` should not be used
|
||||
/// outside of the `curve25519_dalek::field` module.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FieldElement2625(pub(crate) [u32; 10]);
|
||||
pub struct FieldElement2625(pub(crate) fiat_25519_tight_field_element);
|
||||
|
||||
impl Debug for FieldElement2625 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "FieldElement2625({:?})", &self.0[..])
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "FieldElement2625({:?})", &(self.0).0[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement2625 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
(self.0).0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AddAssign<&'b FieldElement2625> for FieldElement2625 {
|
||||
fn add_assign(&mut self, _rhs: &'b FieldElement2625) {
|
||||
let input = self.0;
|
||||
fiat_25519_add(&mut self.0, &input, &_rhs.0);
|
||||
let input = self.0;
|
||||
fiat_25519_carry(&mut self.0, &input);
|
||||
fn add_assign(&mut self, rhs: &'b FieldElement2625) {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
|
||||
fiat_25519_carry(&mut self.0, &result_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b FieldElement2625> for &'a FieldElement2625 {
|
||||
type Output = FieldElement2625;
|
||||
fn add(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
fiat_25519_add(&mut output.0, &self.0, &_rhs.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
fn add(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &result_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b FieldElement2625> for FieldElement2625 {
|
||||
fn sub_assign(&mut self, _rhs: &'b FieldElement2625) {
|
||||
let input = self.0;
|
||||
fiat_25519_sub(&mut self.0, &input, &_rhs.0);
|
||||
let input = self.0;
|
||||
fiat_25519_carry(&mut self.0, &input);
|
||||
fn sub_assign(&mut self, rhs: &'b FieldElement2625) {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
|
||||
fiat_25519_carry(&mut self.0, &result_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b FieldElement2625> for &'a FieldElement2625 {
|
||||
type Output = FieldElement2625;
|
||||
fn sub(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
fiat_25519_sub(&mut output.0, &self.0, &_rhs.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
fn sub(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &result_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b FieldElement2625> for FieldElement2625 {
|
||||
fn mul_assign(&mut self, _rhs: &'b FieldElement2625) {
|
||||
let input = self.0;
|
||||
fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0);
|
||||
fn mul_assign(&mut self, rhs: &'b FieldElement2625) {
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut rhs_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut rhs_loose, &rhs.0);
|
||||
fiat_25519_carry_mul(&mut self.0, &self_loose, &rhs_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
|
||||
type Output = FieldElement2625;
|
||||
fn mul(self, _rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0);
|
||||
fn mul(self, rhs: &'b FieldElement2625) -> FieldElement2625 {
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut rhs_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut rhs_loose, &rhs.0);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry_mul(&mut output.0, &self_loose, &rhs_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
@ -129,10 +134,10 @@ impl<'a, 'b> Mul<&'b FieldElement2625> for &'a FieldElement2625 {
|
||||
impl<'a> Neg for &'a FieldElement2625 {
|
||||
type Output = FieldElement2625;
|
||||
fn neg(self) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
fiat_25519_opp(&mut output.0, &self.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
let mut output_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_opp(&mut output_loose, &self.0);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &output_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
@ -143,8 +148,13 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
b: &FieldElement2625,
|
||||
choice: Choice,
|
||||
) -> FieldElement2625 {
|
||||
let mut output = [0u32; 10];
|
||||
fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0);
|
||||
let mut output = fiat_25519_tight_field_element([0u32; 10]);
|
||||
fiat_25519_selectznz(
|
||||
&mut output.0,
|
||||
choice.unwrap_u8() as fiat_25519_u1,
|
||||
&(a.0).0,
|
||||
&(b.0).0,
|
||||
);
|
||||
FieldElement2625(output)
|
||||
}
|
||||
|
||||
@ -161,7 +171,7 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
fiat_25519_cmovznz_u32(&mut output[7], choicebit, self.0[7], other.0[7]);
|
||||
fiat_25519_cmovznz_u32(&mut output[8], choicebit, self.0[8], other.0[8]);
|
||||
fiat_25519_cmovznz_u32(&mut output[9], choicebit, self.0[9], other.0[9]);
|
||||
*self = FieldElement2625(output);
|
||||
*self = FieldElement2625::from_limbs(output);
|
||||
}
|
||||
|
||||
fn conditional_swap(a: &mut FieldElement2625, b: &mut FieldElement2625, choice: Choice) {
|
||||
@ -179,12 +189,16 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
}
|
||||
|
||||
impl FieldElement2625 {
|
||||
pub(crate) const fn from_limbs(limbs: [u32; 10]) -> FieldElement2625 {
|
||||
FieldElement2625(fiat_25519_tight_field_element(limbs))
|
||||
}
|
||||
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement2625 = FieldElement2625([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const ONE: FieldElement2625 = FieldElement2625::from_limbs([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625([
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625::from_limbs([
|
||||
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
|
||||
0x3ffffff, 0x1ffffff,
|
||||
]);
|
||||
@ -220,7 +234,7 @@ impl FieldElement2625 {
|
||||
let mut temp = [0u8; 32];
|
||||
temp.copy_from_slice(data);
|
||||
temp[31] &= 127u8;
|
||||
let mut output = [0u32; 10];
|
||||
let mut output = fiat_25519_tight_field_element([0u32; 10]);
|
||||
fiat_25519_from_bytes(&mut output, &temp);
|
||||
FieldElement2625(output)
|
||||
}
|
||||
@ -235,20 +249,23 @@ impl FieldElement2625 {
|
||||
|
||||
/// Compute `self^2`.
|
||||
pub fn square(&self) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
fiat_25519_carry_square(&mut output.0, &self.0);
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry_square(&mut output.0, &self_loose);
|
||||
output
|
||||
}
|
||||
|
||||
/// Compute `2*self^2`.
|
||||
pub fn square2(&self) -> FieldElement2625 {
|
||||
let mut output = *self;
|
||||
let mut temp = *self;
|
||||
// Void vs return type, measure cost of copying self
|
||||
fiat_25519_carry_square(&mut temp.0, &self.0);
|
||||
fiat_25519_add(&mut output.0, &temp.0, &temp.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut square = fiat_25519_tight_field_element([0; 10]);
|
||||
fiat_25519_carry_square(&mut square, &self_loose);
|
||||
let mut output_loose = fiat_25519_loose_field_element([0; 10]);
|
||||
fiat_25519_add(&mut output_loose, &square, &square);
|
||||
let mut output = FieldElement2625::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &output_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,73 +44,78 @@ use fiat_crypto::curve25519_64::*;
|
||||
/// The backend-specific type `FieldElement51` should not be used
|
||||
/// outside of the `curve25519_dalek::field` module.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FieldElement51(pub(crate) [u64; 5]);
|
||||
pub struct FieldElement51(pub(crate) fiat_25519_tight_field_element);
|
||||
|
||||
impl Debug for FieldElement51 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "FieldElement51({:?})", &self.0[..])
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "FieldElement51({:?})", &(self.0).0[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Zeroize for FieldElement51 {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize();
|
||||
(self.0).0.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> AddAssign<&'b FieldElement51> for FieldElement51 {
|
||||
fn add_assign(&mut self, _rhs: &'b FieldElement51) {
|
||||
let input = self.0;
|
||||
fiat_25519_add(&mut self.0, &input, &_rhs.0);
|
||||
let input = self.0;
|
||||
fiat_25519_carry(&mut self.0, &input);
|
||||
fn add_assign(&mut self, rhs: &'b FieldElement51) {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
|
||||
fiat_25519_carry(&mut self.0, &result_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b FieldElement51> for &'a FieldElement51 {
|
||||
type Output = FieldElement51;
|
||||
fn add(self, _rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
fiat_25519_add(&mut output.0, &self.0, &_rhs.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
fn add(self, rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_add(&mut result_loose, &self.0, &rhs.0);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &result_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b FieldElement51> for FieldElement51 {
|
||||
fn sub_assign(&mut self, _rhs: &'b FieldElement51) {
|
||||
let input = self.0;
|
||||
fiat_25519_sub(&mut self.0, &input, &_rhs.0);
|
||||
let input = self.0;
|
||||
fiat_25519_carry(&mut self.0, &input);
|
||||
fn sub_assign(&mut self, rhs: &'b FieldElement51) {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
|
||||
fiat_25519_carry(&mut self.0, &result_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b FieldElement51> for &'a FieldElement51 {
|
||||
type Output = FieldElement51;
|
||||
fn sub(self, _rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
fiat_25519_sub(&mut output.0, &self.0, &_rhs.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
fn sub(self, rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut result_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_sub(&mut result_loose, &self.0, &rhs.0);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &result_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b FieldElement51> for FieldElement51 {
|
||||
fn mul_assign(&mut self, _rhs: &'b FieldElement51) {
|
||||
let input = self.0;
|
||||
fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0);
|
||||
fn mul_assign(&mut self, rhs: &'b FieldElement51) {
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut rhs_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut rhs_loose, &rhs.0);
|
||||
fiat_25519_carry_mul(&mut self.0, &self_loose, &rhs_loose);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
|
||||
type Output = FieldElement51;
|
||||
fn mul(self, _rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0);
|
||||
fn mul(self, rhs: &'b FieldElement51) -> FieldElement51 {
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut rhs_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut rhs_loose, &rhs.0);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry_mul(&mut output.0, &self_loose, &rhs_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
@ -118,10 +123,10 @@ impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 {
|
||||
impl<'a> Neg for &'a FieldElement51 {
|
||||
type Output = FieldElement51;
|
||||
fn neg(self) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
fiat_25519_opp(&mut output.0, &self.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
let mut output_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_opp(&mut output_loose, &self.0);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &output_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
@ -132,8 +137,13 @@ impl ConditionallySelectable for FieldElement51 {
|
||||
b: &FieldElement51,
|
||||
choice: Choice,
|
||||
) -> FieldElement51 {
|
||||
let mut output = [0u64; 5];
|
||||
fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0);
|
||||
let mut output = fiat_25519_tight_field_element([0u64; 5]);
|
||||
fiat_25519_selectznz(
|
||||
&mut output.0,
|
||||
choice.unwrap_u8() as fiat_25519_u1,
|
||||
&(a.0).0,
|
||||
&(b.0).0,
|
||||
);
|
||||
FieldElement51(output)
|
||||
}
|
||||
|
||||
@ -145,25 +155,29 @@ impl ConditionallySelectable for FieldElement51 {
|
||||
u64::conditional_swap(&mut a.0[4], &mut b.0[4], choice);
|
||||
}
|
||||
|
||||
fn conditional_assign(&mut self, _rhs: &FieldElement51, choice: Choice) {
|
||||
fn conditional_assign(&mut self, rhs: &FieldElement51, choice: Choice) {
|
||||
let mut output = [0u64; 5];
|
||||
let choicebit = choice.unwrap_u8() as fiat_25519_u1;
|
||||
fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], _rhs.0[0]);
|
||||
fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], _rhs.0[1]);
|
||||
fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], _rhs.0[2]);
|
||||
fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], _rhs.0[3]);
|
||||
fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], _rhs.0[4]);
|
||||
*self = FieldElement51(output);
|
||||
fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], rhs.0[0]);
|
||||
fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], rhs.0[1]);
|
||||
fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], rhs.0[2]);
|
||||
fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], rhs.0[3]);
|
||||
fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], rhs.0[4]);
|
||||
*self = FieldElement51::from_limbs(output);
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldElement51 {
|
||||
pub(crate) const fn from_limbs(limbs: [u64; 5]) -> FieldElement51 {
|
||||
FieldElement51(fiat_25519_tight_field_element(limbs))
|
||||
}
|
||||
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement51 = FieldElement51([0, 0, 0, 0, 0]);
|
||||
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement51 = FieldElement51([1, 0, 0, 0, 0]);
|
||||
pub const ONE: FieldElement51 = FieldElement51::from_limbs([1, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51([
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51::from_limbs([
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
@ -174,10 +188,11 @@ impl FieldElement51 {
|
||||
/// Given 64-bit input limbs, reduce to enforce the bound 2^(51 + epsilon).
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)] // Need this to not complain about reduce not being used
|
||||
fn reduce(mut limbs: [u64; 5]) -> FieldElement51 {
|
||||
let input = limbs;
|
||||
fiat_25519_carry(&mut limbs, &input);
|
||||
FieldElement51(limbs)
|
||||
fn reduce(limbs: [u64; 5]) -> FieldElement51 {
|
||||
let input = fiat_25519_loose_field_element(limbs);
|
||||
let mut output = fiat_25519_tight_field_element([0; 5]);
|
||||
fiat_25519_carry(&mut output, &input);
|
||||
FieldElement51(output)
|
||||
}
|
||||
|
||||
/// Load a `FieldElement51` from the low 255 bits of a 256-bit
|
||||
@ -196,7 +211,7 @@ impl FieldElement51 {
|
||||
let mut temp = [0u8; 32];
|
||||
temp.copy_from_slice(bytes);
|
||||
temp[31] &= 127u8;
|
||||
let mut output = [0u64; 5];
|
||||
let mut output = fiat_25519_tight_field_element([0u64; 5]);
|
||||
fiat_25519_from_bytes(&mut output, &temp);
|
||||
FieldElement51(output)
|
||||
}
|
||||
@ -213,7 +228,8 @@ impl FieldElement51 {
|
||||
pub fn pow2k(&self, mut k: u32) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
loop {
|
||||
let input = output.0;
|
||||
let mut input = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut input, &output.0);
|
||||
fiat_25519_carry_square(&mut output.0, &input);
|
||||
k -= 1;
|
||||
if k == 0 {
|
||||
@ -224,20 +240,23 @@ impl FieldElement51 {
|
||||
|
||||
/// Returns the square of this field element.
|
||||
pub fn square(&self) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
fiat_25519_carry_square(&mut output.0, &self.0);
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry_square(&mut output.0, &self_loose);
|
||||
output
|
||||
}
|
||||
|
||||
/// Returns 2 times the square of this field element.
|
||||
pub fn square2(&self) -> FieldElement51 {
|
||||
let mut output = *self;
|
||||
let mut temp = *self;
|
||||
// Void vs return type, measure cost of copying self
|
||||
fiat_25519_carry_square(&mut temp.0, &self.0);
|
||||
fiat_25519_add(&mut output.0, &temp.0, &temp.0);
|
||||
let input = output.0;
|
||||
fiat_25519_carry(&mut output.0, &input);
|
||||
let mut self_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_relax(&mut self_loose, &self.0);
|
||||
let mut square = fiat_25519_tight_field_element([0; 5]);
|
||||
fiat_25519_carry_square(&mut square, &self_loose);
|
||||
let mut output_loose = fiat_25519_loose_field_element([0; 5]);
|
||||
fiat_25519_add(&mut output_loose, &square, &square);
|
||||
let mut output = FieldElement51::ZERO;
|
||||
fiat_25519_carry(&mut output.0, &output_loose);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,17 +24,21 @@ cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
#[doc(hidden)]
|
||||
pub mod fiat_u32;
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
#[doc(hidden)]
|
||||
pub mod fiat_u64;
|
||||
|
||||
} else {
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
#[doc(hidden)]
|
||||
pub mod u32;
|
||||
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
#[doc(hidden)]
|
||||
pub mod u64;
|
||||
|
||||
}
|
||||
|
||||
@ -154,8 +154,7 @@ impl VartimeMultiscalarMul for Pippenger {
|
||||
});
|
||||
|
||||
// Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`.
|
||||
// `unwrap()` always succeeds because we know we have more than zero digits.
|
||||
let hi_column = columns.next().unwrap();
|
||||
let hi_column = columns.next().expect("should have more than zero digits");
|
||||
|
||||
Some(columns.fold(hi_column, |total, p| total.mul_by_pow_2(w as u32) + p))
|
||||
}
|
||||
@ -165,7 +164,6 @@ impl VartimeMultiscalarMul for Pippenger {
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::constants;
|
||||
use crate::scalar::Scalar;
|
||||
|
||||
#[test]
|
||||
fn test_vartime_pippenger() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,7 @@ use zeroize::Zeroize;
|
||||
pub struct FieldElement2625(pub(crate) [u32; 10]);
|
||||
|
||||
impl Debug for FieldElement2625 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "FieldElement2625({:?})", &self.0[..])
|
||||
}
|
||||
}
|
||||
@ -284,12 +284,16 @@ impl ConditionallySelectable for FieldElement2625 {
|
||||
}
|
||||
|
||||
impl FieldElement2625 {
|
||||
pub(crate) const fn from_limbs(limbs: [u32; 10]) -> FieldElement2625 {
|
||||
FieldElement2625(limbs)
|
||||
}
|
||||
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const ZERO: FieldElement2625 = FieldElement2625::from_limbs([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement2625 = FieldElement2625([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const ONE: FieldElement2625 = FieldElement2625::from_limbs([1, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625([
|
||||
pub const MINUS_ONE: FieldElement2625 = FieldElement2625::from_limbs([
|
||||
0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff,
|
||||
0x3ffffff, 0x1ffffff,
|
||||
]);
|
||||
|
||||
@ -24,7 +24,7 @@ use crate::constants;
|
||||
pub struct Scalar29(pub [u32; 9]);
|
||||
|
||||
impl Debug for Scalar29 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Scalar29: {:?}", &self.0[..])
|
||||
}
|
||||
}
|
||||
@ -185,6 +185,14 @@ impl Scalar29 {
|
||||
|
||||
/// Compute `a - b` (mod l).
|
||||
pub fn sub(a: &Scalar29, b: &Scalar29) -> Scalar29 {
|
||||
// Optimization barrier to prevent compiler from inserting branch instructions
|
||||
// TODO(tarcieri): find a better home (or abstraction) for this
|
||||
fn black_box(value: u32) -> u32 {
|
||||
// SAFETY: `u32` is a simple integer `Copy` type and `value` lives on the stack so
|
||||
// a pointer to it will be valid.
|
||||
unsafe { core::ptr::read_volatile(&value) }
|
||||
}
|
||||
|
||||
let mut difference = Scalar29::ZERO;
|
||||
let mask = (1u32 << 29) - 1;
|
||||
|
||||
@ -199,7 +207,7 @@ impl Scalar29 {
|
||||
let underflow_mask = ((borrow >> 31) ^ 1).wrapping_sub(1);
|
||||
let mut carry: u32 = 0;
|
||||
for i in 0..9 {
|
||||
carry = (carry >> 29) + difference[i] + (constants::L[i] & underflow_mask);
|
||||
carry = (carry >> 29) + difference[i] + (constants::L[i] & black_box(underflow_mask));
|
||||
difference[i] = carry & mask;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -43,7 +43,7 @@ use zeroize::Zeroize;
|
||||
pub struct FieldElement51(pub(crate) [u64; 5]);
|
||||
|
||||
impl Debug for FieldElement51 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "FieldElement51({:?})", &self.0[..])
|
||||
}
|
||||
}
|
||||
@ -255,12 +255,16 @@ impl ConditionallySelectable for FieldElement51 {
|
||||
}
|
||||
|
||||
impl FieldElement51 {
|
||||
pub(crate) const fn from_limbs(limbs: [u64; 5]) -> FieldElement51 {
|
||||
FieldElement51(limbs)
|
||||
}
|
||||
|
||||
/// The scalar \\( 0 \\).
|
||||
pub const ZERO: FieldElement51 = FieldElement51([0, 0, 0, 0, 0]);
|
||||
pub const ZERO: FieldElement51 = FieldElement51::from_limbs([0, 0, 0, 0, 0]);
|
||||
/// The scalar \\( 1 \\).
|
||||
pub const ONE: FieldElement51 = FieldElement51([1, 0, 0, 0, 0]);
|
||||
pub const ONE: FieldElement51 = FieldElement51::from_limbs([1, 0, 0, 0, 0]);
|
||||
/// The scalar \\( -1 \\).
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51([
|
||||
pub const MINUS_ONE: FieldElement51 = FieldElement51::from_limbs([
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
|
||||
@ -25,7 +25,7 @@ use crate::constants;
|
||||
pub struct Scalar52(pub [u64; 5]);
|
||||
|
||||
impl Debug for Scalar52 {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Scalar52: {:?}", &self.0[..])
|
||||
}
|
||||
}
|
||||
@ -174,6 +174,14 @@ impl Scalar52 {
|
||||
|
||||
/// Compute `a - b` (mod l)
|
||||
pub fn sub(a: &Scalar52, b: &Scalar52) -> Scalar52 {
|
||||
// Optimization barrier to prevent compiler from inserting branch instructions
|
||||
// TODO(tarcieri): find a better home (or abstraction) for this
|
||||
fn black_box(value: u64) -> u64 {
|
||||
// SAFETY: `u64` is a simple integer `Copy` type and `value` lives on the stack so
|
||||
// a pointer to it will be valid.
|
||||
unsafe { core::ptr::read_volatile(&value) }
|
||||
}
|
||||
|
||||
let mut difference = Scalar52::ZERO;
|
||||
let mask = (1u64 << 52) - 1;
|
||||
|
||||
@ -188,7 +196,9 @@ impl Scalar52 {
|
||||
let underflow_mask = ((borrow >> 63) ^ 1).wrapping_sub(1);
|
||||
let mut carry: u64 = 0;
|
||||
for i in 0..5 {
|
||||
carry = (carry >> 52) + difference[i] + (constants::L[i] & underflow_mask);
|
||||
// SECURITY: `black_box` prevents LLVM from inserting a `jns` conditional on x86(_64)
|
||||
// which can be used to bypass this section when `underflow_mask` is zero.
|
||||
carry = (carry >> 52) + difference[i] + (constants::L[i] & black_box(underflow_mask));
|
||||
difference[i] = carry & mask;
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,6 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::convert::From;
|
||||
use core::ops::{Add, Neg, Sub};
|
||||
|
||||
use subtle::Choice;
|
||||
@ -240,7 +239,7 @@ impl ConditionallySelectable for CachedPoint {
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> Neg for &'a CachedPoint {
|
||||
impl Neg for &CachedPoint {
|
||||
type Output = CachedPoint;
|
||||
/// Lazily negate the point.
|
||||
///
|
||||
@ -255,11 +254,11 @@ impl<'a> Neg for &'a CachedPoint {
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
impl Add<&CachedPoint> for &ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
/// Add an `ExtendedPoint` and a `CachedPoint`.
|
||||
fn add(self, other: &'b CachedPoint) -> ExtendedPoint {
|
||||
fn add(self, other: &CachedPoint) -> ExtendedPoint {
|
||||
// The coefficients of an `ExtendedPoint` are reduced after
|
||||
// every operation. If the `CachedPoint` was negated, its
|
||||
// coefficients grow by one bit. So on input, `self` is
|
||||
@ -293,7 +292,7 @@ impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
impl Sub<&CachedPoint> for &ExtendedPoint {
|
||||
type Output = ExtendedPoint;
|
||||
|
||||
/// Implement subtraction by negating the point and adding.
|
||||
@ -301,14 +300,14 @@ impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
|
||||
/// Empirically, this seems about the same cost as a custom
|
||||
/// subtraction impl (maybe because the benefit is cancelled by
|
||||
/// increased code size?)
|
||||
fn sub(self, other: &'b CachedPoint) -> ExtendedPoint {
|
||||
fn sub(self, other: &CachedPoint) -> ExtendedPoint {
|
||||
self + &(-other)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
|
||||
fn from(point: &'a edwards::EdwardsPoint) -> Self {
|
||||
impl From<&edwards::EdwardsPoint> for LookupTable<CachedPoint> {
|
||||
fn from(point: &edwards::EdwardsPoint) -> Self {
|
||||
let P = ExtendedPoint::from(*point);
|
||||
let mut points = [CachedPoint::from(P); 8];
|
||||
for i in 0..7 {
|
||||
@ -319,8 +318,8 @@ 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 {
|
||||
impl From<&edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
|
||||
fn from(point: &edwards::EdwardsPoint) -> Self {
|
||||
let A = ExtendedPoint::from(*point);
|
||||
let mut Ai = [CachedPoint::from(A); 8];
|
||||
let A2 = A.double();
|
||||
@ -334,8 +333,8 @@ 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 {
|
||||
impl From<&edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
|
||||
fn from(point: &edwards::EdwardsPoint) -> Self {
|
||||
let A = ExtendedPoint::from(*point);
|
||||
let mut Ai = [CachedPoint::from(A); 64];
|
||||
let A2 = A.double();
|
||||
|
||||
@ -760,7 +760,7 @@ impl Mul<(u32, u32, u32, u32)> for FieldElement2625x4 {
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
|
||||
impl Mul<&FieldElement2625x4> for &FieldElement2625x4 {
|
||||
type Output = FieldElement2625x4;
|
||||
/// Multiply `self` by `rhs`.
|
||||
///
|
||||
@ -776,7 +776,7 @@ impl<'a, 'b> Mul<&'b FieldElement2625x4> for &'a FieldElement2625x4 {
|
||||
///
|
||||
#[rustfmt::skip] // keep alignment of z* calculations
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b FieldElement2625x4) -> FieldElement2625x4 {
|
||||
fn mul(self, rhs: &FieldElement2625x4) -> FieldElement2625x4 {
|
||||
#[inline(always)]
|
||||
fn m(x: u32x8, y: u32x8) -> u64x4 {
|
||||
x.mul32(y)
|
||||
|
||||
@ -123,8 +123,7 @@ pub mod spec {
|
||||
});
|
||||
|
||||
// Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`.
|
||||
// `unwrap()` always succeeds because we know we have more than zero digits.
|
||||
let hi_column = columns.next().unwrap();
|
||||
let hi_column = columns.next().expect("should have more than zero digits");
|
||||
|
||||
Some(
|
||||
columns
|
||||
|
||||
@ -8,24 +8,7 @@
|
||||
// Authors:
|
||||
// - isis agora lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
//! Various constants, such as the Ristretto and Ed25519 basepoints.
|
||||
//!
|
||||
//! Most of the constants are given with
|
||||
//! `LONG_DESCRIPTIVE_UPPER_CASE_NAMES`, but they can be brought into
|
||||
//! scope using a `let` binding:
|
||||
//!
|
||||
#![cfg_attr(feature = "precomputed-tables", doc = "```")]
|
||||
#![cfg_attr(not(feature = "precomputed-tables"), doc = "```ignore")]
|
||||
//! use curve25519_dalek::constants;
|
||||
//! use curve25519_dalek::traits::IsIdentity;
|
||||
//!
|
||||
//! let B = constants::RISTRETTO_BASEPOINT_TABLE;
|
||||
//! let l = &constants::BASEPOINT_ORDER;
|
||||
//!
|
||||
//! let A = l * B;
|
||||
//! assert!(A.is_identity());
|
||||
//! ```
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
@ -86,7 +69,10 @@ pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BAS
|
||||
/// $$
|
||||
/// \ell = 2^\{252\} + 27742317777372353535851937790883648493.
|
||||
/// $$
|
||||
pub const BASEPOINT_ORDER: Scalar = Scalar {
|
||||
#[deprecated(since = "4.1.1", note = "Should not have been in public API")]
|
||||
pub const BASEPOINT_ORDER: Scalar = BASEPOINT_ORDER_PRIVATE;
|
||||
|
||||
pub(crate) const BASEPOINT_ORDER_PRIVATE: Scalar = Scalar {
|
||||
bytes: [
|
||||
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde,
|
||||
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
@ -96,7 +96,6 @@
|
||||
use core::array::TryFromSliceError;
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt::Debug;
|
||||
use core::iter::Iterator;
|
||||
use core::iter::Sum;
|
||||
use core::ops::{Add, Neg, Sub};
|
||||
use core::ops::{AddAssign, SubAssign};
|
||||
@ -107,6 +106,15 @@ use cfg_if::cfg_if;
|
||||
#[cfg(feature = "digest")]
|
||||
use digest::{generic_array::typenum::U64, Digest};
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
use {
|
||||
group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
|
||||
subtle::CtOption,
|
||||
};
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
use rand_core::RngCore;
|
||||
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallyNegatable;
|
||||
use subtle::ConditionallySelectable;
|
||||
@ -163,7 +171,7 @@ impl ConstantTimeEq for CompressedEdwardsY {
|
||||
}
|
||||
|
||||
impl Debug for CompressedEdwardsY {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "CompressedEdwardsY: {:?}", self.as_bytes())
|
||||
}
|
||||
}
|
||||
@ -183,25 +191,52 @@ impl CompressedEdwardsY {
|
||||
///
|
||||
/// Returns `None` if the input is not the \\(y\\)-coordinate of a
|
||||
/// curve point.
|
||||
#[rustfmt::skip] // keep alignment of explanatory comments
|
||||
pub fn decompress(&self) -> Option<EdwardsPoint> {
|
||||
let Y = FieldElement::from_bytes(self.as_bytes());
|
||||
let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
|
||||
|
||||
if is_valid_y_coord.into() {
|
||||
Some(decompress::step_2(self, X, Y, Z))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod decompress {
|
||||
use super::*;
|
||||
|
||||
#[rustfmt::skip] // keep alignment of explanatory comments
|
||||
pub(super) fn step_1(
|
||||
repr: &CompressedEdwardsY,
|
||||
) -> (Choice, FieldElement, FieldElement, FieldElement) {
|
||||
let Y = FieldElement::from_bytes(repr.as_bytes());
|
||||
let Z = FieldElement::ONE;
|
||||
let YY = Y.square();
|
||||
let u = &YY - &Z; // u = y²-1
|
||||
let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1
|
||||
let (is_valid_y_coord, mut X) = FieldElement::sqrt_ratio_i(&u, &v);
|
||||
let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v);
|
||||
|
||||
if (!is_valid_y_coord).into() {
|
||||
return None;
|
||||
}
|
||||
(is_valid_y_coord, X, Y, Z)
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub(super) fn step_2(
|
||||
repr: &CompressedEdwardsY,
|
||||
mut X: FieldElement,
|
||||
Y: FieldElement,
|
||||
Z: FieldElement,
|
||||
) -> EdwardsPoint {
|
||||
// FieldElement::sqrt_ratio_i always returns the nonnegative square root,
|
||||
// so we negate according to the supplied sign bit.
|
||||
let compressed_sign_bit = Choice::from(self.as_bytes()[31] >> 7);
|
||||
let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7);
|
||||
X.conditional_negate(compressed_sign_bit);
|
||||
|
||||
Some(EdwardsPoint{ X, Y, Z, T: &X * &Y })
|
||||
EdwardsPoint {
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
T: &X * &Y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +259,7 @@ impl TryFrom<&[u8]> for CompressedEdwardsY {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Visitor;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for EdwardsPoint {
|
||||
@ -267,7 +302,7 @@ impl<'de> Deserialize<'de> for EdwardsPoint {
|
||||
impl<'de> Visitor<'de> for EdwardsPointVisitor {
|
||||
type Value = EdwardsPoint;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
formatter.write_str("a valid point in Edwards y + sign format")
|
||||
}
|
||||
|
||||
@ -276,6 +311,7 @@ impl<'de> Deserialize<'de> for EdwardsPoint {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
@ -302,7 +338,7 @@ impl<'de> Deserialize<'de> for CompressedEdwardsY {
|
||||
impl<'de> Visitor<'de> for CompressedEdwardsYVisitor {
|
||||
type Value = CompressedEdwardsY;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
formatter.write_str("32 bytes of data")
|
||||
}
|
||||
|
||||
@ -311,6 +347,7 @@ impl<'de> Deserialize<'de> for CompressedEdwardsY {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
@ -1016,7 +1053,7 @@ macro_rules! impl_basepoint_table {
|
||||
}
|
||||
|
||||
impl Debug for $name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}([\n", stringify!($name))?;
|
||||
for i in 0..32 {
|
||||
write!(f, "\t{:?},\n", &self.0[i])?;
|
||||
@ -1218,7 +1255,7 @@ impl EdwardsPoint {
|
||||
/// assert_eq!((P+Q).is_torsion_free(), false);
|
||||
/// ```
|
||||
pub fn is_torsion_free(&self) -> bool {
|
||||
(self * constants::BASEPOINT_ORDER).is_identity()
|
||||
(self * constants::BASEPOINT_ORDER_PRIVATE).is_identity()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1227,7 +1264,7 @@ impl EdwardsPoint {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
impl Debug for EdwardsPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
|
||||
@ -1236,6 +1273,318 @@ impl Debug for EdwardsPoint {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// group traits
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Use the full trait path to avoid Group::identity overlapping Identity::identity in the
|
||||
// rest of the module (e.g. tests).
|
||||
#[cfg(feature = "group")]
|
||||
impl group::Group for EdwardsPoint {
|
||||
type Scalar = Scalar;
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let mut repr = CompressedEdwardsY([0u8; 32]);
|
||||
loop {
|
||||
rng.fill_bytes(&mut repr.0);
|
||||
if let Some(p) = repr.decompress() {
|
||||
if !IsIdentity::is_identity(&p) {
|
||||
break p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Identity::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
constants::ED25519_BASEPOINT_POINT
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.ct_eq(&Identity::identity())
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
self.double()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl GroupEncoding for EdwardsPoint {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
let repr = CompressedEdwardsY(*bytes);
|
||||
let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr);
|
||||
CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord)
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
// Just use the checked API; there are no checks we can skip.
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
self.compress().to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// A `SubgroupPoint` represents a point on the Edwards form of Curve25519, that is
|
||||
/// guaranteed to be in the prime-order subgroup.
|
||||
#[cfg(feature = "group")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct SubgroupPoint(EdwardsPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl From<SubgroupPoint> for EdwardsPoint {
|
||||
fn from(p: SubgroupPoint) -> Self {
|
||||
p.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Neg for SubgroupPoint {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
SubgroupPoint(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Add<&SubgroupPoint> for &SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
fn add(self, other: &SubgroupPoint) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_add_variants!(
|
||||
LHS = SubgroupPoint,
|
||||
RHS = SubgroupPoint,
|
||||
Output = SubgroupPoint
|
||||
);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Add<&SubgroupPoint> for &EdwardsPoint {
|
||||
type Output = EdwardsPoint;
|
||||
fn add(self, other: &SubgroupPoint) -> EdwardsPoint {
|
||||
self + other.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_add_variants!(
|
||||
LHS = EdwardsPoint,
|
||||
RHS = SubgroupPoint,
|
||||
Output = EdwardsPoint
|
||||
);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl AddAssign<&SubgroupPoint> for SubgroupPoint {
|
||||
fn add_assign(&mut self, rhs: &SubgroupPoint) {
|
||||
self.0 += rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl AddAssign<&SubgroupPoint> for EdwardsPoint {
|
||||
fn add_assign(&mut self, rhs: &SubgroupPoint) {
|
||||
*self += rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Sub<&SubgroupPoint> for &SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
fn sub(self, other: &SubgroupPoint) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0 - other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_sub_variants!(
|
||||
LHS = SubgroupPoint,
|
||||
RHS = SubgroupPoint,
|
||||
Output = SubgroupPoint
|
||||
);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Sub<&SubgroupPoint> for &EdwardsPoint {
|
||||
type Output = EdwardsPoint;
|
||||
fn sub(self, other: &SubgroupPoint) -> EdwardsPoint {
|
||||
self - other.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_sub_variants!(
|
||||
LHS = EdwardsPoint,
|
||||
RHS = SubgroupPoint,
|
||||
Output = EdwardsPoint
|
||||
);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl SubAssign<&SubgroupPoint> for SubgroupPoint {
|
||||
fn sub_assign(&mut self, rhs: &SubgroupPoint) {
|
||||
self.0 -= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl SubAssign<&SubgroupPoint> for EdwardsPoint {
|
||||
fn sub_assign(&mut self, rhs: &SubgroupPoint) {
|
||||
*self -= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl<T> Sum<T> for SubgroupPoint
|
||||
where
|
||||
T: Borrow<SubgroupPoint>,
|
||||
{
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
{
|
||||
use group::Group;
|
||||
iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Mul<&Scalar> for &SubgroupPoint {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
/// Scalar multiplication: compute `scalar * self`.
|
||||
///
|
||||
/// For scalar multiplication of a basepoint,
|
||||
/// `EdwardsBasepointTable` is approximately 4x faster.
|
||||
fn mul(self, scalar: &Scalar) -> SubgroupPoint {
|
||||
SubgroupPoint(self.0 * scalar)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Mul<&SubgroupPoint> for &Scalar {
|
||||
type Output = SubgroupPoint;
|
||||
|
||||
/// Scalar multiplication: compute `scalar * self`.
|
||||
///
|
||||
/// For scalar multiplication of a basepoint,
|
||||
/// `EdwardsBasepointTable` is approximately 4x faster.
|
||||
fn mul(self, point: &SubgroupPoint) -> SubgroupPoint {
|
||||
point * self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl MulAssign<&Scalar> for SubgroupPoint {
|
||||
fn mul_assign(&mut self, scalar: &Scalar) {
|
||||
self.0 *= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl group::Group for SubgroupPoint {
|
||||
type Scalar = Scalar;
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
use group::ff::Field;
|
||||
|
||||
// This will almost never loop, but `Group::random` is documented as returning a
|
||||
// non-identity element.
|
||||
let s = loop {
|
||||
let s: Scalar = Field::random(&mut rng);
|
||||
if !s.is_zero_vartime() {
|
||||
break s;
|
||||
}
|
||||
};
|
||||
|
||||
// This gives an element of the prime-order subgroup.
|
||||
Self::generator() * s
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
SubgroupPoint(Identity::identity())
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
SubgroupPoint(EdwardsPoint::generator())
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.0.ct_eq(&Identity::identity())
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
SubgroupPoint(self.0.double())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl GroupEncoding for SubgroupPoint {
|
||||
type Repr = <EdwardsPoint as GroupEncoding>::Repr;
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup())
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
self.0.compress().to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl PrimeGroup for SubgroupPoint {}
|
||||
|
||||
/// Ristretto has a cofactor of 1.
|
||||
#[cfg(feature = "group")]
|
||||
impl CofactorGroup for EdwardsPoint {
|
||||
type Subgroup = SubgroupPoint;
|
||||
|
||||
fn clear_cofactor(&self) -> Self::Subgroup {
|
||||
SubgroupPoint(self.mul_by_cofactor())
|
||||
}
|
||||
|
||||
fn into_subgroup(self) -> CtOption<Self::Subgroup> {
|
||||
CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
|
||||
}
|
||||
|
||||
fn is_torsion_free(&self) -> Choice {
|
||||
(self * constants::BASEPOINT_ORDER_PRIVATE).ct_eq(&Self::identity())
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------
|
||||
@ -1243,8 +1592,10 @@ impl Debug for EdwardsPoint {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{field::FieldElement, scalar::Scalar};
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
// If `group` is set, then this is already imported in super
|
||||
#[cfg(not(feature = "group"))]
|
||||
use rand_core::RngCore;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
@ -1252,8 +1603,6 @@ mod test {
|
||||
#[cfg(feature = "precomputed-tables")]
|
||||
use crate::constants::ED25519_BASEPOINT_TABLE;
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
/// X coordinate of the basepoint.
|
||||
/// = 15112221349535400772501151409588531511454012693041857206046113283949847762202
|
||||
static BASE_X_COORD_BYTES: [u8; 32] = [
|
||||
@ -1421,7 +1770,7 @@ mod test {
|
||||
/// Test that multiplication by the basepoint order kills the basepoint
|
||||
#[test]
|
||||
fn basepoint_mult_by_basepoint_order() {
|
||||
let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER);
|
||||
let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER_PRIVATE);
|
||||
assert!(should_be_id.is_identity());
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
//! Field operations defined in terms of other field operations, such as
|
||||
//! field inversion or square roots, are defined here.
|
||||
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
#![allow(unused_qualifications)]
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
@ -37,11 +37,6 @@ use crate::constants;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
pub use backend::serial::fiat_u32::field::*;
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
pub use backend::serial::fiat_u64::field::*;
|
||||
|
||||
/// A `FieldElement` represents an element of the field
|
||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||
///
|
||||
@ -50,7 +45,7 @@ cfg_if! {
|
||||
///
|
||||
/// Using formally-verified field arithmetic from fiat-crypto.
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
|
||||
pub(crate) type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
|
||||
|
||||
/// A `FieldElement` represents an element of the field
|
||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||
@ -60,25 +55,21 @@ cfg_if! {
|
||||
///
|
||||
/// Using formally-verified field arithmetic from fiat-crypto.
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
|
||||
pub(crate) type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
|
||||
} else if #[cfg(curve25519_dalek_bits = "64")] {
|
||||
pub use crate::backend::serial::u64::field::*;
|
||||
|
||||
/// A `FieldElement` represents an element of the field
|
||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||
///
|
||||
/// The `FieldElement` type is an alias for one of the platform-specific
|
||||
/// implementations.
|
||||
pub type FieldElement = backend::serial::u64::field::FieldElement51;
|
||||
pub(crate) type FieldElement = backend::serial::u64::field::FieldElement51;
|
||||
} else {
|
||||
pub use backend::serial::u32::field::*;
|
||||
|
||||
/// A `FieldElement` represents an element of the field
|
||||
/// \\( \mathbb Z / (2\^{255} - 19)\\).
|
||||
///
|
||||
/// The `FieldElement` type is an alias for one of the platform-specific
|
||||
/// implementations.
|
||||
pub type FieldElement = backend::serial::u32::field::FieldElement2625;
|
||||
pub(crate) type FieldElement = backend::serial::u32::field::FieldElement2625;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +98,7 @@ impl FieldElement {
|
||||
/// # Return
|
||||
///
|
||||
/// If negative, return `Choice(1)`. Otherwise, return `Choice(0)`.
|
||||
pub fn is_negative(&self) -> Choice {
|
||||
pub(crate) fn is_negative(&self) -> Choice {
|
||||
let bytes = self.as_bytes();
|
||||
(bytes[0] & 1).into()
|
||||
}
|
||||
@ -117,7 +108,7 @@ impl FieldElement {
|
||||
/// # Return
|
||||
///
|
||||
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
pub(crate) fn is_zero(&self) -> Choice {
|
||||
let zero = [0u8; 32];
|
||||
let bytes = self.as_bytes();
|
||||
|
||||
@ -163,11 +154,11 @@ impl FieldElement {
|
||||
(t19, t3)
|
||||
}
|
||||
|
||||
/// Given a slice of public `FieldElements`, replace each with its inverse.
|
||||
/// Given a slice of pub(crate)lic `FieldElements`, replace each with its inverse.
|
||||
///
|
||||
/// When an input `FieldElement` is zero, its value is unchanged.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_invert(inputs: &mut [FieldElement]) {
|
||||
pub(crate) fn batch_invert(inputs: &mut [FieldElement]) {
|
||||
// Montgomery’s Trick and Fast Implementation of Masked AES
|
||||
// Genelle, Prouff and Quisquater
|
||||
// Section 3.2
|
||||
@ -212,7 +203,7 @@ impl FieldElement {
|
||||
/// This function returns zero on input zero.
|
||||
#[rustfmt::skip] // keep alignment of explanatory comments
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn invert(&self) -> FieldElement {
|
||||
pub(crate) fn invert(&self) -> FieldElement {
|
||||
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
|
||||
//
|
||||
// nonzero bits of exponent
|
||||
@ -249,7 +240,7 @@ impl FieldElement {
|
||||
/// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero;
|
||||
/// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square).
|
||||
///
|
||||
pub fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
|
||||
pub(crate) fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
|
||||
// Using the same trick as in ed25519 decoding, we merge the
|
||||
// inversion, the square root, and the square test as follows.
|
||||
//
|
||||
@ -309,7 +300,7 @@ impl FieldElement {
|
||||
/// - `(Choice(0), zero) ` if `self` is zero;
|
||||
/// - `(Choice(0), +sqrt(i/self)) ` if `self` is a nonzero nonsquare;
|
||||
///
|
||||
pub fn invsqrt(&self) -> (Choice, FieldElement) {
|
||||
pub(crate) fn invsqrt(&self) -> (Choice, FieldElement) {
|
||||
FieldElement::sqrt_ratio_i(&FieldElement::ONE, self)
|
||||
}
|
||||
}
|
||||
@ -317,7 +308,6 @@ impl FieldElement {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::field::*;
|
||||
use subtle::ConditionallyNegatable;
|
||||
|
||||
/// Random element a of GF(2^255-19), from Sage
|
||||
/// a = 1070314506888354081329385823235218444233221\
|
||||
|
||||
@ -10,22 +10,40 @@
|
||||
// - 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,
|
||||
any(target_arch = "x86", target_arch = "x86_64")
|
||||
),
|
||||
feature(stdarch_x86_avx512)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
all(curve25519_dalek_backend = "simd", nightly),
|
||||
feature(avx512_target_feature)
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
#![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")]
|
||||
//------------------------------------------------------------------------
|
||||
// Linting:
|
||||
//------------------------------------------------------------------------
|
||||
#![cfg_attr(allow_unused_unsafe, allow(unused_unsafe))]
|
||||
#![warn(
|
||||
clippy::unwrap_used,
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
unused_lifetimes,
|
||||
unused_qualifications
|
||||
)]
|
||||
// Requires MSRV 1.77 as it does not allow build.rs gating
|
||||
#![allow(unexpected_cfgs)]
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// External dependencies:
|
||||
|
||||
@ -237,7 +237,7 @@ impl RistrettoPoint {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
extern crate sha2;
|
||||
use sha2;
|
||||
|
||||
use self::sha2::Sha256;
|
||||
use super::*;
|
||||
|
||||
@ -3,36 +3,44 @@ use cfg_if::cfg_if;
|
||||
cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
pub use crate::backend::serial::fiat_u32::field::FieldElement2625;
|
||||
|
||||
const fn field_element(element: [u32; 10]) -> FieldElement2625 {
|
||||
FieldElement2625(fiat_crypto::curve25519_32::fiat_25519_tight_field_element(element))
|
||||
}
|
||||
} else {
|
||||
pub use crate::backend::serial::u32::field::FieldElement2625;
|
||||
|
||||
const fn field_element(element: [u32; 10]) -> FieldElement2625 {
|
||||
FieldElement2625(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement2625 = FieldElement2625([
|
||||
pub const SQRT_ID: FieldElement2625 = field_element([
|
||||
39590824, 701138, 28659366, 23623507, 53932708, 32206357, 36326585, 24309414, 26167230, 1494357,
|
||||
]);
|
||||
|
||||
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
|
||||
pub const DP1_OVER_DM1: FieldElement2625 = FieldElement2625([
|
||||
pub const DP1_OVER_DM1: FieldElement2625 = field_element([
|
||||
58833708, 32184294, 62457071, 26110240, 19032991, 27203620, 7122892, 18068959, 51019405,
|
||||
3776288,
|
||||
]);
|
||||
|
||||
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = FieldElement2625([
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([
|
||||
54885894, 25242303, 55597453, 9067496, 51808079, 33312638, 25456129, 14121551, 54921728,
|
||||
3972023,
|
||||
]);
|
||||
|
||||
/// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters
|
||||
/// and `i = +sqrt(-1)`.
|
||||
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = FieldElement2625([
|
||||
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([
|
||||
58178520, 23970840, 26444491, 29801899, 41064376, 743696, 2900628, 27920316, 41968995, 5270573,
|
||||
]);
|
||||
|
||||
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement2625 = FieldElement2625([
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement2625 = field_element([
|
||||
38019585, 4791795, 20332186, 18653482, 46576675, 33182583, 65658549, 2817057, 12569934,
|
||||
30919145,
|
||||
]);
|
||||
|
||||
@ -3,13 +3,21 @@ use cfg_if::cfg_if;
|
||||
cfg_if! {
|
||||
if #[cfg(curve25519_dalek_backend = "fiat")] {
|
||||
pub use crate::backend::serial::fiat_u64::field::FieldElement51;
|
||||
|
||||
const fn field_element(element: [u64; 5]) -> FieldElement51 {
|
||||
FieldElement51(fiat_crypto::curve25519_64::fiat_25519_tight_field_element(element))
|
||||
}
|
||||
} else {
|
||||
pub use crate::backend::serial::u64::field::FieldElement51;
|
||||
|
||||
const fn field_element(element: [u64; 5]) -> FieldElement51 {
|
||||
FieldElement51(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement51 = FieldElement51([
|
||||
pub const SQRT_ID: FieldElement51 = field_element([
|
||||
2298852427963285,
|
||||
3837146560810661,
|
||||
4413131899466403,
|
||||
@ -18,7 +26,7 @@ pub const SQRT_ID: FieldElement51 = FieldElement51([
|
||||
]);
|
||||
|
||||
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
|
||||
pub const DP1_OVER_DM1: FieldElement51 = FieldElement51([
|
||||
pub const DP1_OVER_DM1: FieldElement51 = field_element([
|
||||
2159851467815724,
|
||||
1752228607624431,
|
||||
1825604053920671,
|
||||
@ -27,7 +35,7 @@ pub const DP1_OVER_DM1: FieldElement51 = FieldElement51([
|
||||
]);
|
||||
|
||||
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = FieldElement51([
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([
|
||||
1693982333959686,
|
||||
608509411481997,
|
||||
2235573344831311,
|
||||
@ -37,7 +45,7 @@ pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = FieldElement51([
|
||||
|
||||
/// `= -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([
|
||||
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([
|
||||
1608655899704280,
|
||||
1999971613377227,
|
||||
49908634785720,
|
||||
@ -46,7 +54,7 @@ pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = FieldElement51([
|
||||
]);
|
||||
|
||||
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = FieldElement51([
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = field_element([
|
||||
321571956990465,
|
||||
1251814006996634,
|
||||
2226845496292387,
|
||||
|
||||
@ -151,6 +151,43 @@ impl MontgomeryPoint {
|
||||
Self::mul_base(&s)
|
||||
}
|
||||
|
||||
/// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
|
||||
/// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
|
||||
///
|
||||
/// **NOTE:** You probably do not want to use this function. Almost every protocol built on
|
||||
/// Curve25519 uses _clamped multiplication_, explained
|
||||
/// [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/).
|
||||
/// When in doubt, use [`Self::mul_clamped`].
|
||||
pub fn mul_bits_be(&self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
|
||||
// Algorithm 8 of Costello-Smith 2017
|
||||
let affine_u = FieldElement::from_bytes(&self.0);
|
||||
let mut x0 = ProjectivePoint::identity();
|
||||
let mut x1 = ProjectivePoint {
|
||||
U: affine_u,
|
||||
W: FieldElement::ONE,
|
||||
};
|
||||
|
||||
// Go through the bits from most to least significant, using a sliding window of 2
|
||||
let mut prev_bit = false;
|
||||
for cur_bit in bits {
|
||||
let choice: u8 = (prev_bit ^ cur_bit) as u8;
|
||||
|
||||
debug_assert!(choice == 0 || choice == 1);
|
||||
|
||||
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
|
||||
differential_add_and_double(&mut x0, &mut x1, &affine_u);
|
||||
|
||||
prev_bit = cur_bit;
|
||||
}
|
||||
// The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
|
||||
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
|
||||
// Don't leave the bit in the stack
|
||||
#[cfg(feature = "zeroize")]
|
||||
prev_bit.zeroize();
|
||||
|
||||
x0.as_affine()
|
||||
}
|
||||
|
||||
/// View this `MontgomeryPoint` as an array of bytes.
|
||||
pub const fn as_bytes(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
@ -357,55 +394,27 @@ define_mul_variants!(
|
||||
);
|
||||
|
||||
/// Multiply this `MontgomeryPoint` by a `Scalar`.
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint {
|
||||
impl Mul<&Scalar> for &MontgomeryPoint {
|
||||
type Output = MontgomeryPoint;
|
||||
|
||||
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\).
|
||||
fn mul(self, scalar: &'b Scalar) -> MontgomeryPoint {
|
||||
// Algorithm 8 of Costello-Smith 2017
|
||||
let affine_u = FieldElement::from_bytes(&self.0);
|
||||
let mut x0 = ProjectivePoint::identity();
|
||||
let mut x1 = ProjectivePoint {
|
||||
U: affine_u,
|
||||
W: FieldElement::ONE,
|
||||
};
|
||||
|
||||
// NOTE: The below swap-double-add routine skips the first iteration, i.e., it assumes the
|
||||
// MSB of `scalar` is 0. This is allowed, since it follows from Scalar invariant #1.
|
||||
|
||||
// Go through the bits from most to least significant, using a sliding window of 2
|
||||
let mut bits = scalar.bits_le().rev();
|
||||
let mut prev_bit = bits.next().unwrap();
|
||||
for cur_bit in bits {
|
||||
let choice: u8 = (prev_bit ^ cur_bit) as u8;
|
||||
|
||||
debug_assert!(choice == 0 || choice == 1);
|
||||
|
||||
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
|
||||
differential_add_and_double(&mut x0, &mut x1, &affine_u);
|
||||
|
||||
prev_bit = cur_bit;
|
||||
}
|
||||
// The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
|
||||
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
|
||||
// Don't leave the bit in the stack
|
||||
#[cfg(feature = "zeroize")]
|
||||
prev_bit.zeroize();
|
||||
|
||||
x0.as_affine()
|
||||
/// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
|
||||
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
|
||||
// We multiply by the integer representation of the given Scalar. By scalar invariant #1,
|
||||
// the MSB is 0, so we can skip it.
|
||||
self.mul_bits_be(scalar.bits_le().rev().skip(1))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b Scalar> for MontgomeryPoint {
|
||||
fn mul_assign(&mut self, scalar: &'b Scalar) {
|
||||
impl MulAssign<&Scalar> for MontgomeryPoint {
|
||||
fn mul_assign(&mut self, scalar: &Scalar) {
|
||||
*self = (self as &MontgomeryPoint) * scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
|
||||
impl Mul<&MontgomeryPoint> for &Scalar {
|
||||
type Output = MontgomeryPoint;
|
||||
|
||||
fn mul(self, point: &'b MontgomeryPoint) -> MontgomeryPoint {
|
||||
fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
|
||||
point * self
|
||||
}
|
||||
}
|
||||
@ -422,7 +431,7 @@ mod test {
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use rand_core::RngCore;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
#[test]
|
||||
fn identity_in_different_coordinates() {
|
||||
@ -505,15 +514,33 @@ mod test {
|
||||
assert_eq!(u18, u18_unred);
|
||||
}
|
||||
|
||||
/// Returns a random point on the prime-order subgroup
|
||||
fn rand_prime_order_point(mut rng: impl RngCore + CryptoRng) -> EdwardsPoint {
|
||||
let s: Scalar = Scalar::random(&mut rng);
|
||||
EdwardsPoint::mul_base(&s)
|
||||
}
|
||||
|
||||
/// Given a bytestring that's little-endian at the byte level, return an iterator over all the
|
||||
/// bits, in little-endian order.
|
||||
fn bytestring_bits_le(x: &[u8]) -> impl DoubleEndedIterator<Item = bool> + Clone + '_ {
|
||||
let bitlen = x.len() * 8;
|
||||
(0..bitlen).map(|i| {
|
||||
// As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
|
||||
// the byte. Since self.bytes is little-endian at the byte level, this iterator is
|
||||
// little-endian on the bit level
|
||||
((x[i >> 3] >> (i & 7)) & 1u8) == 1
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn montgomery_ladder_matches_edwards_scalarmult() {
|
||||
let mut csprng = rand_core::OsRng;
|
||||
|
||||
for _ in 0..100 {
|
||||
let s: Scalar = Scalar::random(&mut csprng);
|
||||
let p_edwards = EdwardsPoint::mul_base(&s);
|
||||
let p_edwards = rand_prime_order_point(&mut csprng);
|
||||
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
|
||||
|
||||
let s: Scalar = Scalar::random(&mut csprng);
|
||||
let expected = s * p_edwards;
|
||||
let result = s * p_montgomery;
|
||||
|
||||
@ -521,6 +548,65 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that, on the prime-order subgroup, MontgomeryPoint::mul_bits_be is the same as
|
||||
// multiplying by the Scalar representation of the same bits
|
||||
#[test]
|
||||
fn montgomery_mul_bits_be() {
|
||||
let mut csprng = rand_core::OsRng;
|
||||
|
||||
for _ in 0..100 {
|
||||
// Make a random prime-order point P
|
||||
let p_edwards = rand_prime_order_point(&mut csprng);
|
||||
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
|
||||
|
||||
// Make a random integer b
|
||||
let mut bigint = [0u8; 64];
|
||||
csprng.fill_bytes(&mut bigint[..]);
|
||||
let bigint_bits_be = bytestring_bits_le(&bigint).rev();
|
||||
|
||||
// Check that bP is the same whether calculated as scalar-times-edwards or
|
||||
// integer-times-montgomery.
|
||||
let expected = Scalar::from_bytes_mod_order_wide(&bigint) * p_edwards;
|
||||
let result = p_montgomery.mul_bits_be(bigint_bits_be);
|
||||
assert_eq!(result, expected.to_montgomery())
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that MontgomeryPoint::mul_bits_be is consistent on any point, even ones that might be
|
||||
// on the curve's twist. Specifically, this tests that b₁(b₂P) == b₂(b₁P) for random
|
||||
// integers b₁, b₂ and random (curve or twist) point P.
|
||||
#[test]
|
||||
fn montgomery_mul_bits_be_twist() {
|
||||
let mut csprng = rand_core::OsRng;
|
||||
|
||||
for _ in 0..100 {
|
||||
// Make a random point P on the curve or its twist
|
||||
let p_montgomery = {
|
||||
let mut buf = [0u8; 32];
|
||||
csprng.fill_bytes(&mut buf);
|
||||
MontgomeryPoint(buf)
|
||||
};
|
||||
|
||||
// Compute two big integers b₁ and b₂
|
||||
let mut bigint1 = [0u8; 64];
|
||||
let mut bigint2 = [0u8; 64];
|
||||
csprng.fill_bytes(&mut bigint1[..]);
|
||||
csprng.fill_bytes(&mut bigint2[..]);
|
||||
|
||||
// Compute b₁P and b₂P
|
||||
let bigint1_bits_be = bytestring_bits_le(&bigint1).rev();
|
||||
let bigint2_bits_be = bytestring_bits_le(&bigint2).rev();
|
||||
let prod1 = p_montgomery.mul_bits_be(bigint1_bits_be.clone());
|
||||
let prod2 = p_montgomery.mul_bits_be(bigint2_bits_be.clone());
|
||||
|
||||
// Check that b₁(b₂P) == b₂(b₁P)
|
||||
assert_eq!(
|
||||
prod1.mul_bits_be(bigint2_bits_be),
|
||||
prod2.mul_bits_be(bigint1_bits_be)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that mul_base_clamped and mul_clamped agree
|
||||
#[test]
|
||||
fn mul_base_clamped() {
|
||||
|
||||
@ -180,6 +180,13 @@ use digest::Digest;
|
||||
use crate::constants;
|
||||
use crate::field::FieldElement;
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
use {
|
||||
group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
|
||||
rand_core::RngCore,
|
||||
subtle::CtOption,
|
||||
};
|
||||
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallyNegatable;
|
||||
use subtle::ConditionallySelectable;
|
||||
@ -246,6 +253,26 @@ impl CompressedRistretto {
|
||||
///
|
||||
/// - `None` if `self` was not the canonical encoding of a point.
|
||||
pub fn decompress(&self) -> Option<RistrettoPoint> {
|
||||
let (s_encoding_is_canonical, s_is_negative, s) = decompress::step_1(self);
|
||||
|
||||
if (!s_encoding_is_canonical | s_is_negative).into() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (ok, t_is_negative, y_is_zero, res) = decompress::step_2(s);
|
||||
|
||||
if (!ok | t_is_negative | y_is_zero).into() {
|
||||
None
|
||||
} else {
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod decompress {
|
||||
use super::*;
|
||||
|
||||
pub(super) fn step_1(repr: &CompressedRistretto) -> (Choice, Choice, FieldElement) {
|
||||
// Step 1. Check s for validity:
|
||||
// 1.a) s must be 32 bytes (we get this from the type system)
|
||||
// 1.b) s < p
|
||||
@ -257,15 +284,15 @@ impl CompressedRistretto {
|
||||
// converting back to bytes, and checking that we get the
|
||||
// original input, since our encoding routine is canonical.
|
||||
|
||||
let s = FieldElement::from_bytes(self.as_bytes());
|
||||
let s = FieldElement::from_bytes(repr.as_bytes());
|
||||
let s_bytes_check = s.as_bytes();
|
||||
let s_encoding_is_canonical = s_bytes_check[..].ct_eq(self.as_bytes());
|
||||
let s_encoding_is_canonical = s_bytes_check[..].ct_eq(repr.as_bytes());
|
||||
let s_is_negative = s.is_negative();
|
||||
|
||||
if (!s_encoding_is_canonical | s_is_negative).into() {
|
||||
return None;
|
||||
}
|
||||
(s_encoding_is_canonical, s_is_negative, s)
|
||||
}
|
||||
|
||||
pub(super) fn step_2(s: FieldElement) -> (Choice, Choice, Choice, RistrettoPoint) {
|
||||
// Step 2. Compute (X:Y:Z:T).
|
||||
let one = FieldElement::ONE;
|
||||
let ss = s.square();
|
||||
@ -292,16 +319,17 @@ impl CompressedRistretto {
|
||||
// t == ((1+as²) sqrt(4s²/(ad(1+as²)² - (1-as²)²)))/(1-as²)
|
||||
let t = &x * &y;
|
||||
|
||||
if (!ok | t.is_negative() | y.is_zero()).into() {
|
||||
None
|
||||
} else {
|
||||
Some(RistrettoPoint(EdwardsPoint {
|
||||
(
|
||||
ok,
|
||||
t.is_negative(),
|
||||
y.is_zero(),
|
||||
RistrettoPoint(EdwardsPoint {
|
||||
X: x,
|
||||
Y: y,
|
||||
Z: one,
|
||||
T: t,
|
||||
}))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,7 +364,7 @@ impl TryFrom<&[u8]> for CompressedRistretto {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Visitor;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for RistrettoPoint {
|
||||
@ -379,7 +407,7 @@ impl<'de> Deserialize<'de> for RistrettoPoint {
|
||||
impl<'de> Visitor<'de> for RistrettoPointVisitor {
|
||||
type Value = RistrettoPoint;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
formatter.write_str("a valid point in Ristretto format")
|
||||
}
|
||||
|
||||
@ -388,6 +416,7 @@ impl<'de> Deserialize<'de> for RistrettoPoint {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
@ -414,7 +443,7 @@ impl<'de> Deserialize<'de> for CompressedRistretto {
|
||||
impl<'de> Visitor<'de> for CompressedRistrettoVisitor {
|
||||
type Value = CompressedRistretto;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
formatter.write_str("32 bytes of data")
|
||||
}
|
||||
|
||||
@ -423,6 +452,7 @@ impl<'de> Deserialize<'de> for CompressedRistretto {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
@ -623,7 +653,7 @@ impl RistrettoPoint {
|
||||
///
|
||||
/// This method is not public because it's just used for hashing
|
||||
/// to a point -- proper elligator support is deferred for now.
|
||||
pub fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
|
||||
pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
|
||||
let i = &constants::SQRT_M1;
|
||||
let d = &constants::EDWARDS_D;
|
||||
let one_minus_d_sq = &constants::ONE_MINUS_EDWARDS_D_SQUARED;
|
||||
@ -1125,13 +1155,13 @@ impl ConditionallySelectable for RistrettoPoint {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
impl Debug for CompressedRistretto {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "CompressedRistretto: {:?}", self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RistrettoPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let coset = self.coset4();
|
||||
write!(
|
||||
f,
|
||||
@ -1141,6 +1171,86 @@ impl Debug for RistrettoPoint {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// group traits
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Use the full trait path to avoid Group::identity overlapping Identity::identity in the
|
||||
// rest of the module (e.g. tests).
|
||||
#[cfg(feature = "group")]
|
||||
impl group::Group for RistrettoPoint {
|
||||
type Scalar = Scalar;
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
// NOTE: this is duplicated due to different `rng` bounds
|
||||
let mut uniform_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut uniform_bytes);
|
||||
RistrettoPoint::from_uniform_bytes(&uniform_bytes)
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Identity::identity()
|
||||
}
|
||||
|
||||
fn generator() -> Self {
|
||||
constants::RISTRETTO_BASEPOINT_POINT
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.ct_eq(&Identity::identity())
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
self + self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl GroupEncoding for RistrettoPoint {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
let (s_encoding_is_canonical, s_is_negative, s) =
|
||||
decompress::step_1(&CompressedRistretto(*bytes));
|
||||
|
||||
let s_is_valid = s_encoding_is_canonical & !s_is_negative;
|
||||
|
||||
let (ok, t_is_negative, y_is_zero, res) = decompress::step_2(s);
|
||||
|
||||
CtOption::new(res, s_is_valid & ok & !t_is_negative & !y_is_zero)
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
// Just use the checked API; the checks we could skip aren't expensive.
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
self.compress().to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl PrimeGroup for RistrettoPoint {}
|
||||
|
||||
/// Ristretto has a cofactor of 1.
|
||||
#[cfg(feature = "group")]
|
||||
impl CofactorGroup for RistrettoPoint {
|
||||
type Subgroup = Self;
|
||||
|
||||
fn clear_cofactor(&self) -> Self::Subgroup {
|
||||
*self
|
||||
}
|
||||
|
||||
fn into_subgroup(self) -> CtOption<Self::Subgroup> {
|
||||
CtOption::new(self, Choice::from(1))
|
||||
}
|
||||
|
||||
fn is_torsion_free(&self) -> Choice {
|
||||
Choice::from(1)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Zeroize traits
|
||||
// ------------------------------------------------------------------------
|
||||
@ -1167,8 +1277,6 @@ impl Zeroize for RistrettoPoint {
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::edwards::CompressedEdwardsY;
|
||||
use crate::scalar::Scalar;
|
||||
use crate::traits::Identity;
|
||||
|
||||
use rand_core::OsRng;
|
||||
|
||||
@ -1270,7 +1378,7 @@ mod test {
|
||||
let bp_compressed_ristretto = constants::RISTRETTO_BASEPOINT_POINT.compress();
|
||||
let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0;
|
||||
// Check that bp_recaf differs from bp by a point of order 4
|
||||
let diff = &constants::RISTRETTO_BASEPOINT_POINT.0 - bp_recaf;
|
||||
let diff = constants::RISTRETTO_BASEPOINT_POINT.0 - bp_recaf;
|
||||
let diff4 = diff.mul_by_pow_2(2);
|
||||
assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
|
||||
}
|
||||
@ -1681,7 +1789,7 @@ mod test {
|
||||
];
|
||||
// Check that onewaymap(input) == output for all the above vectors
|
||||
for (input, output) in test_vectors {
|
||||
let Q = RistrettoPoint::from_uniform_bytes(&input);
|
||||
let Q = RistrettoPoint::from_uniform_bytes(input);
|
||||
assert_eq!(&Q.compress(), output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,8 +112,6 @@
|
||||
//! has been enabled.
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
use core::convert::TryInto;
|
||||
use core::fmt::Debug;
|
||||
use core::iter::{Product, Sum};
|
||||
use core::ops::Index;
|
||||
@ -124,6 +122,14 @@ use core::ops::{Sub, SubAssign};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
use group::ff::{Field, FromUniformBytes, PrimeField};
|
||||
#[cfg(feature = "group-bits")]
|
||||
use group::ff::{FieldBits, PrimeFieldBits};
|
||||
|
||||
#[cfg(any(test, feature = "group"))]
|
||||
use rand_core::RngCore;
|
||||
|
||||
#[cfg(any(test, feature = "rand_core"))]
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
@ -279,7 +285,7 @@ impl Scalar {
|
||||
}
|
||||
|
||||
impl Debug for Scalar {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Scalar{{\n\tbytes: {:?},\n}}", &self.bytes)
|
||||
}
|
||||
}
|
||||
@ -394,7 +400,7 @@ impl ConditionallySelectable for Scalar {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::de::Visitor;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
@ -424,7 +430,7 @@ impl<'de> Deserialize<'de> for Scalar {
|
||||
impl<'de> Visitor<'de> for ScalarVisitor {
|
||||
type Value = Scalar;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
formatter.write_str(
|
||||
"a sequence of 32 bytes whose little-endian interpretation is less than the \
|
||||
basepoint order ℓ",
|
||||
@ -436,13 +442,14 @@ impl<'de> Deserialize<'de> for Scalar {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
Option::from(Scalar::from_canonical_bytes(bytes))
|
||||
.ok_or_else(|| serde::de::Error::custom(&"scalar was not canonically encoded"))
|
||||
.ok_or_else(|| serde::de::Error::custom("scalar was not canonically encoded"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,7 +831,7 @@ impl Scalar {
|
||||
}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
zeroize::Zeroize::zeroize(&mut scratch);
|
||||
Zeroize::zeroize(&mut scratch);
|
||||
|
||||
ret
|
||||
}
|
||||
@ -1201,6 +1208,141 @@ impl UnpackedScalar {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl Field for Scalar {
|
||||
const ZERO: Self = Self::ZERO;
|
||||
const ONE: Self = Self::ONE;
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
// NOTE: this is duplicated due to different `rng` bounds
|
||||
let mut scalar_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut scalar_bytes);
|
||||
Self::from_bytes_mod_order_wide(&scalar_bytes)
|
||||
}
|
||||
|
||||
fn square(&self) -> Self {
|
||||
self * self
|
||||
}
|
||||
|
||||
fn double(&self) -> Self {
|
||||
self + self
|
||||
}
|
||||
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
CtOption::new(self.invert(), !self.is_zero())
|
||||
}
|
||||
|
||||
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
|
||||
#[allow(unused_qualifications)]
|
||||
group::ff::helpers::sqrt_ratio_generic(num, div)
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
#[allow(unused_qualifications)]
|
||||
group::ff::helpers::sqrt_tonelli_shanks(
|
||||
self,
|
||||
[
|
||||
0xcb02_4c63_4b9e_ba7d,
|
||||
0x029b_df3b_d45e_f39a,
|
||||
0x0000_0000_0000_0000,
|
||||
0x0200_0000_0000_0000,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl PrimeField for Scalar {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||
Self::from_canonical_bytes(repr)
|
||||
}
|
||||
|
||||
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
|
||||
// Check that the high bit is not set
|
||||
if (repr[31] >> 7) != 0u8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let candidate = Scalar { bytes: repr };
|
||||
|
||||
if candidate == candidate.reduce() {
|
||||
Some(candidate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn to_repr(&self) -> Self::Repr {
|
||||
self.to_bytes()
|
||||
}
|
||||
|
||||
fn is_odd(&self) -> Choice {
|
||||
Choice::from(self.as_bytes()[0] & 1)
|
||||
}
|
||||
|
||||
const MODULUS: &'static str =
|
||||
"0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed";
|
||||
const NUM_BITS: u32 = 253;
|
||||
const CAPACITY: u32 = 252;
|
||||
|
||||
const TWO_INV: Self = Self {
|
||||
bytes: [
|
||||
0xf7, 0xe9, 0x7a, 0x2e, 0x8d, 0x31, 0x09, 0x2c, 0x6b, 0xce, 0x7b, 0x51, 0xef, 0x7c,
|
||||
0x6f, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x08,
|
||||
],
|
||||
};
|
||||
const MULTIPLICATIVE_GENERATOR: Self = Self {
|
||||
bytes: [
|
||||
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
],
|
||||
};
|
||||
const S: u32 = 2;
|
||||
const ROOT_OF_UNITY: Self = Self {
|
||||
bytes: [
|
||||
0xd4, 0x07, 0xbe, 0xeb, 0xdf, 0x75, 0x87, 0xbe, 0xfe, 0x83, 0xce, 0x42, 0x53, 0x56,
|
||||
0xf0, 0x0e, 0x7a, 0xc2, 0xc1, 0xab, 0x60, 0x6d, 0x3d, 0x7d, 0xe7, 0x81, 0x79, 0xe0,
|
||||
0x10, 0x73, 0x4a, 0x09,
|
||||
],
|
||||
};
|
||||
const ROOT_OF_UNITY_INV: Self = Self {
|
||||
bytes: [
|
||||
0x19, 0xcc, 0x37, 0x71, 0x3a, 0xed, 0x8a, 0x99, 0xd7, 0x18, 0x29, 0x60, 0x8b, 0xa3,
|
||||
0xee, 0x05, 0x86, 0x3d, 0x3e, 0x54, 0x9f, 0x92, 0xc2, 0x82, 0x18, 0x7e, 0x86, 0x1f,
|
||||
0xef, 0x8c, 0xb5, 0x06,
|
||||
],
|
||||
};
|
||||
const DELTA: Self = Self {
|
||||
bytes: [
|
||||
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "group-bits")]
|
||||
impl PrimeFieldBits for Scalar {
|
||||
type ReprBits = [u8; 32];
|
||||
|
||||
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||
self.to_repr().into()
|
||||
}
|
||||
|
||||
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||
constants::BASEPOINT_ORDER_PRIVATE.to_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
impl FromUniformBytes<64> for Scalar {
|
||||
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
||||
Scalar::from_bytes_mod_order_wide(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read one or more u64s stored as little endian bytes.
|
||||
///
|
||||
/// ## Panics
|
||||
@ -1240,6 +1382,7 @@ fn read_le_u64_into(src: &[u8], dst: &mut [u64]) {
|
||||
///
|
||||
/// See [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/) for
|
||||
/// more details.
|
||||
#[must_use]
|
||||
pub const fn clamp_integer(mut bytes: [u8; 32]) -> [u8; 32] {
|
||||
bytes[0] &= 0b1111_1000;
|
||||
bytes[31] &= 0b0111_1111;
|
||||
@ -1250,13 +1393,10 @@ pub const fn clamp_integer(mut bytes: [u8; 32]) -> [u8; 32] {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::constants;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use rand::RngCore;
|
||||
|
||||
/// x = 2238329342913194256032495932344128051776374960164957527413114840482143558222
|
||||
pub static X: Scalar = Scalar {
|
||||
bytes: [
|
||||
@ -1475,13 +1615,13 @@ pub(crate) mod test {
|
||||
#[cfg(feature = "alloc")]
|
||||
fn impl_product() {
|
||||
// Test that product works for non-empty iterators
|
||||
let X_Y_vector = vec![X, Y];
|
||||
let X_Y_vector = [X, Y];
|
||||
let should_be_X_times_Y: Scalar = X_Y_vector.iter().product();
|
||||
assert_eq!(should_be_X_times_Y, X_TIMES_Y);
|
||||
|
||||
// Test that product works for the empty iterator
|
||||
let one = Scalar::ONE;
|
||||
let empty_vector = vec![];
|
||||
let empty_vector = [];
|
||||
let should_be_one: Scalar = empty_vector.iter().product();
|
||||
assert_eq!(should_be_one, one);
|
||||
|
||||
@ -1506,13 +1646,13 @@ pub(crate) mod test {
|
||||
fn impl_sum() {
|
||||
// Test that sum works for non-empty iterators
|
||||
let two = Scalar::from(2u64);
|
||||
let one_vector = vec![Scalar::ONE, Scalar::ONE];
|
||||
let one_vector = [Scalar::ONE, Scalar::ONE];
|
||||
let should_be_two: Scalar = one_vector.iter().sum();
|
||||
assert_eq!(should_be_two, two);
|
||||
|
||||
// Test that sum works for the empty iterator
|
||||
let zero = Scalar::ZERO;
|
||||
let empty_vector = vec![];
|
||||
let empty_vector = [];
|
||||
let should_be_zero: Scalar = empty_vector.iter().sum();
|
||||
assert_eq!(should_be_zero, zero);
|
||||
|
||||
@ -1812,6 +1952,56 @@ pub(crate) mod test {
|
||||
assert_eq!(sx + s1, Scalar::from(x + 1));
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
#[test]
|
||||
fn ff_constants() {
|
||||
assert_eq!(Scalar::from(2u64) * Scalar::TWO_INV, Scalar::ONE);
|
||||
|
||||
assert_eq!(
|
||||
Scalar::ROOT_OF_UNITY * Scalar::ROOT_OF_UNITY_INV,
|
||||
Scalar::ONE,
|
||||
);
|
||||
|
||||
// ROOT_OF_UNITY^{2^s} mod m == 1
|
||||
assert_eq!(
|
||||
Scalar::ROOT_OF_UNITY.pow(&[1u64 << Scalar::S, 0, 0, 0]),
|
||||
Scalar::ONE,
|
||||
);
|
||||
|
||||
// DELTA^{t} mod m == 1
|
||||
assert_eq!(
|
||||
Scalar::DELTA.pow(&[
|
||||
0x9604_98c6_973d_74fb,
|
||||
0x0537_be77_a8bd_e735,
|
||||
0x0000_0000_0000_0000,
|
||||
0x0400_0000_0000_0000,
|
||||
]),
|
||||
Scalar::ONE,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "group")]
|
||||
#[test]
|
||||
fn ff_impls() {
|
||||
assert!(bool::from(Scalar::ZERO.is_even()));
|
||||
assert!(bool::from(Scalar::ONE.is_odd()));
|
||||
assert!(bool::from(Scalar::from(2u64).is_even()));
|
||||
assert!(bool::from(Scalar::DELTA.is_even()));
|
||||
|
||||
assert!(bool::from(Field::invert(&Scalar::ZERO).is_none()));
|
||||
assert_eq!(Field::invert(&X).unwrap(), XINV);
|
||||
|
||||
let x_sq = X.square();
|
||||
// We should get back either the positive or negative root.
|
||||
assert!([X, -X].contains(&x_sq.sqrt().unwrap()));
|
||||
|
||||
assert_eq!(Scalar::from_repr_vartime(X.to_repr()), Some(X));
|
||||
assert_eq!(Scalar::from_repr_vartime([0xff; 32]), None);
|
||||
|
||||
assert_eq!(Scalar::from_repr(X.to_repr()).unwrap(), X);
|
||||
assert!(bool::from(Scalar::from_repr([0xff; 32]).is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_read_le_u64_into_should_panic_on_bad_input() {
|
||||
|
||||
@ -15,9 +15,8 @@
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use subtle;
|
||||
|
||||
use crate::scalar::{clamp_integer, Scalar};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Public Traits
|
||||
@ -41,7 +40,7 @@ pub trait IsIdentity {
|
||||
/// constructor.
|
||||
impl<T> IsIdentity for T
|
||||
where
|
||||
T: subtle::ConstantTimeEq + Identity,
|
||||
T: ConstantTimeEq + Identity,
|
||||
{
|
||||
fn is_identity(&self) -> bool {
|
||||
self.ct_eq(&T::identity()).into()
|
||||
@ -259,7 +258,7 @@ pub trait VartimeMultiscalarMul {
|
||||
scalars,
|
||||
points.into_iter().map(|P| Some(P.borrow().clone())),
|
||||
)
|
||||
.unwrap()
|
||||
.expect("should return some point")
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,7 +364,7 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||
dynamic_scalars,
|
||||
dynamic_points.into_iter().map(|P| Some(P.borrow().clone())),
|
||||
)
|
||||
.unwrap()
|
||||
.expect("should return some point")
|
||||
}
|
||||
|
||||
/// Given `static_scalars`, an iterator of public scalars
|
||||
@ -409,6 +408,7 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||
/// This trait is only for debugging/testing, since it should be
|
||||
/// impossible for a `curve25519-dalek` user to construct an invalid
|
||||
/// point.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) trait ValidityCheck {
|
||||
/// Checks whether the point is on the curve. Not CT.
|
||||
fn is_valid(&self) -> bool;
|
||||
|
||||
@ -83,7 +83,7 @@ macro_rules! impl_lookup_table {
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for $name<T> {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}(", stringify!($name))?;
|
||||
|
||||
for x in self.0.iter() {
|
||||
@ -193,7 +193,7 @@ impl<T: Copy> NafLookupTable5<T> {
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for NafLookupTable5<T> {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "NafLookupTable5({:?})", self.0)
|
||||
}
|
||||
}
|
||||
@ -240,7 +240,7 @@ impl<T: Copy> NafLookupTable8<T> {
|
||||
|
||||
#[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 {
|
||||
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])?;
|
||||
|
||||
@ -6,8 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
Entries are listed in reverse chronological order per undeprecated major series.
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 2.x series
|
||||
|
||||
## 2.1.1
|
||||
|
||||
* Fix nightly SIMD build
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* Add `SigningKey::to_scalar_bytes` for getting the unclamped scalar from a signing key
|
||||
* Loosened `signature` dependency to allow version 2.2
|
||||
|
||||
## 2.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.0.0-rc.3"
|
||||
version = "2.1.1"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"isis lovecruft <isis@patternsinthevoid.net>",
|
||||
@ -9,7 +9,8 @@ authors = [
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/dalek-cryptography/ed25519-dalek"
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek"
|
||||
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
documentation = "https://docs.rs/ed25519-dalek"
|
||||
keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"]
|
||||
categories = ["cryptography", "no-std"]
|
||||
@ -27,8 +28,9 @@ 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 }
|
||||
signature = { version = ">=2.0, <2.3", optional = true, default-features = false }
|
||||
sha2 = { version = "0.10", default-features = false }
|
||||
subtle = { version = "2.3.0", default-features = false }
|
||||
|
||||
# optional features
|
||||
merlin = { version = "3", default-features = false, optional = true }
|
||||
@ -38,17 +40,18 @@ zeroize = { version = "1.5", default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
curve25519-dalek = { version = "4", path = "../curve25519-dalek", default-features = false, features = ["digest", "rand_core"] }
|
||||
x25519-dalek = { version = "2", path = "../x25519-dalek", default-features = false, features = ["static_secrets"] }
|
||||
blake2 = "0.10"
|
||||
sha3 = "0.10"
|
||||
hex = "0.4"
|
||||
bincode = "1.0"
|
||||
serde_json = "1.0"
|
||||
criterion = { version = "0.4", features = ["html_reports"] }
|
||||
hex-literal = "0.3"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
hex-literal = "0.4"
|
||||
rand = "0.8"
|
||||
rand_core = { version = "0.6.4", default-features = false }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = { version = "0.5" }
|
||||
toml = { version = "0.7" }
|
||||
|
||||
[[bench]]
|
||||
name = "ed25519_benchmarks"
|
||||
|
||||
@ -5,20 +5,10 @@ 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"
|
||||
ed25519-dalek = "2"
|
||||
```
|
||||
|
||||
# Feature Flags
|
||||
@ -41,7 +31,7 @@ This crate is `#[no_std]` compatible with `default-features = false`.
|
||||
|
||||
# Major Changes
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of this crate.
|
||||
See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past versions of this crate.
|
||||
|
||||
## Breaking Changes in 2.0.0
|
||||
|
||||
@ -53,7 +43,7 @@ See [CHANGELOG.md](CHANGELOG.md) for a list of changes made in past version of t
|
||||
* 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])
|
||||
* 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()`
|
||||
|
||||
@ -73,7 +63,7 @@ SemVer exemptions are outlined below for MSRV and public API.
|
||||
| 2.x | 1.60 |
|
||||
| 1.x | 1.41 |
|
||||
|
||||
From 2.x and on, MSRV changes will be accompanied by a minor version bump.
|
||||
From 2.x onwards, MSRV changes will be accompanied by a minor version bump.
|
||||
|
||||
## Public API SemVer Exemptions
|
||||
|
||||
@ -140,7 +130,7 @@ Backend selection details and instructions can be found in the [curve25519-dalek
|
||||
|
||||
# Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
See [CONTRIBUTING.md](../CONTRIBUTING.md)
|
||||
|
||||
# Batch Signature Verification
|
||||
|
||||
|
||||
@ -64,8 +64,7 @@ mod ed25519_benches {
|
||||
.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 signatures: Vec<Signature> = keypairs.iter().map(|key| key.sign(msg)).collect();
|
||||
let verifying_keys: Vec<_> =
|
||||
keypairs.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::iter::once;
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
@ -176,7 +175,7 @@ pub fn verify_batch(
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
h.update(signatures[i].r_bytes());
|
||||
h.update(verifying_keys[i].as_bytes());
|
||||
h.update(&messages[i]);
|
||||
h.update(messages[i]);
|
||||
*h.finalize().as_ref()
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -80,6 +80,8 @@ impl<'k, 'v, K> Context<'k, 'v, K> {
|
||||
|
||||
#[cfg(all(test, feature = "digest"))]
|
||||
mod test {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::{Signature, SigningKey, VerifyingKey};
|
||||
use curve25519_dalek::digest::Digest;
|
||||
use ed25519::signature::{DigestSigner, DigestVerifier};
|
||||
|
||||
@ -156,11 +156,11 @@ where
|
||||
/// [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>(
|
||||
pub fn raw_sign_prehashed<CtxDigest, MsgDigest>(
|
||||
esk: &ExpandedSecretKey,
|
||||
prehashed_message: MsgDigest,
|
||||
verifying_key: &VerifyingKey,
|
||||
context: Option<&'a [u8]>,
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
MsgDigest: Digest<OutputSize = U64>,
|
||||
@ -204,6 +204,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use super::*;
|
||||
|
||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||
@ -226,8 +228,8 @@ mod test {
|
||||
#[test]
|
||||
fn sign_verify_nonspec() {
|
||||
// Generate the keypair
|
||||
let mut rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(&mut rng);
|
||||
let rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(rng);
|
||||
let vk = VerifyingKey::from(&esk);
|
||||
|
||||
let msg = b"Then one day, a piano fell on my head";
|
||||
@ -245,8 +247,8 @@ mod test {
|
||||
use curve25519_dalek::digest::Digest;
|
||||
|
||||
// Generate the keypair
|
||||
let mut rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(&mut rng);
|
||||
let rng = OsRng;
|
||||
let esk = ExpandedSecretKey::random(rng);
|
||||
let vk = VerifyingKey::from(&esk);
|
||||
|
||||
// Hash the message
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! // $ cargo add ed25519_dalek --features rand_core
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use ed25519_dalek::SigningKey;
|
||||
//! use ed25519_dalek::Signature;
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
//! An ed25519 signature.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||
@ -58,7 +57,7 @@ impl Clone for InternalSignature {
|
||||
}
|
||||
|
||||
impl Debug for InternalSignature {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
//! ed25519 signing keys.
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8;
|
||||
|
||||
@ -19,6 +21,7 @@ use rand_core::CryptoRngCore;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use sha2::Sha512;
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
|
||||
use curve25519_dalek::{
|
||||
digest::{generic_array::typenum::U64, Digest},
|
||||
@ -54,9 +57,10 @@ use crate::{
|
||||
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)]
|
||||
// Invariant: `verifying_key` is always the public key of
|
||||
// `secret_key`. This prevents the signing function oracle attack
|
||||
// described in https://github.com/MystenLabs/ed25519-unsafe-libs
|
||||
#[derive(Clone)]
|
||||
pub struct SigningKey {
|
||||
/// The secret half of this signing key.
|
||||
pub(crate) secret_key: SecretKey,
|
||||
@ -109,6 +113,12 @@ impl SigningKey {
|
||||
self.secret_key
|
||||
}
|
||||
|
||||
/// Convert this [`SigningKey`] into a [`SecretKey`] reference
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &SecretKey {
|
||||
&self.secret_key
|
||||
}
|
||||
|
||||
/// Construct a [`SigningKey`] from the bytes of a `VerifyingKey` and `SecretKey`.
|
||||
///
|
||||
/// # Inputs
|
||||
@ -121,7 +131,7 @@ impl SigningKey {
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` whose okay value is an EdDSA [`SigningKey`] or whose error value
|
||||
/// is an `SignatureError` describing the error that occurred.
|
||||
/// is a `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);
|
||||
@ -473,15 +483,43 @@ impl SigningKey {
|
||||
self.verifying_key.verify_strict(message, signature)
|
||||
}
|
||||
|
||||
/// Convert this signing key into a byte representation of a(n) (unreduced) Curve25519 scalar.
|
||||
/// Convert this signing key into a byte representation of an unreduced, unclamped Curve25519
|
||||
/// scalar. This is NOT the same thing as `self.to_scalar().to_bytes()`, since `to_scalar()`
|
||||
/// performs a clamping step, which changes the value of the resulting 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()`.
|
||||
/// by this function are a valid corresponding [`StaticSecret`](https://docs.rs/x25519-dalek/2.0.0/x25519_dalek/struct.StaticSecret.html#impl-From%3C%5Bu8;+32%5D%3E-for-StaticSecret)
|
||||
/// 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
|
||||
/// We do NOT recommend using a signing/verifying key for encryption. 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_bytes(&self) -> [u8; 32] {
|
||||
// Per the spec, the ed25519 secret key sk is expanded to
|
||||
// (scalar_bytes, hash_prefix) = SHA-512(sk)
|
||||
// where the two outputs are both 32 bytes. scalar_bytes is what we return. Its clamped and
|
||||
// reduced form is what we use for signing (see impl ExpandedSecretKey)
|
||||
let mut buf = [0u8; 32];
|
||||
let scalar_and_hash_prefix = Sha512::default().chain_update(self.secret_key).finalize();
|
||||
buf.copy_from_slice(&scalar_and_hash_prefix[..32]);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Convert this signing key into a Curve25519 scalar. This is computed by clamping and
|
||||
/// reducing the output of [`Self::to_scalar_bytes`].
|
||||
///
|
||||
/// This can be used anywhere where a Curve25519 scalar is used as a private key, e.g., in
|
||||
/// [`crypto_box`](https://docs.rs/crypto_box/0.9.1/crypto_box/struct.SecretKey.html#impl-From%3CScalar%3E-for-SecretKey).
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// We do NOT recommend using a signing/verifying key for encryption. 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.
|
||||
///
|
||||
@ -489,6 +527,11 @@ impl SigningKey {
|
||||
/// 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 {
|
||||
// Per the spec, the ed25519 secret key sk is expanded to
|
||||
// (scalar_bytes, hash_prefix) = SHA-512(sk)
|
||||
// where the two outputs are both 32 bytes. To use for signing, scalar_bytes must be
|
||||
// clamped and reduced (see ExpandedSecretKey::from_bytes). We return the clamped and
|
||||
// reduced form.
|
||||
ExpandedSecretKey::from(&self.secret_key).scalar
|
||||
}
|
||||
}
|
||||
@ -499,6 +542,14 @@ impl AsRef<VerifyingKey> for SigningKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SigningKey {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("SigningKey")
|
||||
.field("verifying_key", &self.verifying_key)
|
||||
.finish_non_exhaustive() // avoids printing `secret_key`
|
||||
}
|
||||
}
|
||||
|
||||
impl KeypairRef for SigningKey {
|
||||
type VerifyingKey = VerifyingKey;
|
||||
}
|
||||
@ -583,6 +634,20 @@ impl TryFrom<&[u8]> for SigningKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for SigningKey {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.secret_key.ct_eq(&other.secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SigningKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SigningKey {}
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
impl Drop for SigningKey {
|
||||
fn drop(&mut self) {
|
||||
@ -677,15 +742,12 @@ impl<'d> Deserialize<'d> for SigningKey {
|
||||
impl<'de> serde::de::Visitor<'de> for SigningKeyVisitor {
|
||||
type Value = SigningKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
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_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
|
||||
SigningKey::try_from(bytes).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
@ -693,6 +755,7 @@ impl<'d> Deserialize<'d> for SigningKey {
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
@ -782,11 +845,11 @@ impl ExpandedSecretKey {
|
||||
#[cfg(feature = "digest")]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn raw_sign_prehashed<'a, CtxDigest, MsgDigest>(
|
||||
pub(crate) fn raw_sign_prehashed<CtxDigest, MsgDigest>(
|
||||
&self,
|
||||
prehashed_message: MsgDigest,
|
||||
verifying_key: &VerifyingKey,
|
||||
context: Option<&'a [u8]>,
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<Signature, SignatureError>
|
||||
where
|
||||
CtxDigest: Digest<OutputSize = U64>,
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
//! ed25519 public keys.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt::Debug;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
@ -505,6 +504,11 @@ impl VerifyingKey {
|
||||
pub fn to_montgomery(&self) -> MontgomeryPoint {
|
||||
self.point.to_montgomery()
|
||||
}
|
||||
|
||||
/// Return this verifying key in Edwards form.
|
||||
pub fn to_edwards(&self) -> EdwardsPoint {
|
||||
self.point
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<ed25519::Signature> for VerifyingKey {
|
||||
@ -563,6 +567,12 @@ impl TryFrom<&[u8]> for VerifyingKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerifyingKey> for EdwardsPoint {
|
||||
fn from(vk: VerifyingKey) -> EdwardsPoint {
|
||||
vk.point
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
|
||||
impl pkcs8::EncodePublicKey for VerifyingKey {
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
|
||||
@ -632,15 +642,12 @@ impl<'d> Deserialize<'d> for VerifyingKey {
|
||||
impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor {
|
||||
type Value = VerifyingKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
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_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
|
||||
VerifyingKey::try_from(bytes).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
@ -649,6 +656,7 @@ impl<'d> Deserialize<'d> for VerifyingKey {
|
||||
{
|
||||
let mut bytes = [0u8; 32];
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq
|
||||
.next_element()?
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
//! Integration tests for ed25519-dalek.
|
||||
|
||||
use curve25519_dalek;
|
||||
#![allow(clippy::items_after_test_module)]
|
||||
|
||||
use ed25519_dalek::*;
|
||||
|
||||
@ -27,10 +27,11 @@ mod vectors {
|
||||
scalar::Scalar,
|
||||
traits::IsIdentity,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "digest"))]
|
||||
use sha2::{digest::Digest, Sha512};
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
ops::Neg,
|
||||
@ -63,10 +64,10 @@ mod vectors {
|
||||
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: 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();
|
||||
@ -161,13 +162,13 @@ mod vectors {
|
||||
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([1]);
|
||||
h.update([c.len() as u8]);
|
||||
h.update(c);
|
||||
}
|
||||
h.update(&signature_r.compress().as_bytes());
|
||||
h.update(signature_r.compress().as_bytes());
|
||||
h.update(&pub_key.compress().as_bytes()[..]);
|
||||
h.update(&message);
|
||||
h.update(message);
|
||||
Scalar::from_hash(h)
|
||||
}
|
||||
|
||||
@ -223,7 +224,7 @@ mod vectors {
|
||||
// = 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 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());
|
||||
@ -265,7 +266,7 @@ mod vectors {
|
||||
|
||||
// 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 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)
|
||||
@ -288,52 +289,47 @@ mod vectors {
|
||||
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 signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
good_sig = signing_key.sign(&good);
|
||||
bad_sig = signing_key.sign(&bad);
|
||||
let good_sig: Signature = signing_key.sign(good);
|
||||
let bad_sig: Signature = 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(),
|
||||
signing_key.verify(good, &good_sig).is_ok(),
|
||||
"Verification of a valid signature failed!"
|
||||
);
|
||||
assert!(
|
||||
verifying_key.verify_strict(&good, &good_sig).is_ok(),
|
||||
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(),
|
||||
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(),
|
||||
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(),
|
||||
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(),
|
||||
verifying_key.verify_strict(bad, &good_sig).is_err(),
|
||||
"Strict verification of a signature on a different message passed!"
|
||||
);
|
||||
}
|
||||
@ -341,10 +337,6 @@ mod integrations {
|
||||
#[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";
|
||||
|
||||
@ -365,12 +357,12 @@ mod integrations {
|
||||
|
||||
let context: &[u8] = b"testing testing 1 2 3";
|
||||
|
||||
signing_key = SigningKey::generate(&mut csprng);
|
||||
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
good_sig = signing_key
|
||||
let good_sig: Signature = signing_key
|
||||
.sign_prehashed(prehashed_good1, Some(context))
|
||||
.unwrap();
|
||||
bad_sig = signing_key
|
||||
let bad_sig: Signature = signing_key
|
||||
.sign_prehashed(prehashed_bad1, Some(context))
|
||||
.unwrap();
|
||||
|
||||
@ -427,9 +419,9 @@ mod integrations {
|
||||
let mut signing_keys: Vec<SigningKey> = Vec::new();
|
||||
let mut signatures: Vec<Signature> = Vec::new();
|
||||
|
||||
for i in 0..messages.len() {
|
||||
for msg in messages {
|
||||
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
|
||||
signatures.push(signing_key.sign(&messages[i]));
|
||||
signatures.push(signing_key.sign(msg));
|
||||
signing_keys.push(signing_key);
|
||||
}
|
||||
let verifying_keys: Vec<VerifyingKey> =
|
||||
@ -466,6 +458,29 @@ mod integrations {
|
||||
assert_eq!(v, "Second public key");
|
||||
assert_eq!(m.len(), 2usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn montgomery_and_edwards_conversion() {
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
let signing_key = SigningKey::generate(&mut rng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
|
||||
let ed = verifying_key.to_edwards();
|
||||
|
||||
// Check that to_edwards and From return same result:
|
||||
assert_eq!(ed, curve25519_dalek::EdwardsPoint::from(verifying_key));
|
||||
|
||||
// The verifying key serialization is simply the compressed Edwards point
|
||||
assert_eq!(verifying_key.to_bytes(), ed.compress().0);
|
||||
|
||||
// Check that modulo sign, to_montgomery().to_edwards() returns the original point
|
||||
let monty = verifying_key.to_montgomery();
|
||||
let via_monty0 = monty.to_edwards(0).unwrap();
|
||||
let via_monty1 = monty.to_edwards(1).unwrap();
|
||||
|
||||
assert!(via_monty0 != via_monty1);
|
||||
assert!(ed == via_monty0 || ed == via_monty1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
@ -477,6 +492,8 @@ struct Demo {
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
mod serialisation {
|
||||
#![allow(clippy::zero_prefixed_literal)]
|
||||
|
||||
use super::*;
|
||||
|
||||
// The size for bincode to serialize the length of a byte array.
|
||||
@ -547,7 +564,7 @@ mod serialisation {
|
||||
// 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)
|
||||
let de_err = serde_json::from_str::<VerifyingKey>(encoded_verifying_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
@ -560,7 +577,7 @@ mod serialisation {
|
||||
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)
|
||||
let de_err = serde_json::from_str::<VerifyingKey>(encoded_verifying_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
@ -575,6 +592,7 @@ mod serialisation {
|
||||
let encoded_signing_key: Vec<u8> = bincode::serialize(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap();
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
@ -586,6 +604,7 @@ mod serialisation {
|
||||
let encoded_signing_key = serde_json::to_string(&signing_key).unwrap();
|
||||
let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap();
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..SECRET_KEY_LENGTH {
|
||||
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
|
||||
}
|
||||
@ -596,7 +615,7 @@ mod serialisation {
|
||||
// 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)
|
||||
let de_err = serde_json::from_str::<SigningKey>(encoded_signing_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
@ -609,7 +628,7 @@ mod serialisation {
|
||||
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)
|
||||
let de_err = serde_json::from_str::<SigningKey>(encoded_signing_key_too_long)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
|
||||
@ -22,7 +22,7 @@ const VERIFY_ALLOWED_EDGECASES: &[Flag] = &[
|
||||
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
|
||||
/// Each variant describes a specific 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
|
||||
@ -89,7 +89,7 @@ impl From<IntermediateTestVector> for TestVector {
|
||||
let msg = tv.msg.as_bytes().to_vec();
|
||||
|
||||
// Unwrap the Option<Set<Flag>>
|
||||
let flags = tv.flags.unwrap_or_else(Default::default);
|
||||
let flags = tv.flags.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
number,
|
||||
|
||||
@ -4,63 +4,77 @@ 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()
|
||||
}
|
||||
use x25519_dalek::{PublicKey as XPublicKey, StaticSecret as XStaticSecret};
|
||||
|
||||
/// 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 ed_secret_key_a = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
|
||||
let ed_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 ed_signing_key_a = SigningKey::from_bytes(&ed_secret_key_a);
|
||||
let ed_signing_key_b = SigningKey::from_bytes(&ed_secret_key_b);
|
||||
|
||||
let scalar_a = ed25519_signing_key_a.to_scalar();
|
||||
let scalar_b = ed25519_signing_key_b.to_scalar();
|
||||
// Create an x25519 static secret from the ed25519 signing key
|
||||
let scalar_bytes_a = ed_signing_key_a.to_scalar_bytes();
|
||||
let scalar_bytes_b = ed_signing_key_b.to_scalar_bytes();
|
||||
let x_static_secret_a = XStaticSecret::from(scalar_bytes_a);
|
||||
let x_static_secret_b = XStaticSecret::from(scalar_bytes_b);
|
||||
|
||||
// Compute the secret scalars too
|
||||
let scalar_a = ed_signing_key_a.to_scalar();
|
||||
let scalar_b = ed_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_bytes_a, &Sha512::digest(ed_secret_key_a)[..32]);
|
||||
assert_eq!(scalar_bytes_b, &Sha512::digest(ed_secret_key_b)[..32]);
|
||||
|
||||
// Compare the scalar with the clamped and reduced scalar bytes
|
||||
assert_eq!(
|
||||
scalar_a.to_bytes(),
|
||||
clamp_and_reduce(&Sha512::digest(ed25519_secret_key_a)[..32]),
|
||||
scalar_a,
|
||||
Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_a))
|
||||
);
|
||||
assert_eq!(
|
||||
scalar_b.to_bytes(),
|
||||
clamp_and_reduce(&Sha512::digest(ed25519_secret_key_b)[..32]),
|
||||
scalar_b,
|
||||
Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_b))
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
let x_public_key_a = XPublicKey::from(&x_static_secret_a);
|
||||
let x_public_key_b = XPublicKey::from(&x_static_secret_b);
|
||||
assert_eq!(
|
||||
x25519_public_key_a.to_bytes(),
|
||||
x_public_key_a.to_bytes(),
|
||||
hex!("d85e07ec22b0ad881537c2f44d662d1a143cf830c57aca4305d85c7a90f6b62e")
|
||||
);
|
||||
assert_eq!(
|
||||
x25519_public_key_b.to_bytes(),
|
||||
x_public_key_b.to_bytes(),
|
||||
hex!("25c704c594b88afc00a76b69d1ed2b984d7e22550f3ed0802d04fbcd07d38d47")
|
||||
);
|
||||
|
||||
// Test the claim made in the comments of SigningKey::to_scalar_bytes, i.e., that the resulting
|
||||
// scalar is a valid private key for the x25519 pubkey represented by
|
||||
// `sk.verifying_key().to_montgomery()`
|
||||
assert_eq!(
|
||||
ed_signing_key_a.verifying_key().to_montgomery().as_bytes(),
|
||||
x_public_key_a.as_bytes()
|
||||
);
|
||||
assert_eq!(
|
||||
ed_signing_key_b.verifying_key().to_montgomery().as_bytes(),
|
||||
x_public_key_b.as_bytes()
|
||||
);
|
||||
|
||||
// Check that Diffie-Hellman works
|
||||
let expected_shared_secret =
|
||||
hex!("5166f24a6918368e2af831a4affadd97af0ac326bdf143596c045967cc00230e");
|
||||
|
||||
assert_eq!(
|
||||
(x25519_public_key_a * scalar_b).to_bytes(),
|
||||
expected_shared_secret
|
||||
x_static_secret_a.diffie_hellman(&x_public_key_b).to_bytes(),
|
||||
expected_shared_secret,
|
||||
);
|
||||
assert_eq!(
|
||||
(x25519_public_key_b * scalar_a).to_bytes(),
|
||||
expected_shared_secret
|
||||
x_static_secret_b.diffie_hellman(&x_public_key_a).to_bytes(),
|
||||
expected_shared_secret,
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,6 +6,10 @@ Entries are listed in reverse chronological order.
|
||||
|
||||
* Note: All `x255919-dalek` 2.x releases are in sync with the underlying `curve25519-dalek` 4.x releases.
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* Fix nightly SIMD build
|
||||
|
||||
## 2.0.0-rc.3
|
||||
|
||||
* `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication.
|
||||
|
||||
@ -6,7 +6,7 @@ edition = "2021"
|
||||
# - update html_root_url
|
||||
# - update CHANGELOG
|
||||
# - if any changes were made to README.md, mirror them in src/lib.rs docs
|
||||
version = "2.0.0-rc.3"
|
||||
version = "2.0.1"
|
||||
authors = [
|
||||
"Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||
"DebugSteven <debugsteven@gmail.com>",
|
||||
@ -14,8 +14,8 @@ authors = [
|
||||
]
|
||||
readme = "README.md"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/dalek-cryptography/x25519-dalek"
|
||||
homepage = "https://dalek.rs/"
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek"
|
||||
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
documentation = "https://docs.rs/x25519-dalek"
|
||||
categories = ["cryptography", "no-std"]
|
||||
keywords = ["cryptography", "curve25519", "key-exchange", "x25519", "diffie-hellman"]
|
||||
@ -45,7 +45,7 @@ zeroize = { version = "1", default-features = false, optional = true, features =
|
||||
|
||||
[dev-dependencies]
|
||||
bincode = "1"
|
||||
criterion = "0.4.0"
|
||||
criterion = "0.5"
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
|
||||
[[bench]]
|
||||
|
||||
@ -53,9 +53,9 @@ shared secret with Bob by doing:
|
||||
```rust
|
||||
# use rand_core::OsRng;
|
||||
# use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
# let alice_secret = EphemeralSecret::new(OsRng);
|
||||
# let alice_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let alice_public = PublicKey::from(&alice_secret);
|
||||
# let bob_secret = EphemeralSecret::new(OsRng);
|
||||
# let bob_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let bob_public = PublicKey::from(&bob_secret);
|
||||
let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
|
||||
```
|
||||
@ -65,9 +65,9 @@ Similarly, Bob computes a shared secret by doing:
|
||||
```rust
|
||||
# use rand_core::OsRng;
|
||||
# use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
# let alice_secret = EphemeralSecret::new(OsRng);
|
||||
# let alice_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let alice_public = PublicKey::from(&alice_secret);
|
||||
# let bob_secret = EphemeralSecret::new(OsRng);
|
||||
# let bob_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let bob_public = PublicKey::from(&bob_secret);
|
||||
let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
|
||||
```
|
||||
@ -77,9 +77,9 @@ These secrets are the same:
|
||||
```rust
|
||||
# use rand_core::OsRng;
|
||||
# use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
# let alice_secret = EphemeralSecret::new(OsRng);
|
||||
# let alice_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let alice_public = PublicKey::from(&alice_secret);
|
||||
# let bob_secret = EphemeralSecret::new(OsRng);
|
||||
# let bob_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||
# let bob_public = PublicKey::from(&bob_secret);
|
||||
# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
|
||||
# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
|
||||
@ -100,7 +100,7 @@ To install, add the following to your project's `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
x25519-dalek = "2.0.0-rc.3"
|
||||
x25519-dalek = "2"
|
||||
```
|
||||
|
||||
# MSRV
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
// README.md as the crate documentation.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "bench", feature(test))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
@ -101,7 +101,7 @@ impl EphemeralSecret {
|
||||
/// Generate a new [`EphemeralSecret`].
|
||||
#[cfg(feature = "getrandom")]
|
||||
pub fn random() -> Self {
|
||||
Self::random_from_rng(&mut rand_core::OsRng)
|
||||
Self::random_from_rng(rand_core::OsRng)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ impl ReusableSecret {
|
||||
/// Generate a new [`ReusableSecret`].
|
||||
#[cfg(feature = "getrandom")]
|
||||
pub fn random() -> Self {
|
||||
Self::random_from_rng(&mut rand_core::OsRng)
|
||||
Self::random_from_rng(rand_core::OsRng)
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ impl StaticSecret {
|
||||
/// Generate a new [`StaticSecret`].
|
||||
#[cfg(feature = "getrandom")]
|
||||
pub fn random() -> Self {
|
||||
Self::random_from_rng(&mut rand_core::OsRng)
|
||||
Self::random_from_rng(rand_core::OsRng)
|
||||
}
|
||||
|
||||
/// Extract this key's bytes for serialization.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user