chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"c33294e46d9522fee05a49173fb0c9807390f854cc71b5da81e38bb6021be653",".travis.yml":"acc0b3a2394eff00e773fadc9002121077c639d2e4dd302f1a96e6821899b805","CHANGELOG.md":"1198cba348d83e8d4e3ea155e8f2e3b9d31b5f8d63f330f880bcfb5d74ac1ddd","Cargo.lock":"f95d6bf38a307d3b423480ac37b8eb6546c8251301f7a5b4863d9c29305b8938","Cargo.toml":"65e415110f13ea42e3205a6d49426257c3242aa1f46ffca2e6cbafae0b205b14","Cargo.toml.orig":"1086d10b520d48fece15a5da12a5f3d8f1ea1de432294965d20a268b9875778b","LICENSE":"7a313964a6e050794d2ad57f4863c11f6bbe055c0ae6ce2cf3b9fc45150bada3","README.md":"1a042eab412b1ee625d34e44e9b8d3a3bc815065f5ba6795fc2c33e8b62ae743","benches/ed25519_benchmarks.rs":"984b425fc49153e6029144351089bfd7931e887bfa6962a7669fce6380b053c3","docs/assets/ed25519-malleability.png":"4cdb06fd41ec66467ae46f31d7381374c510ce94725744ab5ed86b3e6319be2d","docs/assets/rustdoc-include-katex-header.html":"088c9bb27bdc5c240c00ff1c06d583d77995d53b76421116609a751ca4a54b59","src/batch.rs":"37f1335ebf129abe704fb019be549a05612ceb58013885420c0014694c284fcd","src/constants.rs":"95ae51f859483bf58654338e13b9c9bff90cce51ca0375657ff167be5935b19e","src/context.rs":"8df67feb4368b2b649ad2b654d6cb86f2eae9457f8e247e1edd5f023b281866a","src/errors.rs":"761f62273cbb2e58df922fc44eec988d532cde0bf28beb60ecc57bc2a0b35136","src/hazmat.rs":"9f1fe8f5d1e0f949d49e50a3202f699b6a425685341584fc61459a4c955f4847","src/lib.rs":"a419b059459147b7ba6a9e52aec77cac0eac6c3a667c6e92c15751ebac70bc5f","src/signature.rs":"1058f5119312e3521594a560ac1be6fb5d8016b9b010e4f281cefc304716d56c","src/signing.rs":"dcf83ccfaf98d9ab144ec7a6ec4576499ccdfe8308c0714b4e890a1aa7a22b82","src/verifying.rs":"baaf05c1503a2cd66785ca01c215e26781d09dd6db9545ebbb0f2c4242117848","src/verifying/stream.rs":"2a275173366251aaaa007af1f45b7e81bec3702a1cad17f6dd3165b0fb4c2b5a","tests/ed25519.rs":"56f2d5829128e38376d22aa161cbfb8b1cb698aca56082c44e5a5b4dce232c67","tests/examples/pkcs8-v1.der":"80b885d1af82668ad76d1c5a1a916b5bfc23d247e2f7f450e45b5c1024843e96","tests/examples/pkcs8-v2.der":"78f1cd2051065b209431249093f218b0e01a5fd0b9e756db820d4893d9dbbf55","tests/examples/pubkey.der":"a1e9156054e04fac899ae9f275132cdc07a5dbc4ea2c2ad3a1ffc6e0d253681f","tests/pkcs8.rs":"173abc9933fce8e2892fdf0dcd57bb7f0252e9ed21f1e67f874e0b1b7d06c4ef","tests/validation_criteria.rs":"65dbf427ea420104d0ddc2505bb720c927df7306efc65d090cf16bea3880987e","tests/x25519.rs":"0ab6f05d63a7c57824ea13322888609a37f10874c595153b3b417a1a9aab429f"},"package":"70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "8016d6d9b9cdbaa681f24147e0b9377cc8cef934"
},
"path_in_vcs": "ed25519-dalek"
}

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

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

55
vendor/ed25519-dalek/CHANGELOG.md vendored Normal file
View File

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

1148
vendor/ed25519-dalek/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

226
vendor/ed25519-dalek/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,226 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.81"
name = "ed25519-dalek"
version = "2.2.0"
authors = [
"isis lovecruft <isis@patternsinthevoid.net>",
"Tony Arcieri <bascule@gmail.com>",
"Michael Rosenberg <michael@mrosenberg.pub>",
]
build = false
exclude = [
".gitignore",
"TESTVECTORS",
"VALIDATIONVECTORS",
"res/*",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust."
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/ed25519-dalek"
readme = "README.md"
keywords = [
"cryptography",
"ed25519",
"curve25519",
"signature",
"ECC",
]
categories = [
"cryptography",
"no-std",
]
license = "BSD-3-Clause"
repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek"
[package.metadata.docs.rs]
features = [
"batch",
"digest",
"hazmat",
"pem",
"serde",
]
rustdoc-args = [
"--html-in-header",
"docs/assets/rustdoc-include-katex-header.html",
"--cfg",
"docsrs",
]
[features]
alloc = [
"curve25519-dalek/alloc",
"ed25519/alloc",
"serde?/alloc",
"zeroize/alloc",
]
asm = ["sha2/asm"]
batch = [
"alloc",
"merlin",
"rand_core",
]
default = [
"fast",
"std",
"zeroize",
]
digest = ["signature/digest"]
fast = ["curve25519-dalek/precomputed-tables"]
hazmat = []
legacy_compatibility = ["curve25519-dalek/legacy_compatibility"]
pem = [
"alloc",
"ed25519/pem",
"pkcs8",
]
pkcs8 = ["ed25519/pkcs8"]
rand_core = ["dep:rand_core"]
serde = [
"dep:serde",
"ed25519/serde",
]
std = [
"alloc",
"ed25519/std",
"serde?/std",
"sha2/std",
]
zeroize = [
"dep:zeroize",
"curve25519-dalek/zeroize",
]
[lib]
name = "ed25519_dalek"
path = "src/lib.rs"
[[test]]
name = "ed25519"
path = "tests/ed25519.rs"
[[test]]
name = "pkcs8"
path = "tests/pkcs8.rs"
[[test]]
name = "validation_criteria"
path = "tests/validation_criteria.rs"
[[test]]
name = "x25519"
path = "tests/x25519.rs"
[[bench]]
name = "ed25519_benchmarks"
path = "benches/ed25519_benchmarks.rs"
harness = false
required-features = ["rand_core"]
[dependencies.curve25519-dalek]
version = "4"
features = ["digest"]
default-features = false
[dependencies.ed25519]
version = ">=2.2, <2.3"
default-features = false
[dependencies.merlin]
version = "3"
optional = true
default-features = false
[dependencies.rand_core]
version = "0.6.4"
optional = true
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
default-features = false
[dependencies.sha2]
version = "0.10"
default-features = false
[dependencies.signature]
version = ">=2.0, <2.3"
optional = true
default-features = false
[dependencies.subtle]
version = "2.3.0"
default-features = false
[dependencies.zeroize]
version = "1.5"
optional = true
default-features = false
[dev-dependencies.bincode]
version = "1.0"
[dev-dependencies.blake2]
version = "0.10"
[dev-dependencies.criterion]
version = "0.5"
features = ["html_reports"]
[dev-dependencies.curve25519-dalek]
version = "4"
features = [
"digest",
"rand_core",
]
default-features = false
[dev-dependencies.hex]
version = "0.4"
[dev-dependencies.hex-literal]
version = "0.4"
[dev-dependencies.rand]
version = "0.8"
[dev-dependencies.rand_core]
version = "0.6.4"
default-features = false
[dev-dependencies.serde]
version = "1.0"
features = ["derive"]
[dev-dependencies.serde_json]
version = "1.0"
[dev-dependencies.sha3]
version = "0.10"
[dev-dependencies.toml]
version = "0.7"
[dev-dependencies.x25519-dalek]
version = "2"
features = ["static_secrets"]
default-features = false

28
vendor/ed25519-dalek/LICENSE vendored Normal file
View File

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

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

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

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

241
vendor/ed25519-dalek/src/batch.rs vendored Normal file
View File

@@ -0,0 +1,241 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! Batch signature verification.
use alloc::vec::Vec;
use core::iter::once;
use curve25519_dalek::constants;
use curve25519_dalek::edwards::EdwardsPoint;
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::IsIdentity;
use curve25519_dalek::traits::VartimeMultiscalarMul;
pub use curve25519_dalek::digest::Digest;
use merlin::Transcript;
use rand_core::RngCore;
use sha2::Sha512;
use crate::errors::InternalError;
use crate::errors::SignatureError;
use crate::signature::InternalSignature;
use crate::VerifyingKey;
/// An implementation of `rand_core::RngCore` which does nothing. This is necessary because merlin
/// demands an `Rng` as input to `TranscriptRngBuilder::finalize()`. Using this with `finalize()`
/// yields a PRG whose input is the hashed transcript.
struct ZeroRng;
impl rand_core::RngCore for ZeroRng {
fn next_u32(&mut self) -> u32 {
rand_core::impls::next_u32_via_fill(self)
}
fn next_u64(&mut self) -> u64 {
rand_core::impls::next_u64_via_fill(self)
}
/// A no-op function which leaves the destination bytes for randomness unchanged.
///
/// In this case, the internal merlin code is initialising the destination
/// by doing `[0u8; …]`, which means that when we call
/// `merlin::TranscriptRngBuilder.finalize()`, rather than rekeying the
/// STROBE state based on external randomness, we're doing an
/// `ENC_{state}(00000000000000000000000000000000)` operation, which is
/// identical to the STROBE `MAC` operation.
fn fill_bytes(&mut self, _dest: &mut [u8]) {}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
// `TranscriptRngBuilder::finalize()` requires a `CryptoRng`
impl rand_core::CryptoRng for ZeroRng {}
// We write our own gen() function so we don't need to pull in the rand crate
fn gen_u128<R: RngCore>(rng: &mut R) -> u128 {
let mut buf = [0u8; 16];
rng.fill_bytes(&mut buf);
u128::from_le_bytes(buf)
}
/// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`.
///
/// # Inputs
///
/// * `messages` is a slice of byte slices, one per signed message.
/// * `signatures` is a slice of `Signature`s.
/// * `verifying_keys` is a slice of `VerifyingKey`s.
///
/// # Returns
///
/// * A `Result` whose `Ok` value is an empty tuple and whose `Err` value is a
/// `SignatureError` containing a description of the internal error which
/// occurred.
///
/// ## On Deterministic Nonces
///
/// The nonces for batch signature verification are derived purely from the inputs to this function
/// themselves.
///
/// In any sigma protocol it is wise to include as much context pertaining
/// to the public state in the protocol as possible, to avoid malleability
/// attacks where an adversary alters publics in an algebraic manner that
/// manages to satisfy the equations for the protocol in question.
///
/// For ed25519 batch verification we include the following as scalars in the protocol transcript:
///
/// * All of the computed `H(R||A||M)`s to the protocol transcript, and
/// * All of the `s` components of each signature.
///
/// The former, while not quite as elegant as adding the `R`s, `A`s, and
/// `M`s separately, saves us a bit of context hashing since the
/// `H(R||A||M)`s need to be computed for the verification equation anyway.
///
/// The latter prevents a malleability attack wherein an adversary, without access
/// to the signing key(s), can take any valid signature, `(s,R)`, and swap
/// `s` with `s' = -z1`. This doesn't constitute a signature forgery, merely
/// a vulnerability, as the resulting signature will not pass single
/// signature verification. (Thanks to Github users @real_or_random and
/// @jonasnick for pointing out this malleability issue.)
///
/// # Examples
///
/// ```
/// use ed25519_dalek::{
/// verify_batch, SigningKey, VerifyingKey, Signer, Signature,
/// };
/// use rand::rngs::OsRng;
///
/// # fn main() {
/// let mut csprng = OsRng;
/// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect();
/// let msg: &[u8] = b"They're good dogs Brant";
/// let messages: Vec<_> = (0..64).map(|_| msg).collect();
/// let signatures: Vec<_> = signing_keys.iter().map(|key| key.sign(&msg)).collect();
/// let verifying_keys: Vec<_> = signing_keys.iter().map(|key| key.verifying_key()).collect();
///
/// let result = verify_batch(&messages, &signatures, &verifying_keys);
/// assert!(result.is_ok());
/// # }
/// ```
#[allow(non_snake_case)]
pub fn verify_batch(
messages: &[&[u8]],
signatures: &[ed25519::Signature],
verifying_keys: &[VerifyingKey],
) -> Result<(), SignatureError> {
// Return an Error if any of the vectors were not the same size as the others.
if signatures.len() != messages.len()
|| signatures.len() != verifying_keys.len()
|| verifying_keys.len() != messages.len()
{
return Err(InternalError::ArrayLength {
name_a: "signatures",
length_a: signatures.len(),
name_b: "messages",
length_b: messages.len(),
name_c: "verifying_keys",
length_c: verifying_keys.len(),
}
.into());
}
// Make a transcript which logs all inputs to this function
let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification");
// We make one optimization in the transcript: since we will end up computing H(R || A || M)
// for each (R, A, M) triplet, we will feed _that_ into our transcript rather than each R, A, M
// individually. Since R and A are fixed-length, this modification is secure so long as SHA-512
// is collision-resistant.
// It suffices to take `verifying_keys[i].as_bytes()` even though a `VerifyingKey` has two
// fields, and `as_bytes()` only returns the bytes of the first. This is because of an
// invariant guaranteed by `VerifyingKey`: the second field is always the (unique)
// decompression of the first. Thus, the serialized first field is a unique representation of
// the entire `VerifyingKey`.
let hrams: Vec<[u8; 64]> = (0..signatures.len())
.map(|i| {
// Compute H(R || A || M), where
// R = sig.R
// A = verifying key
// M = msg
let mut h: Sha512 = Sha512::default();
h.update(signatures[i].r_bytes());
h.update(verifying_keys[i].as_bytes());
h.update(messages[i]);
*h.finalize().as_ref()
})
.collect();
// Update transcript with the hashes above. This covers verifying_keys, messages, and the R
// half of signatures
for hram in hrams.iter() {
transcript.append_message(b"hram", hram);
}
// Update transcript with the rest of the data. This covers the s half of the signatures
for sig in signatures {
transcript.append_message(b"sig.s", sig.s_bytes());
}
// All function inputs have now been hashed into the transcript. Finalize it and use it as
// randomness for the batch verification.
let mut rng = transcript.build_rng().finalize(&mut ZeroRng);
// Convert all signatures to `InternalSignature`
let signatures = signatures
.iter()
.map(InternalSignature::try_from)
.collect::<Result<Vec<_>, _>>()?;
// Convert the H(R || A || M) values into scalars
let hrams: Vec<Scalar> = hrams
.iter()
.map(Scalar::from_bytes_mod_order_wide)
.collect();
// Select a random 128-bit scalar for each signature.
let zs: Vec<Scalar> = signatures
.iter()
.map(|_| Scalar::from(gen_u128(&mut rng)))
.collect();
// Compute the basepoint coefficient, ∑ s[i]z[i] (mod l)
let B_coefficient: Scalar = signatures
.iter()
.map(|sig| sig.s)
.zip(zs.iter())
.map(|(s, z)| z * s)
.sum();
// Multiply each H(R || A || M) by the random value
let zhrams = hrams.iter().zip(zs.iter()).map(|(hram, z)| hram * z);
let Rs = signatures.iter().map(|sig| sig.R.decompress());
let As = verifying_keys.iter().map(|pk| Some(pk.point));
let B = once(Some(constants::ED25519_BASEPOINT_POINT));
// Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0
let id = EdwardsPoint::optional_multiscalar_mul(
once(-B_coefficient).chain(zs.iter().cloned()).chain(zhrams),
B.chain(Rs).chain(As),
)
.ok_or(InternalError::Verify)?;
if id.is_identity() {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}

32
vendor/ed25519-dalek/src/constants.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! Common constants such as buffer sizes for keypairs and signatures.
/// The length of a ed25519 `Signature`, in bytes.
pub const SIGNATURE_LENGTH: usize = 64;
/// The length of a ed25519 `SecretKey`, in bytes.
pub const SECRET_KEY_LENGTH: usize = 32;
/// The length of an ed25519 `PublicKey`, in bytes.
pub const PUBLIC_KEY_LENGTH: usize = 32;
/// The length of an ed25519 `Keypair`, in bytes.
pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH;
/// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes.
const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32;
/// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes.
const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32;
/// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes.
pub const EXPANDED_SECRET_KEY_LENGTH: usize =
EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH;

112
vendor/ed25519-dalek/src/context.rs vendored Normal file
View File

@@ -0,0 +1,112 @@
use crate::{InternalError, SignatureError};
/// Ed25519 contexts as used by Ed25519ph.
///
/// Contexts are domain separator strings that can be used to isolate uses of
/// the algorithm between different protocols (which is very hard to reliably do
/// otherwise) and between different uses within the same protocol.
///
/// To create a context, call either of the following:
///
/// - [`SigningKey::with_context`](crate::SigningKey::with_context)
/// - [`VerifyingKey::with_context`](crate::VerifyingKey::with_context)
///
/// For more information, see [RFC8032 § 8.3](https://www.rfc-editor.org/rfc/rfc8032#section-8.3).
///
/// # Example
///
#[cfg_attr(all(feature = "digest", feature = "rand_core"), doc = "```")]
#[cfg_attr(
any(not(feature = "digest"), not(feature = "rand_core")),
doc = "```ignore"
)]
/// # fn main() {
/// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512};
/// # use curve25519_dalek::digest::Digest;
/// # use rand::rngs::OsRng;
/// use ed25519_dalek::{DigestSigner, DigestVerifier};
///
/// # let mut csprng = OsRng;
/// # let signing_key = SigningKey::generate(&mut csprng);
/// # let verifying_key = signing_key.verifying_key();
/// let context_str = b"Local Channel 3";
/// let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
///
/// // Signer
/// let signing_context = signing_key.with_context(context_str).unwrap();
/// let signature = signing_context.sign_digest(prehashed_message.clone());
///
/// // Verifier
/// let verifying_context = verifying_key.with_context(context_str).unwrap();
/// let verified: bool = verifying_context
/// .verify_digest(prehashed_message, &signature)
/// .is_ok();
///
/// # assert!(verified);
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct Context<'k, 'v, K> {
/// Key this context is being used with.
key: &'k K,
/// Context value: a bytestring no longer than 255 octets.
value: &'v [u8],
}
impl<'k, 'v, K> Context<'k, 'v, K> {
/// Maximum length of the context value in octets.
pub const MAX_LENGTH: usize = 255;
/// Create a new Ed25519ph context.
pub(crate) fn new(key: &'k K, value: &'v [u8]) -> Result<Self, SignatureError> {
if value.len() <= Self::MAX_LENGTH {
Ok(Self { key, value })
} else {
Err(SignatureError::from(InternalError::PrehashedContextLength))
}
}
/// Borrow the key.
pub fn key(&self) -> &'k K {
self.key
}
/// Borrow the context string value.
pub fn value(&self) -> &'v [u8] {
self.value
}
}
#[cfg(all(test, feature = "digest"))]
mod test {
#![allow(clippy::unwrap_used)]
use crate::{Signature, SigningKey, VerifyingKey};
use curve25519_dalek::digest::Digest;
use ed25519::signature::{DigestSigner, DigestVerifier};
use rand::rngs::OsRng;
use sha2::Sha512;
#[test]
fn context_correctness() {
let mut csprng = OsRng;
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
let verifying_key: VerifyingKey = signing_key.verifying_key();
let context_str = b"Local Channel 3";
let prehashed_message = Sha512::default().chain_update(b"Stay tuned for more news at 7");
// Signer
let signing_context = signing_key.with_context(context_str).unwrap();
let signature: Signature = signing_context.sign_digest(prehashed_message.clone());
// Verifier
let verifying_context = verifying_key.with_context(context_str).unwrap();
let verified: bool = verifying_context
.verify_digest(prehashed_message, &signature)
.is_ok();
assert!(verified);
}
}

119
vendor/ed25519-dalek/src/errors.rs vendored Normal file
View File

@@ -0,0 +1,119 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! Errors which may occur when parsing keys and/or signatures to or from wire formats.
// rustc seems to think the typenames in match statements (e.g. in
// Display) should be snake cased, for some reason.
#![allow(non_snake_case)]
use core::fmt;
use core::fmt::Display;
#[cfg(feature = "std")]
use std::error::Error;
/// Internal errors. Most application-level developers will likely not
/// need to pay any attention to these.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub(crate) enum InternalError {
PointDecompression,
ScalarFormat,
/// An error in the length of bytes handed to a constructor.
///
/// To use this, pass a string specifying the `name` of the type which is
/// returning the error, and the `length` in bytes which its constructor
/// expects.
BytesLength {
name: &'static str,
length: usize,
},
/// The verification equation wasn't satisfied
Verify,
/// Two arrays did not match in size, making the called signature
/// verification method impossible.
#[cfg(feature = "batch")]
ArrayLength {
name_a: &'static str,
length_a: usize,
name_b: &'static str,
length_b: usize,
name_c: &'static str,
length_c: usize,
},
/// An ed25519ph signature can only take up to 255 octets of context.
#[cfg(feature = "digest")]
PrehashedContextLength,
/// A mismatched (public, secret) key pair.
MismatchedKeypair,
}
impl Display for InternalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
InternalError::PointDecompression => write!(f, "Cannot decompress Edwards point"),
InternalError::ScalarFormat => write!(f, "Cannot use scalar with high-bit set"),
InternalError::BytesLength { name: n, length: l } => {
write!(f, "{} must be {} bytes in length", n, l)
}
InternalError::Verify => write!(f, "Verification equation was not satisfied"),
#[cfg(feature = "batch")]
InternalError::ArrayLength {
name_a: na,
length_a: la,
name_b: nb,
length_b: lb,
name_c: nc,
length_c: lc,
} => write!(
f,
"Arrays must be the same length: {} has length {},
{} has length {}, {} has length {}.",
na, la, nb, lb, nc, lc
),
#[cfg(feature = "digest")]
InternalError::PrehashedContextLength => write!(
f,
"An ed25519ph signature can only take up to 255 octets of context"
),
InternalError::MismatchedKeypair => write!(f, "Mismatched Keypair detected"),
}
}
}
#[cfg(feature = "std")]
impl Error for InternalError {}
/// Errors which may occur while processing signatures and keypairs.
///
/// This error may arise due to:
///
/// * Being given bytes with a length different to what was expected.
///
/// * A problem decompressing `r`, a curve point, in the `Signature`, or the
/// curve point for a `PublicKey`.
///
/// * A problem with the format of `s`, a scalar, in the `Signature`. This
/// is only raised if the high-bit of the scalar was set. (Scalars must
/// only be constructed from 255-bit integers.)
///
/// * Failure of a signature to satisfy the verification equation.
pub type SignatureError = ed25519::signature::Error;
impl From<InternalError> for SignatureError {
#[cfg(not(feature = "std"))]
fn from(_err: InternalError) -> SignatureError {
SignatureError::new()
}
#[cfg(feature = "std")]
fn from(err: InternalError) -> SignatureError {
SignatureError::from_source(err)
}
}

359
vendor/ed25519-dalek/src/hazmat.rs vendored Normal file
View File

@@ -0,0 +1,359 @@
//! Low-level interfaces to ed25519 functions
//!
//! # ⚠️ Warning: Hazmat
//!
//! These primitives are easy-to-misuse low-level interfaces.
//!
//! If you are an end user / non-expert in cryptography, **do not use any of these functions**.
//! Failure to use them correctly can lead to catastrophic failures including **full private key
//! recovery.**
// Permit dead code because 1) this module is only public when the `hazmat` feature is set, and 2)
// even without `hazmat` we still need this module because this is where `ExpandedSecretKey` is
// defined.
#![allow(dead_code)]
use core::fmt::Debug;
use crate::{InternalError, SignatureError};
use curve25519_dalek::scalar::{clamp_integer, Scalar};
use subtle::{Choice, ConstantTimeEq};
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
// These are used in the functions that are made public when the hazmat feature is set
use crate::{Signature, VerifyingKey};
use curve25519_dalek::digest::{generic_array::typenum::U64, Digest};
/// Contains the secret scalar and domain separator used for generating signatures.
///
/// This is used internally for signing.
///
/// In the usual Ed25519 signing algorithm, `scalar` and `hash_prefix` are defined such that
/// `scalar || hash_prefix = H(sk)` where `sk` is the signing key and `H` is SHA-512.
/// **WARNING:** Deriving the values for these fields in any other way can lead to full key
/// recovery, as documented in [`raw_sign`] and [`raw_sign_prehashed`].
///
/// Instances of this secret are automatically overwritten with zeroes when they fall out of scope.
pub struct ExpandedSecretKey {
/// The secret scalar used for signing
pub scalar: Scalar,
/// The domain separator used when hashing the message to generate the pseudorandom `r` value
pub hash_prefix: [u8; 32],
}
impl Debug for ExpandedSecretKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ExpandedSecretKey").finish_non_exhaustive() // avoids printing secrets
}
}
impl ConstantTimeEq for ExpandedSecretKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.scalar.ct_eq(&other.scalar) & self.hash_prefix.ct_eq(&other.hash_prefix)
}
}
impl PartialEq for ExpandedSecretKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for ExpandedSecretKey {}
#[cfg(feature = "zeroize")]
impl Drop for ExpandedSecretKey {
fn drop(&mut self) {
self.scalar.zeroize();
self.hash_prefix.zeroize()
}
}
#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for ExpandedSecretKey {}
// Some conversion methods for `ExpandedSecretKey`. The signing methods are defined in
// `signing.rs`, since we need them even when `not(feature = "hazmat")`
impl ExpandedSecretKey {
/// Construct an `ExpandedSecretKey` from an array of 64 bytes. In the spec, the bytes are the
/// output of a SHA-512 hash. This clamps the first 32 bytes and uses it as a scalar, and uses
/// the second 32 bytes as a domain separator for hashing.
pub fn from_bytes(bytes: &[u8; 64]) -> Self {
// TODO: Use bytes.split_array_ref once its in MSRV.
let mut scalar_bytes: [u8; 32] = [0u8; 32];
let mut hash_prefix: [u8; 32] = [0u8; 32];
scalar_bytes.copy_from_slice(&bytes[00..32]);
hash_prefix.copy_from_slice(&bytes[32..64]);
// For signing, we'll need the integer, clamped, and converted to a Scalar. See
// PureEdDSA.keygen in RFC 8032 Appendix A.
let scalar = Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes));
ExpandedSecretKey {
scalar,
hash_prefix,
}
}
/// Construct an `ExpandedSecretKey` from a slice of 64 bytes.
///
/// # Returns
///
/// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose error value is an
/// `SignatureError` describing the error that occurred, namely that the given slice's length
/// is not 64.
pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
// Try to coerce bytes to a [u8; 64]
bytes.try_into().map(Self::from_bytes).map_err(|_| {
InternalError::BytesLength {
name: "ExpandedSecretKey",
length: 64,
}
.into()
})
}
}
impl TryFrom<&[u8]> for ExpandedSecretKey {
type Error = SignatureError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_slice(bytes)
}
}
/// Compute an ordinary Ed25519 signature over the given message. `CtxDigest` is the digest used to
/// calculate the pseudorandomness needed for signing. According to the Ed25519 spec, `CtxDigest =
/// Sha512`.
///
/// # ⚠️ Cryptographically Unsafe
///
/// Do NOT use this function unless you absolutely must. Using the wrong values in
/// `ExpandedSecretKey` can leak your signing key. See
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
pub fn raw_sign<CtxDigest>(
esk: &ExpandedSecretKey,
message: &[u8],
verifying_key: &VerifyingKey,
) -> Signature
where
CtxDigest: Digest<OutputSize = U64>,
{
esk.raw_sign::<CtxDigest>(message, verifying_key)
}
/// Compute a signature over the given prehashed message, the Ed25519ph algorithm defined in
/// [RFC8032 §5.1][rfc8032]. `MsgDigest` is the digest function used to hash the signed message.
/// `CtxDigest` is the digest function used to calculate the pseudorandomness needed for signing.
/// According to the Ed25519 spec, `MsgDigest = CtxDigest = Sha512`.
///
/// # ⚠️ Cryptographically Unsafe
//
/// Do NOT use this function unless you absolutely must. Using the wrong values in
/// `ExpandedSecretKey` can leak your signing key. See
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
///
/// # Inputs
///
/// * `esk` is the [`ExpandedSecretKey`] being used for signing
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
/// output which has had the message to be signed previously fed into its
/// state.
/// * `verifying_key` is a [`VerifyingKey`] which corresponds to this secret key.
/// * `context` is an optional context string, up to 255 bytes inclusive,
/// which may be used to provide additional domain separation. If not
/// set, this will default to an empty string.
///
/// `scalar` and `hash_prefix` are usually selected such that `scalar || hash_prefix = H(sk)` where
/// `sk` is the signing key
///
/// # Returns
///
/// A `Result` whose `Ok` value is an Ed25519ph [`Signature`] on the
/// `prehashed_message` if the context was 255 bytes or less, otherwise
/// a `SignatureError`.
///
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn raw_sign_prehashed<CtxDigest, MsgDigest>(
esk: &ExpandedSecretKey,
prehashed_message: MsgDigest,
verifying_key: &VerifyingKey,
context: Option<&[u8]>,
) -> Result<Signature, SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
CtxDigest: Digest<OutputSize = U64>,
{
esk.raw_sign_prehashed::<CtxDigest, MsgDigest>(prehashed_message, verifying_key, context)
}
/// Compute an ordinary Ed25519 signature, with the message contents provided incrementally by
/// updating a digest instance.
///
/// The `msg_update` closure provides the message content, updating a hasher argument. It will be
/// called twice. This closure MUST leave its hasher in the same state (i.e., must hash the same
/// values) after both calls. Otherwise it will produce an invalid signature.
///
/// `CtxDigest` is the digest used to calculate the pseudorandomness needed for signing. According
/// to the Ed25519 spec, `CtxDigest = Sha512`.
///
/// # ⚠️ Cryptographically Unsafe
///
/// Do NOT use this function unless you absolutely must. Using the wrong values in
/// `ExpandedSecretKey` can leak your signing key. See
/// [here](https://github.com/MystenLabs/ed25519-unsafe-libs) for more details on this attack.
pub fn raw_sign_byupdate<CtxDigest, F>(
esk: &ExpandedSecretKey,
msg_update: F,
verifying_key: &VerifyingKey,
) -> Result<Signature, SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
F: Fn(&mut CtxDigest) -> Result<(), SignatureError>,
{
esk.raw_sign_byupdate::<CtxDigest, F>(msg_update, verifying_key)
}
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R
/// values.`CtxDigest` is the digest used to calculate the pseudorandomness needed for signing.
/// According to the Ed25519 spec, `CtxDigest = Sha512`.
pub fn raw_verify<CtxDigest>(
vk: &VerifyingKey,
message: &[u8],
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
{
vk.raw_verify::<CtxDigest>(message, signature)
}
/// The batched Ed25519 verification check, rejecting non-canonical R values. `MsgDigest` is the
/// digest used to hash the signed message. `CtxDigest` is the digest used to calculate the
/// pseudorandomness needed for signing. According to the Ed25519 spec, `MsgDigest = CtxDigest =
/// Sha512`.
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn raw_verify_prehashed<CtxDigest, MsgDigest>(
vk: &VerifyingKey,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
CtxDigest: Digest<OutputSize = U64>,
{
vk.raw_verify_prehashed::<CtxDigest, MsgDigest>(prehashed_message, context, signature)
}
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use super::*;
use rand::{rngs::OsRng, CryptoRng, RngCore};
// Pick distinct, non-spec 512-bit hash functions for message and sig-context hashing
type CtxDigest = blake2::Blake2b512;
type MsgDigest = sha3::Sha3_512;
impl ExpandedSecretKey {
// Make a random expanded secret key for testing purposes. This is NOT how you generate
// expanded secret keys IRL. They're the hash of a seed.
fn random<R: RngCore + CryptoRng>(mut rng: R) -> Self {
let mut bytes = [0u8; 64];
rng.fill_bytes(&mut bytes);
ExpandedSecretKey::from_bytes(&bytes)
}
}
// Check that raw_sign and raw_verify work when a non-spec CtxDigest is used
#[test]
fn sign_verify_nonspec() {
// Generate the keypair
let rng = OsRng;
let esk = ExpandedSecretKey::random(rng);
let vk = VerifyingKey::from(&esk);
let msg = b"Then one day, a piano fell on my head";
// Sign and verify
let sig = raw_sign::<CtxDigest>(&esk, msg, &vk);
raw_verify::<CtxDigest>(&vk, msg, &sig).unwrap();
}
// Check that raw_sign_prehashed and raw_verify_prehashed work when distinct, non-spec
// MsgDigest and CtxDigest are used
#[cfg(feature = "digest")]
#[test]
fn sign_verify_prehashed_nonspec() {
use curve25519_dalek::digest::Digest;
// Generate the keypair
let rng = OsRng;
let esk = ExpandedSecretKey::random(rng);
let vk = VerifyingKey::from(&esk);
// Hash the message
let msg = b"And then I got trampled by a herd of buffalo";
let mut h = MsgDigest::new();
h.update(msg);
let ctx_str = &b"consequences"[..];
// Sign and verify prehashed
let sig = raw_sign_prehashed::<CtxDigest, MsgDigest>(&esk, h.clone(), &vk, Some(ctx_str))
.unwrap();
raw_verify_prehashed::<CtxDigest, MsgDigest>(&vk, h, Some(ctx_str), &sig).unwrap();
}
#[test]
fn sign_byupdate() {
// Generate the keypair
let rng = OsRng;
let esk = ExpandedSecretKey::random(rng);
let vk = VerifyingKey::from(&esk);
let msg = b"realistic";
// signatures are deterministic so we can compare with a good one
let good_sig = raw_sign::<CtxDigest>(&esk, msg, &vk);
let sig = raw_sign_byupdate::<CtxDigest, _>(
&esk,
|h| {
h.update(msg);
Ok(())
},
&vk,
);
assert!(sig.unwrap() == good_sig, "sign byupdate matches");
let sig = raw_sign_byupdate::<CtxDigest, _>(
&esk,
|h| {
h.update(msg);
Err(SignatureError::new())
},
&vk,
);
assert!(sig.is_err(), "sign byupdate failure propagates");
let sig = raw_sign_byupdate::<CtxDigest, _>(
&esk,
|h| {
h.update(&msg[..1]);
h.update(&msg[1..]);
Ok(())
},
&vk,
);
assert!(sig.unwrap() == good_sig, "sign byupdate two part");
}
}

294
vendor/ed25519-dalek/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,294 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! A Rust implementation of ed25519 key generation, signing, and verification.
//!
//! # Example
//!
//! Creating an ed25519 signature on a message is simple.
//!
//! First, we need to generate a `SigningKey`, which includes both public and
//! secret halves of an asymmetric key. To do so, we need a cryptographically
//! secure pseudorandom number generator (CSPRNG). For this example, we'll use
//! the operating system's builtin PRNG:
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() {
//! // $ cargo add ed25519_dalek --features rand_core
//! use rand::rngs::OsRng;
//! use ed25519_dalek::SigningKey;
//! use ed25519_dalek::Signature;
//!
//! let mut csprng = OsRng;
//! let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! # }
//! ```
//!
//! We can now use this `signing_key` to sign a message:
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::SigningKey;
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! use ed25519_dalek::{Signature, Signer};
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
//! let signature: Signature = signing_key.sign(message);
//! # }
//! ```
//!
//! As well as to verify that this is, indeed, a valid signature on
//! that `message`:
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer};
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = signing_key.sign(message);
//! use ed25519_dalek::Verifier;
//! assert!(signing_key.verify(message, &signature).is_ok());
//! # }
//! ```
//!
//! Anyone else, given the `public` half of the `signing_key` can also easily
//! verify this signature:
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::SigningKey;
//! # use ed25519_dalek::Signature;
//! # use ed25519_dalek::Signer;
//! use ed25519_dalek::{VerifyingKey, Verifier};
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = signing_key.sign(message);
//!
//! let verifying_key: VerifyingKey = signing_key.verifying_key();
//! assert!(verifying_key.verify(message, &signature).is_ok());
//! # }
//! ```
//!
//! ## Serialisation
//!
//! `VerifyingKey`s, `SecretKey`s, `SigningKey`s, and `Signature`s can be serialised
//! into byte-arrays by calling `.to_bytes()`. It's perfectly acceptable and
//! safe to transfer and/or store those bytes. (Of course, never transfer your
//! secret key to anyone else, since they will only need the public key to
//! verify your signatures!)
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
//! use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = signing_key.sign(message);
//!
//! let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key.verifying_key().to_bytes();
//! let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key.to_bytes();
//! let signing_key_bytes: [u8; KEYPAIR_LENGTH] = signing_key.to_keypair_bytes();
//! let signature_bytes: [u8; SIGNATURE_LENGTH] = signature.to_bytes();
//! # }
//! ```
//!
//! And similarly, decoded from bytes with `::from_bytes()`:
//!
#![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # use core::convert::{TryFrom, TryInto};
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey, SecretKey, SignatureError};
//! # use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, KEYPAIR_LENGTH, SIGNATURE_LENGTH};
//! # fn do_test() -> Result<(SigningKey, VerifyingKey, Signature), SignatureError> {
//! # let mut csprng = OsRng;
//! # let signing_key_orig: SigningKey = SigningKey::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature_orig: Signature = signing_key_orig.sign(message);
//! # let verifying_key_bytes: [u8; PUBLIC_KEY_LENGTH] = signing_key_orig.verifying_key().to_bytes();
//! # let signing_key_bytes: [u8; SECRET_KEY_LENGTH] = signing_key_orig.to_bytes();
//! # let signature_bytes: [u8; SIGNATURE_LENGTH] = signature_orig.to_bytes();
//! #
//! let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&verifying_key_bytes)?;
//! let signing_key: SigningKey = SigningKey::from_bytes(&signing_key_bytes);
//! let signature: Signature = Signature::try_from(&signature_bytes[..])?;
//! #
//! # Ok((signing_key, verifying_key, signature))
//! # }
//! # fn main() {
//! # do_test();
//! # }
//! ```
//!
//! ### PKCS#8 Key Encoding
//!
//! PKCS#8 is a private key format with support for multiple algorithms.
//! It can be encoded as binary (DER) or text (PEM).
//!
//! You can recognize PEM-encoded PKCS#8 keys by the following:
//!
//! ```text
//! -----BEGIN PRIVATE KEY-----
//! ```
//!
//! To use PKCS#8, you need to enable the `pkcs8` crate feature.
//!
//! The following traits can be used to decode/encode [`SigningKey`] and
//! [`VerifyingKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the
//! toplevel of the crate:
//!
//! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8
//! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8
//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8
//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8
//!
//! #### Example
//!
//! NOTE: this requires the `pem` crate feature.
//!
#![cfg_attr(feature = "pem", doc = "```")]
#![cfg_attr(not(feature = "pem"), doc = "```ignore")]
//! use ed25519_dalek::{VerifyingKey, pkcs8::DecodePublicKey};
//!
//! let pem = "-----BEGIN PUBLIC KEY-----
//! MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
//! -----END PUBLIC KEY-----";
//!
//! let verifying_key = VerifyingKey::from_public_key_pem(pem)
//! .expect("invalid public key PEM");
//! ```
//!
//! ### Using Serde
//!
//! If you prefer the bytes to be wrapped in another serialisation format, all
//! types additionally come with built-in [serde](https://serde.rs) support by
//! building `ed25519-dalek` via:
//!
//! ```bash
//! $ cargo build --features="serde"
//! ```
//!
//! They can be then serialised into any of the wire formats which serde supports.
//! For example, using [bincode](https://github.com/TyOverby/bincode):
//!
#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
//! use bincode::serialize;
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! # let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = signing_key.sign(message);
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
//!
//! let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
//! let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
//! # }
//! ```
//!
//! After sending the `encoded_verifying_key` and `encoded_signature`, the
//! recipient may deserialise them and verify:
//!
#![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
#![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
//! # fn main() {
//! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
//! # use bincode::serialize;
//! use bincode::deserialize;
//!
//! # let mut csprng = OsRng;
//! # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
//! let message: &[u8] = b"This is a test of the tsunami alert system.";
//! # let signature: Signature = signing_key.sign(message);
//! # let verifying_key: VerifyingKey = signing_key.verifying_key();
//! # let verified: bool = verifying_key.verify(message, &signature).is_ok();
//! # let encoded_verifying_key: Vec<u8> = serialize(&verifying_key).unwrap();
//! # let encoded_signature: Vec<u8> = serialize(&signature).unwrap();
//! let decoded_verifying_key: VerifyingKey = deserialize(&encoded_verifying_key).unwrap();
//! let decoded_signature: Signature = deserialize(&encoded_signature).unwrap();
//!
//! # assert_eq!(verifying_key, decoded_verifying_key);
//! # assert_eq!(signature, decoded_signature);
//! #
//! let verified: bool = decoded_verifying_key.verify(&message, &decoded_signature).is_ok();
//!
//! assert!(verified);
//! # }
//! ```
#![no_std]
#![warn(future_incompatible, rust_2018_idioms)]
#![deny(missing_docs)] // refuse to compile if documentation is missing
#![deny(clippy::unwrap_used)] // don't allow unwrap
#![cfg_attr(not(test), forbid(unsafe_code))]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
#[cfg(feature = "batch")]
extern crate alloc;
#[cfg(any(feature = "std", test))]
#[macro_use]
extern crate std;
pub use ed25519;
#[cfg(feature = "batch")]
mod batch;
mod constants;
#[cfg(feature = "digest")]
mod context;
mod errors;
mod signature;
mod signing;
mod verifying;
#[cfg(feature = "hazmat")]
pub mod hazmat;
#[cfg(not(feature = "hazmat"))]
mod hazmat;
#[cfg(feature = "digest")]
pub use curve25519_dalek::digest::Digest;
#[cfg(feature = "digest")]
pub use sha2::Sha512;
#[cfg(feature = "batch")]
pub use crate::batch::*;
pub use crate::constants::*;
#[cfg(feature = "digest")]
pub use crate::context::Context;
pub use crate::errors::*;
pub use crate::signing::*;
pub use crate::verifying::*;
// Re-export the `Signer` and `Verifier` traits from the `signature` crate
#[cfg(feature = "digest")]
pub use ed25519::signature::{DigestSigner, DigestVerifier};
pub use ed25519::signature::{Signer, Verifier};
pub use ed25519::Signature;
#[cfg(feature = "pkcs8")]
pub use ed25519::pkcs8;

177
vendor/ed25519-dalek/src/signature.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! An ed25519 signature.
use core::fmt::Debug;
use curve25519_dalek::edwards::CompressedEdwardsY;
use curve25519_dalek::scalar::Scalar;
use crate::constants::*;
use crate::errors::*;
/// An ed25519 signature.
///
/// # Note
///
/// These signatures, unlike the ed25519 signature reference implementation, are
/// "detached"—that is, they do **not** include a copy of the message which has
/// been signed.
#[allow(non_snake_case)]
#[derive(Copy, Eq, PartialEq)]
pub(crate) struct InternalSignature {
/// `R` is an `EdwardsPoint`, formed by using an hash function with
/// 512-bits output to produce the digest of:
///
/// - the nonce half of the `ExpandedSecretKey`, and
/// - the message to be signed.
///
/// This digest is then interpreted as a `Scalar` and reduced into an
/// element in /l. The scalar is then multiplied by the distinguished
/// basepoint to produce `R`, and `EdwardsPoint`.
pub(crate) R: CompressedEdwardsY,
/// `s` is a `Scalar`, formed by using an hash function with 512-bits output
/// to produce the digest of:
///
/// - the `r` portion of this `Signature`,
/// - the `PublicKey` which should be used to verify this `Signature`, and
/// - the message to be signed.
///
/// This digest is then interpreted as a `Scalar` and reduced into an
/// element in /l.
pub(crate) s: Scalar,
}
impl Clone for InternalSignature {
fn clone(&self) -> Self {
*self
}
}
impl Debug for InternalSignature {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s)
}
}
/// Ensures that the scalar `s` of a signature is within the bounds [0, 2^253).
///
/// **Unsafe**: This version of `check_scalar` permits signature malleability. See README.
#[cfg(feature = "legacy_compatibility")]
#[inline(always)]
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
// The highest 3 bits must not be set. No other checking for the
// remaining 2^253 - 2^252 + 27742317777372353535851937790883648493
// potential non-reduced scalars is performed.
//
// This is compatible with ed25519-donna and libsodium when
// `-D ED25519_COMPAT` is NOT specified.
if bytes[31] & 224 != 0 {
return Err(InternalError::ScalarFormat.into());
}
// You cannot do arithmetic with scalars construct with Scalar::from_bits. We only use this
// scalar for EdwardsPoint::vartime_double_scalar_mul_basepoint, which is an accepted usecase.
// The `from_bits` method is deprecated because it's unsafe. We know this.
#[allow(deprecated)]
Ok(Scalar::from_bits(bytes))
}
/// Ensures that the scalar `s` of a signature is within the bounds [0, )
#[cfg(not(feature = "legacy_compatibility"))]
#[inline(always)]
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
match Scalar::from_canonical_bytes(bytes).into() {
None => Err(InternalError::ScalarFormat.into()),
Some(x) => Ok(x),
}
}
impl InternalSignature {
/// Construct a `Signature` from a slice of bytes.
///
/// # Scalar Malleability Checking
///
/// As originally specified in the ed25519 paper (cf. the "Malleability"
/// section of the README in this repo), no checks whatsoever were performed
/// for signature malleability.
///
/// Later, a semi-functional, hacky check was added to most libraries to
/// "ensure" that the scalar portion, `s`, of the signature was reduced `mod
/// \ell`, the order of the basepoint:
///
/// ```ignore
/// if signature.s[31] & 224 != 0 {
/// return Err();
/// }
/// ```
///
/// This bit-twiddling ensures that the most significant three bits of the
/// scalar are not set:
///
/// ```python,ignore
/// >>> 0b00010000 & 224
/// 0
/// >>> 0b00100000 & 224
/// 32
/// >>> 0b01000000 & 224
/// 64
/// >>> 0b10000000 & 224
/// 128
/// ```
///
/// However, this check is hacky and insufficient to check that the scalar is
/// fully reduced `mod \ell = 2^252 + 27742317777372353535851937790883648493` as
/// it leaves us with a guanteed bound of 253 bits. This means that there are
/// `2^253 - 2^252 + 2774231777737235353585193779088364849311` remaining scalars
/// which could cause malleabilllity.
///
/// RFC8032 [states](https://tools.ietf.org/html/rfc8032#section-5.1.7):
///
/// > To verify a signature on a message M using public key A, [...]
/// > first split the signature into two 32-octet halves. Decode the first
/// > half as a point R, and the second half as an integer S, in the range
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
/// > decodings fail (including S being out of range), the signature is
/// > invalid.
///
/// However, by the time this was standardised, most libraries in use were
/// only checking the most significant three bits. (See also the
/// documentation for [`crate::VerifyingKey::verify_strict`].)
#[inline]
#[allow(non_snake_case)]
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> {
// TODO: Use bytes.split_array_ref once its in MSRV.
let mut R_bytes: [u8; 32] = [0u8; 32];
let mut s_bytes: [u8; 32] = [0u8; 32];
R_bytes.copy_from_slice(&bytes[00..32]);
s_bytes.copy_from_slice(&bytes[32..64]);
Ok(InternalSignature {
R: CompressedEdwardsY(R_bytes),
s: check_scalar(s_bytes)?,
})
}
}
impl TryFrom<&ed25519::Signature> for InternalSignature {
type Error = SignatureError;
fn try_from(sig: &ed25519::Signature) -> Result<InternalSignature, SignatureError> {
InternalSignature::from_bytes(&sig.to_bytes())
}
}
impl From<InternalSignature> for ed25519::Signature {
fn from(sig: InternalSignature) -> ed25519::Signature {
ed25519::Signature::from_components(*sig.R.as_bytes(), *sig.s.as_bytes())
}
}

955
vendor/ed25519-dalek/src/signing.rs vendored Normal file
View File

@@ -0,0 +1,955 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! ed25519 signing keys.
use core::fmt::Debug;
#[cfg(feature = "pkcs8")]
use ed25519::pkcs8;
#[cfg(any(test, feature = "rand_core"))]
use rand_core::CryptoRngCore;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::Sha512;
use subtle::{Choice, ConstantTimeEq};
use curve25519_dalek::{
digest::{generic_array::typenum::U64, Digest},
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
};
use ed25519::signature::{KeypairRef, Signer, Verifier};
#[cfg(feature = "digest")]
use crate::context::Context;
#[cfg(feature = "digest")]
use signature::DigestSigner;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "hazmat")]
use crate::verifying::StreamVerifier;
use crate::{
constants::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH},
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
verifying::VerifyingKey,
Signature,
};
/// ed25519 secret key as defined in [RFC8032 § 5.1.5]:
///
/// > The private key is 32 octets (256 bits, corresponding to b) of
/// > cryptographically secure random data.
///
/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5
pub type SecretKey = [u8; SECRET_KEY_LENGTH];
/// ed25519 signing key which can be used to produce signatures.
// Invariant: `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,
/// The public half of this signing key.
pub(crate) verifying_key: VerifyingKey,
}
/// # Example
///
/// ```
/// # extern crate ed25519_dalek;
/// #
/// use ed25519_dalek::SigningKey;
/// use ed25519_dalek::SECRET_KEY_LENGTH;
/// use ed25519_dalek::SignatureError;
///
/// # fn doctest() -> Result<SigningKey, SignatureError> {
/// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [
/// 157, 097, 177, 157, 239, 253, 090, 096,
/// 186, 132, 074, 244, 146, 236, 044, 196,
/// 068, 073, 197, 105, 123, 050, 105, 025,
/// 112, 059, 172, 003, 028, 174, 127, 096, ];
///
/// let signing_key: SigningKey = SigningKey::from_bytes(&secret_key_bytes);
/// assert_eq!(signing_key.to_bytes(), secret_key_bytes);
///
/// # Ok(signing_key)
/// # }
/// #
/// # fn main() {
/// # let result = doctest();
/// # assert!(result.is_ok());
/// # }
/// ```
impl SigningKey {
/// Construct a [`SigningKey`] from a [`SecretKey`]
///
#[inline]
pub fn from_bytes(secret_key: &SecretKey) -> Self {
let verifying_key = VerifyingKey::from(&ExpandedSecretKey::from(secret_key));
Self {
secret_key: *secret_key,
verifying_key,
}
}
/// Convert this [`SigningKey`] into a [`SecretKey`]
#[inline]
pub fn to_bytes(&self) -> SecretKey {
self.secret_key
}
/// 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
///
/// * `bytes`: an `&[u8]` of length [`KEYPAIR_LENGTH`], representing the
/// scalar for the secret key, and a compressed Edwards-Y coordinate of a
/// point on curve25519, both as bytes. (As obtained from
/// [`SigningKey::to_bytes`].)
///
/// # Returns
///
/// A `Result` whose okay value is an EdDSA [`SigningKey`] or whose error value
/// is 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);
let signing_key = SigningKey::try_from(secret_key)?;
let verifying_key = VerifyingKey::try_from(verifying_key)?;
if signing_key.verifying_key() != verifying_key {
return Err(InternalError::MismatchedKeypair.into());
}
Ok(signing_key)
}
/// Convert this signing key to a 64-byte keypair.
///
/// # Returns
///
/// An array of bytes, `[u8; KEYPAIR_LENGTH]`. The first
/// `SECRET_KEY_LENGTH` of bytes is the `SecretKey`, and the next
/// `PUBLIC_KEY_LENGTH` bytes is the `VerifyingKey` (the same as other
/// libraries, such as [Adam Langley's ed25519 Golang
/// implementation](https://github.com/agl/ed25519/)). It is guaranteed that
/// the encoded public key is the one derived from the encoded secret key.
pub fn to_keypair_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret_key);
bytes[SECRET_KEY_LENGTH..].copy_from_slice(self.verifying_key.as_bytes());
bytes
}
/// Get the [`VerifyingKey`] for this [`SigningKey`].
pub fn verifying_key(&self) -> VerifyingKey {
self.verifying_key
}
/// Create a signing context that can be used for Ed25519ph with
/// [`DigestSigner`].
#[cfg(feature = "digest")]
pub fn with_context<'k, 'v>(
&'k self,
context_value: &'v [u8],
) -> Result<Context<'k, 'v, Self>, SignatureError> {
Context::new(self, context_value)
}
/// Generate an ed25519 signing key.
///
/// # Example
///
#[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
/// # fn main() {
/// use rand::rngs::OsRng;
/// use ed25519_dalek::{Signature, SigningKey};
///
/// let mut csprng = OsRng;
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
/// # }
/// ```
///
/// # Input
///
/// A CSPRNG with a `fill_bytes()` method, e.g. `rand_os::OsRng`.
#[cfg(any(test, feature = "rand_core"))]
pub fn generate<R: CryptoRngCore + ?Sized>(csprng: &mut R) -> SigningKey {
let mut secret = SecretKey::default();
csprng.fill_bytes(&mut secret);
Self::from_bytes(&secret)
}
/// Sign a `prehashed_message` with this [`SigningKey`] using the
/// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
///
/// # Inputs
///
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
/// output which has had the message to be signed previously fed into its
/// state.
/// * `context` is an optional context string, up to 255 bytes inclusive,
/// which may be used to provide additional domain separation. If not
/// set, this will default to an empty string.
///
/// # Returns
///
/// An Ed25519ph [`Signature`] on the `prehashed_message`.
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
/// function technically works, and is probably safe to use, with any secure hash function with
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
/// [`crate::Sha512`] for user convenience.
///
/// # Examples
///
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
/// use ed25519_dalek::Digest;
/// use ed25519_dalek::SigningKey;
/// use ed25519_dalek::Signature;
/// use sha2::Sha512;
/// use rand::rngs::OsRng;
///
/// # fn main() {
/// let mut csprng = OsRng;
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
///
/// // Create a hash digest object which we'll feed the message into:
/// let mut prehashed: Sha512 = Sha512::new();
///
/// prehashed.update(message);
/// # }
/// ```
///
/// If you want, you can optionally pass a "context". It is generally a
/// good idea to choose a context and try to make it unique to your project
/// and this specific usage of signatures.
///
/// For example, without this, if you were to [convert your OpenPGP key
/// to a Bitcoin key][terrible_idea] (just as an example, and also Don't
/// Ever Do That) and someone tricked you into signing an "email" which was
/// actually a Bitcoin transaction moving all your magic internet money to
/// their address, it'd be a valid transaction.
///
/// By adding a context, this trick becomes impossible, because the context
/// is concatenated into the hash, which is then signed. So, going with the
/// previous example, if your bitcoin wallet used a context of
/// "BitcoinWalletAppTxnSigning" and OpenPGP used a context (this is likely
/// the least of their safety problems) of "GPGsCryptoIsntConstantTimeLol",
/// then the signatures produced by both could never match the other, even
/// if they signed the exact same message with the same key.
///
/// Let's add a context for good measure (remember, you'll want to choose
/// your own!):
///
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
/// # use ed25519_dalek::Digest;
/// # use ed25519_dalek::SigningKey;
/// # use ed25519_dalek::Signature;
/// # use ed25519_dalek::SignatureError;
/// # use sha2::Sha512;
/// # use rand::rngs::OsRng;
/// #
/// # fn do_test() -> Result<Signature, SignatureError> {
/// # let mut csprng = OsRng;
/// # let signing_key: SigningKey = SigningKey::generate(&mut csprng);
/// # let message: &[u8] = b"All I want is to pet all of the dogs.";
/// # let mut prehashed: Sha512 = Sha512::new();
/// # prehashed.update(message);
/// #
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
///
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
/// #
/// # Ok(sig)
/// # }
/// # fn main() {
/// # do_test();
/// # }
/// ```
///
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
/// [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py
#[cfg(feature = "digest")]
pub fn sign_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
) -> Result<Signature, SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
ExpandedSecretKey::from(&self.secret_key).raw_sign_prehashed::<Sha512, MsgDigest>(
prehashed_message,
&self.verifying_key,
context,
)
}
/// Verify a signature on a message with this signing key's public key.
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.verifying_key.verify(message, signature)
}
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
///
/// # Inputs
///
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
/// output which has had the message to be signed previously fed into its
/// state.
/// * `context` is an optional context string, up to 255 bytes inclusive,
/// which may be used to provide additional domain separation. If not
/// set, this will default to an empty string.
/// * `signature` is a purported Ed25519ph [`Signature`] on the `prehashed_message`.
///
/// # Returns
///
/// Returns `true` if the `signature` was a valid signature created by this
/// [`SigningKey`] on the `prehashed_message`.
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
/// function technically works, and is probably safe to use, with any secure hash function with
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
/// [`crate::Sha512`] for user convenience.
///
/// # Examples
///
#[cfg_attr(all(feature = "rand_core", feature = "digest"), doc = "```")]
#[cfg_attr(
any(not(feature = "rand_core"), not(feature = "digest")),
doc = "```ignore"
)]
/// use ed25519_dalek::Digest;
/// use ed25519_dalek::SigningKey;
/// use ed25519_dalek::Signature;
/// use ed25519_dalek::SignatureError;
/// use sha2::Sha512;
/// use rand::rngs::OsRng;
///
/// # fn do_test() -> Result<(), SignatureError> {
/// let mut csprng = OsRng;
/// let signing_key: SigningKey = SigningKey::generate(&mut csprng);
/// let message: &[u8] = b"All I want is to pet all of the dogs.";
///
/// let mut prehashed: Sha512 = Sha512::new();
/// prehashed.update(message);
///
/// let context: &[u8] = b"Ed25519DalekSignPrehashedDoctest";
///
/// let sig: Signature = signing_key.sign_prehashed(prehashed, Some(context))?;
///
/// // The sha2::Sha512 struct doesn't implement Copy, so we'll have to create a new one:
/// let mut prehashed_again: Sha512 = Sha512::default();
/// prehashed_again.update(message);
///
/// let verified = signing_key.verifying_key().verify_prehashed(prehashed_again, Some(context), &sig);
///
/// assert!(verified.is_ok());
///
/// # verified
/// # }
/// #
/// # fn main() {
/// # do_test();
/// # }
/// ```
///
/// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
#[cfg(feature = "digest")]
pub fn verify_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
self.verifying_key
.verify_prehashed(prehashed_message, context, signature)
}
/// Strictly verify a signature on a message with this signing key's public key.
///
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
///
/// This version of verification is technically non-RFC8032 compliant. The
/// following explains why.
///
/// 1. Scalar Malleability
///
/// The authors of the RFC explicitly stated that verification of an ed25519
/// signature must fail if the scalar `s` is not properly reduced mod \ell:
///
/// > To verify a signature on a message M using public key A, with F
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
/// > Ed25519ph is being used, C being the context, first split the
/// > signature into two 32-octet halves. Decode the first half as a
/// > point R, and the second half as an integer S, in the range
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
/// > decodings fail (including S being out of range), the signature is
/// > invalid.)
///
/// All `verify_*()` functions within ed25519-dalek perform this check.
///
/// 2. Point malleability
///
/// The authors of the RFC added in a malleability check to step #3 in
/// §5.1.7, for small torsion components in the `R` value of the signature,
/// *which is not strictly required*, as they state:
///
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
///
/// # History of Malleability Checks
///
/// As originally defined (cf. the "Malleability" section in the README of
/// this repo), ed25519 signatures didn't consider *any* form of
/// malleability to be an issue. Later the scalar malleability was
/// considered important. Still later, particularly with interests in
/// cryptocurrency design and in unique identities (e.g. for Signal users,
/// Tor onion services, etc.), the group element malleability became a
/// concern.
///
/// However, libraries had already been created to conform to the original
/// definition. One well-used library in particular even implemented the
/// group element malleability check, *but only for batch verification*!
/// Which meant that even using the same library, a single signature could
/// verify fine individually, but suddenly, when verifying it with a bunch
/// of other signatures, the whole batch would fail!
///
/// # "Strict" Verification
///
/// This method performs *both* of the above signature malleability checks.
///
/// It must be done as a separate method because one doesn't simply get to
/// change the definition of a cryptographic primitive ten years
/// after-the-fact with zero consideration for backwards compatibility in
/// hardware and protocols which have it already have the older definition
/// baked in.
///
/// # Return
///
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
#[allow(non_snake_case)]
pub fn verify_strict(
&self,
message: &[u8],
signature: &Signature,
) -> Result<(), SignatureError> {
self.verifying_key.verify_strict(message, signature)
}
/// Constructs stream verifier with candidate `signature`.
///
/// See [`VerifyingKey::verify_stream()`] for more details.
#[cfg(feature = "hazmat")]
pub fn verify_stream(
&self,
signature: &ed25519::Signature,
) -> Result<StreamVerifier, SignatureError> {
self.verifying_key.verify_stream(signature)
}
/// 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 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 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.
///
/// For more information on the security of systems which use the same keys for both signing
/// and Diffie-Hellman, see the paper
/// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509).
pub fn to_scalar(&self) -> Scalar {
// 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
}
}
impl AsRef<VerifyingKey> for SigningKey {
fn as_ref(&self) -> &VerifyingKey {
&self.verifying_key
}
}
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;
}
impl Signer<Signature> for SigningKey {
/// Sign a message with this signing key's secret key.
fn try_sign(&self, message: &[u8]) -> Result<Signature, SignatureError> {
let expanded: ExpandedSecretKey = (&self.secret_key).into();
Ok(expanded.raw_sign::<Sha512>(message, &self.verifying_key))
}
}
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`None`].
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
#[cfg(feature = "digest")]
impl<D> DigestSigner<D, Signature> for SigningKey
where
D: Digest<OutputSize = U64>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
self.sign_prehashed(msg_digest, None)
}
}
/// Equivalent to [`SigningKey::sign_prehashed`] with `context` set to [`Some`]
/// containing `self.value()`.
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing. This function technically works, and is
/// probably safe to use, with any secure hash function with 512-bit digests, but anything outside
/// of SHA-512 is NOT specification-compliant. We expose [`crate::Sha512`] for user convenience.
#[cfg(feature = "digest")]
impl<D> DigestSigner<D, Signature> for Context<'_, '_, SigningKey>
where
D: Digest<OutputSize = U64>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature, SignatureError> {
self.key().sign_prehashed(msg_digest, Some(self.value()))
}
}
impl Verifier<Signature> for SigningKey {
/// Verify a signature on a message with this signing key's public key.
fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.verifying_key.verify(message, signature)
}
}
impl From<SecretKey> for SigningKey {
#[inline]
fn from(secret: SecretKey) -> Self {
Self::from_bytes(&secret)
}
}
impl From<&SecretKey> for SigningKey {
#[inline]
fn from(secret: &SecretKey) -> Self {
Self::from_bytes(secret)
}
}
impl TryFrom<&[u8]> for SigningKey {
type Error = SignatureError;
fn try_from(bytes: &[u8]) -> Result<SigningKey, SignatureError> {
SecretKey::try_from(bytes)
.map(|bytes| Self::from_bytes(&bytes))
.map_err(|_| {
InternalError::BytesLength {
name: "SecretKey",
length: SECRET_KEY_LENGTH,
}
.into()
})
}
}
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) {
self.secret_key.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for SigningKey {}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::EncodePrivateKey for SigningKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<pkcs8::SecretDocument> {
pkcs8::KeypairBytes::from(self).to_pkcs8_der()
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::spki::DynSignatureAlgorithmIdentifier for SigningKey {
fn signature_algorithm_identifier(
&self,
) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
// From https://datatracker.ietf.org/doc/html/rfc8410
// `id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }`
Ok(pkcs8::spki::AlgorithmIdentifier {
oid: ed25519::pkcs8::ALGORITHM_OID,
parameters: None,
})
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::KeypairBytes> for SigningKey {
type Error = pkcs8::Error;
fn try_from(pkcs8_key: pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
SigningKey::try_from(&pkcs8_key)
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<&pkcs8::KeypairBytes> for SigningKey {
type Error = pkcs8::Error;
fn try_from(pkcs8_key: &pkcs8::KeypairBytes) -> pkcs8::Result<Self> {
let signing_key = SigningKey::from_bytes(&pkcs8_key.secret_key);
// Validate the public key in the PKCS#8 document if present
if let Some(public_bytes) = &pkcs8_key.public_key {
let expected_verifying_key = VerifyingKey::from_bytes(public_bytes.as_ref())
.map_err(|_| pkcs8::Error::KeyMalformed)?;
if signing_key.verifying_key() != expected_verifying_key {
return Err(pkcs8::Error::KeyMalformed);
}
}
Ok(signing_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<SigningKey> for pkcs8::KeypairBytes {
fn from(signing_key: SigningKey) -> pkcs8::KeypairBytes {
pkcs8::KeypairBytes::from(&signing_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<&SigningKey> for pkcs8::KeypairBytes {
fn from(signing_key: &SigningKey) -> pkcs8::KeypairBytes {
pkcs8::KeypairBytes {
secret_key: signing_key.to_bytes(),
public_key: Some(pkcs8::PublicKeyBytes(signing_key.verifying_key.to_bytes())),
}
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey {
type Error = pkcs8::Error;
fn try_from(private_key: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
pkcs8::KeypairBytes::try_from(private_key)?.try_into()
}
}
#[cfg(feature = "serde")]
impl Serialize for SigningKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.secret_key)
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for SigningKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
struct SigningKeyVisitor;
impl<'de> serde::de::Visitor<'de> for SigningKeyVisitor {
type Value = SigningKey;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, concat!("An ed25519 signing (private) key"))
}
fn visit_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>
where
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"))?;
}
let remaining = (0..)
.map(|_| seq.next_element::<u8>())
.take_while(|el| matches!(el, Ok(Some(_))))
.count();
if remaining > 0 {
return Err(serde::de::Error::invalid_length(
32 + remaining,
&"expected 32 bytes",
));
}
Ok(SigningKey::from(bytes))
}
}
deserializer.deserialize_bytes(SigningKeyVisitor)
}
}
/// The spec-compliant way to define an expanded secret key. This computes `SHA512(sk)`, clamps the
/// first 32 bytes and uses it as a scalar, and uses the second 32 bytes as a domain separator for
/// hashing.
impl From<&SecretKey> for ExpandedSecretKey {
#[allow(clippy::unwrap_used)]
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
let hash = Sha512::default().chain_update(secret_key).finalize();
ExpandedSecretKey::from_bytes(hash.as_ref())
}
}
//
// Signing functions. These are pub(crate) so that the `hazmat` module can use them
//
impl ExpandedSecretKey {
/// The plain, non-prehashed, signing function for Ed25519. `CtxDigest` is the digest used to
/// calculate the pseudorandomness needed for signing. According to the spec, `CtxDigest =
/// Sha512`, and `self` is derived via the method defined in `impl From<&SigningKey> for
/// ExpandedSecretKey`.
///
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
#[allow(non_snake_case)]
#[allow(clippy::unwrap_used)]
#[inline(always)]
pub(crate) fn raw_sign<CtxDigest>(
&self,
message: &[u8],
verifying_key: &VerifyingKey,
) -> Signature
where
CtxDigest: Digest<OutputSize = U64>,
{
// OK unwrap, update can't fail.
self.raw_sign_byupdate(
|h: &mut CtxDigest| {
h.update(message);
Ok(())
},
verifying_key,
)
.unwrap()
}
/// Sign a message provided in parts. The `msg_update` closure will be called twice to hash the
/// message parts. This closure MUST leave its hasher in the same state (i.e., must hash the
/// same values) after both calls. Otherwise it will produce an invalid signature.
#[allow(non_snake_case)]
#[inline(always)]
pub(crate) fn raw_sign_byupdate<CtxDigest, F>(
&self,
msg_update: F,
verifying_key: &VerifyingKey,
) -> Result<Signature, SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
F: Fn(&mut CtxDigest) -> Result<(), SignatureError>,
{
let mut h = CtxDigest::new();
h.update(self.hash_prefix);
msg_update(&mut h)?;
let r = Scalar::from_hash(h);
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
h = CtxDigest::new();
h.update(R.as_bytes());
h.update(verifying_key.as_bytes());
msg_update(&mut h)?;
let k = Scalar::from_hash(h);
let s: Scalar = (k * self.scalar) + r;
Ok(InternalSignature { R, s }.into())
}
/// The prehashed signing function for Ed25519 (i.e., Ed25519ph). `CtxDigest` is the digest
/// function used to calculate the pseudorandomness needed for signing. `MsgDigest` is the
/// digest function used to hash the signed message. According to the spec, `MsgDigest =
/// CtxDigest = Sha512`, and `self` is derived via the method defined in `impl
/// From<&SigningKey> for ExpandedSecretKey`.
///
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
/// change how the `ExpandedSecretKey` is calculated and which `CtxDigest` function to use.
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
#[inline(always)]
pub(crate) fn raw_sign_prehashed<CtxDigest, MsgDigest>(
&self,
prehashed_message: MsgDigest,
verifying_key: &VerifyingKey,
context: Option<&[u8]>,
) -> Result<Signature, SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
MsgDigest: Digest<OutputSize = U64>,
{
let mut prehash: [u8; 64] = [0u8; 64];
let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string.
if ctx.len() > 255 {
return Err(SignatureError::from(InternalError::PrehashedContextLength));
}
let ctx_len: u8 = ctx.len() as u8;
// Get the result of the pre-hashed message.
prehash.copy_from_slice(prehashed_message.finalize().as_slice());
// This is the dumbest, ten-years-late, non-admission of fucking up the
// domain separation I have ever seen. Why am I still required to put
// the upper half "prefix" of the hashed "secret key" in here? Why
// can't the user just supply their own nonce and decide for themselves
// whether or not they want a deterministic signature scheme? Why does
// the message go into what's ostensibly the signature domain separation
// hash? Why wasn't there always a way to provide a context string?
//
// ...
//
// This is a really fucking stupid bandaid, and the damned scheme is
// still bleeding from malleability, for fuck's sake.
let mut h = CtxDigest::new()
.chain_update(b"SigEd25519 no Ed25519 collisions")
.chain_update([1]) // Ed25519ph
.chain_update([ctx_len])
.chain_update(ctx)
.chain_update(self.hash_prefix)
.chain_update(&prehash[..]);
let r = Scalar::from_hash(h);
let R: CompressedEdwardsY = EdwardsPoint::mul_base(&r).compress();
h = CtxDigest::new()
.chain_update(b"SigEd25519 no Ed25519 collisions")
.chain_update([1]) // Ed25519ph
.chain_update([ctx_len])
.chain_update(ctx)
.chain_update(R.as_bytes())
.chain_update(verifying_key.as_bytes())
.chain_update(&prehash[..]);
let k = Scalar::from_hash(h);
let s: Scalar = (k * self.scalar) + r;
Ok(InternalSignature { R, s }.into())
}
}

741
vendor/ed25519-dalek/src/verifying.rs vendored Normal file
View File

@@ -0,0 +1,741 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! ed25519 public keys.
use core::fmt::Debug;
use core::hash::{Hash, Hasher};
use curve25519_dalek::{
digest::{generic_array::typenum::U64, Digest},
edwards::{CompressedEdwardsY, EdwardsPoint},
montgomery::MontgomeryPoint,
scalar::Scalar,
};
use ed25519::signature::Verifier;
use sha2::Sha512;
#[cfg(feature = "pkcs8")]
use ed25519::pkcs8;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "digest")]
use crate::context::Context;
#[cfg(feature = "digest")]
use signature::DigestVerifier;
use crate::{
constants::PUBLIC_KEY_LENGTH,
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
signing::SigningKey,
};
#[cfg(feature = "hazmat")]
mod stream;
#[cfg(feature = "hazmat")]
pub use self::stream::StreamVerifier;
/// An ed25519 public key.
///
/// # Note
///
/// The `Eq` and `Hash` impls here use the compressed Edwards y encoding, _not_ the algebraic
/// representation. This means if this `VerifyingKey` is non-canonically encoded, it will be
/// considered unequal to the other equivalent encoding, despite the two representing the same
/// point. More encoding details can be found
/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am).
/// If you want to make sure that signatures produced with respect to those sorts of public keys
/// are rejected, use [`VerifyingKey::verify_strict`].
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
#[derive(Copy, Clone, Default, Eq)]
pub struct VerifyingKey {
/// Serialized compressed Edwards-y point.
pub(crate) compressed: CompressedEdwardsY,
/// Decompressed Edwards point used for curve arithmetic operations.
pub(crate) point: EdwardsPoint,
}
impl Debug for VerifyingKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point)
}
}
impl AsRef<[u8]> for VerifyingKey {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Hash for VerifyingKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
impl PartialEq<VerifyingKey> for VerifyingKey {
fn eq(&self, other: &VerifyingKey) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl From<&ExpandedSecretKey> for VerifyingKey {
/// Derive this public key from its corresponding `ExpandedSecretKey`.
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
VerifyingKey::from(EdwardsPoint::mul_base(&expanded_secret_key.scalar))
}
}
impl From<&SigningKey> for VerifyingKey {
fn from(signing_key: &SigningKey) -> VerifyingKey {
signing_key.verifying_key()
}
}
impl From<EdwardsPoint> for VerifyingKey {
fn from(point: EdwardsPoint) -> VerifyingKey {
VerifyingKey {
point,
compressed: point.compress(),
}
}
}
impl VerifyingKey {
/// Convert this public key to a byte array.
#[inline]
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.compressed.to_bytes()
}
/// View this public key as a byte array.
#[inline]
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
&(self.compressed).0
}
/// Construct a `VerifyingKey` from a slice of bytes.
///
/// Verifies the point is valid under [ZIP-215] rules. RFC 8032 / NIST point validation criteria
/// are currently unsupported (see [dalek-cryptography/curve25519-dalek#626]).
///
/// # Example
///
/// ```
/// use ed25519_dalek::VerifyingKey;
/// use ed25519_dalek::PUBLIC_KEY_LENGTH;
/// use ed25519_dalek::SignatureError;
///
/// # fn doctest() -> Result<VerifyingKey, SignatureError> {
/// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [
/// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58,
/// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26];
///
/// let public_key = VerifyingKey::from_bytes(&public_key_bytes)?;
/// #
/// # Ok(public_key)
/// # }
/// #
/// # fn main() {
/// # doctest();
/// # }
/// ```
///
/// # Returns
///
/// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value
/// is a `SignatureError` describing the error that occurred.
///
/// [ZIP-215]: https://zips.z.cash/zip-0215
/// [dalek-cryptography/curve25519-dalek#626]: https://github.com/dalek-cryptography/curve25519-dalek/issues/626
#[inline]
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result<VerifyingKey, SignatureError> {
let compressed = CompressedEdwardsY(*bytes);
let point = compressed
.decompress()
.ok_or(InternalError::PointDecompression)?;
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
Ok(VerifyingKey { compressed, point })
}
/// Create a verifying context that can be used for Ed25519ph with
/// [`DigestVerifier`].
#[cfg(feature = "digest")]
pub fn with_context<'k, 'v>(
&'k self,
context_value: &'v [u8],
) -> Result<Context<'k, 'v, Self>, SignatureError> {
Context::new(self, context_value)
}
/// Returns whether this is a _weak_ public key, i.e., if this public key has low order.
///
/// A weak public key can be used to generate a signature that's valid for almost every
/// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this
/// property before verification, then use this method.
pub fn is_weak(&self) -> bool {
self.point.is_small_order()
}
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R values. (see
/// [`Self::RCompute`]). `CtxDigest` is the digest used to calculate the pseudorandomness
/// needed for signing. According to the spec, `CtxDigest = Sha512`.
///
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
#[allow(non_snake_case)]
pub(crate) fn raw_verify<CtxDigest>(
&self,
message: &[u8],
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let expected_R = RCompute::<CtxDigest>::compute(self, signature, None, message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
/// The prehashed non-batched Ed25519 verification check, rejecting non-canonical R values.
/// (see [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the
/// pseudorandomness needed for signing. `MsgDigest` is the digest used to hash the signed
/// message. According to the spec, `MsgDigest = CtxDigest = Sha512`.
///
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
/// change how the `ExpandedSecretKey` is calculated and which hash function to use.
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub(crate) fn raw_verify_prehashed<CtxDigest, MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
MsgDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let ctx: &[u8] = context.unwrap_or(b"");
debug_assert!(
ctx.len() <= 255,
"The context must not be longer than 255 octets."
);
let message = prehashed_message.finalize();
let expected_R = RCompute::<CtxDigest>::compute(self, signature, Some(ctx), &message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
///
/// # Inputs
///
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
/// output which has had the message to be signed previously fed into its
/// state.
/// * `context` is an optional context string, up to 255 bytes inclusive,
/// which may be used to provide additional domain separation. If not
/// set, this will default to an empty string.
/// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
///
/// # Returns
///
/// Returns `true` if the `signature` was a valid signature created by this
/// [`SigningKey`] on the `prehashed_message`.
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
/// function technically works, and is probably safe to use, with any secure hash function with
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
/// [`crate::Sha512`] for user convenience.
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn verify_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
self.raw_verify_prehashed::<Sha512, MsgDigest>(prehashed_message, context, signature)
}
/// Strictly verify a signature on a message with this keypair's public key.
///
/// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
///
/// This version of verification is technically non-RFC8032 compliant. The
/// following explains why.
///
/// 1. Scalar Malleability
///
/// The authors of the RFC explicitly stated that verification of an ed25519
/// signature must fail if the scalar `s` is not properly reduced mod $\ell$:
///
/// > To verify a signature on a message M using public key A, with F
/// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
/// > Ed25519ph is being used, C being the context, first split the
/// > signature into two 32-octet halves. Decode the first half as a
/// > point R, and the second half as an integer S, in the range
/// > 0 <= s < L. Decode the public key A as point A'. If any of the
/// > decodings fail (including S being out of range), the signature is
/// > invalid.)
///
/// All `verify_*()` functions within ed25519-dalek perform this check.
///
/// 2. Point malleability
///
/// The authors of the RFC added in a malleability check to step #3 in
/// §5.1.7, for small torsion components in the `R` value of the signature,
/// *which is not strictly required*, as they state:
///
/// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'. It's
/// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
///
/// # History of Malleability Checks
///
/// As originally defined (cf. the "Malleability" section in the README of
/// this repo), ed25519 signatures didn't consider *any* form of
/// malleability to be an issue. Later the scalar malleability was
/// considered important. Still later, particularly with interests in
/// cryptocurrency design and in unique identities (e.g. for Signal users,
/// Tor onion services, etc.), the group element malleability became a
/// concern.
///
/// However, libraries had already been created to conform to the original
/// definition. One well-used library in particular even implemented the
/// group element malleability check, *but only for batch verification*!
/// Which meant that even using the same library, a single signature could
/// verify fine individually, but suddenly, when verifying it with a bunch
/// of other signatures, the whole batch would fail!
///
/// # "Strict" Verification
///
/// This method performs *both* of the above signature malleability checks.
///
/// It must be done as a separate method because one doesn't simply get to
/// change the definition of a cryptographic primitive ten years
/// after-the-fact with zero consideration for backwards compatibility in
/// hardware and protocols which have it already have the older definition
/// baked in.
///
/// # Return
///
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
#[allow(non_snake_case)]
pub fn verify_strict(
&self,
message: &[u8],
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
let signature = InternalSignature::try_from(signature)?;
let signature_R = signature
.R
.decompress()
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
// Logical OR is fine here as we're not trying to be constant time.
if signature_R.is_small_order() || self.point.is_small_order() {
return Err(InternalError::Verify.into());
}
let expected_R = RCompute::<Sha512>::compute(self, signature, None, message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
/// Constructs stream verifier with candidate `signature`.
///
/// Useful for cases where the whole message is not available all at once, allowing the
/// internal signature state to be updated incrementally and verified at the end. In some cases,
/// this will reduce the need for additional allocations.
#[cfg(feature = "hazmat")]
pub fn verify_stream(
&self,
signature: &ed25519::Signature,
) -> Result<StreamVerifier, SignatureError> {
let signature = InternalSignature::try_from(signature)?;
Ok(StreamVerifier::new(*self, signature))
}
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm,
/// using strict signature checking as defined by [`Self::verify_strict`].
///
/// # Inputs
///
/// * `prehashed_message` is an instantiated hash digest with 512-bits of
/// output which has had the message to be signed previously fed into its
/// state.
/// * `context` is an optional context string, up to 255 bytes inclusive,
/// which may be used to provide additional domain separation. If not
/// set, this will default to an empty string.
/// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
///
/// # Returns
///
/// Returns `true` if the `signature` was a valid signature created by this
/// [`SigningKey`] on the `prehashed_message`.
///
/// # Note
///
/// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
/// function technically works, and is probably safe to use, with any secure hash function with
/// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
/// [`crate::Sha512`] for user convenience.
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn verify_prehashed_strict<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let ctx: &[u8] = context.unwrap_or(b"");
debug_assert!(
ctx.len() <= 255,
"The context must not be longer than 255 octets."
);
let signature_R = signature
.R
.decompress()
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
// Logical OR is fine here as we're not trying to be constant time.
if signature_R.is_small_order() || self.point.is_small_order() {
return Err(InternalError::Verify.into());
}
let message = prehashed_message.finalize();
let expected_R = RCompute::<Sha512>::compute(self, signature, Some(ctx), &message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
/// Convert this verifying key into Montgomery form.
///
/// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The output of
/// this function is a valid X25519 public key whose secret key is `sk.to_scalar_bytes()`,
/// where `sk` is a valid signing key for this `VerifyingKey`.
///
/// # Note
///
/// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually
/// long-term keys, while keys used for key exchange should rather be ephemeral. If you can
/// help it, use a separate key for encryption.
///
/// For more information on the security of systems which use the same keys for both signing
/// and Diffie-Hellman, see the paper
/// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509).
pub fn to_montgomery(&self) -> MontgomeryPoint {
self.point.to_montgomery()
}
/// Return this verifying key in Edwards form.
pub fn to_edwards(&self) -> EdwardsPoint {
self.point
}
}
/// Helper for verification. Computes the _expected_ R component of the signature. The
/// caller compares this to the real R component.
/// This computes `H(R || A || M)` where `H` is the 512-bit hash function
/// given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519).
///
/// For pre-hashed variants a `h` with the context already included can be provided.
/// Note that this returns the compressed form of R and the caller does a byte comparison. This
/// means that all our verification functions do not accept non-canonically encoded R values.
/// See the validation criteria blog post for more details:
/// https://hdevalence.ca/blog/2020-10-04-its-25519am
pub(crate) struct RCompute<CtxDigest> {
key: VerifyingKey,
signature: InternalSignature,
h: CtxDigest,
}
#[allow(non_snake_case)]
impl<CtxDigest> RCompute<CtxDigest>
where
CtxDigest: Digest<OutputSize = U64>,
{
/// If `prehash_ctx.is_some()`, this does the prehashed variant of the computation using its
/// contents.
pub(crate) fn compute(
key: &VerifyingKey,
signature: InternalSignature,
prehash_ctx: Option<&[u8]>,
message: &[u8],
) -> CompressedEdwardsY {
let mut c = Self::new(key, signature, prehash_ctx);
c.update(message);
c.finish()
}
pub(crate) fn new(
key: &VerifyingKey,
signature: InternalSignature,
prehash_ctx: Option<&[u8]>,
) -> Self {
let R = &signature.R;
let A = &key.compressed;
let mut h = CtxDigest::new();
if let Some(c) = prehash_ctx {
h.update(b"SigEd25519 no Ed25519 collisions");
h.update([1]); // Ed25519ph
h.update([c.len() as u8]);
h.update(c);
}
h.update(R.as_bytes());
h.update(A.as_bytes());
Self {
key: *key,
signature,
h,
}
}
pub(crate) fn update(&mut self, m: &[u8]) {
self.h.update(m)
}
pub(crate) fn finish(self) -> CompressedEdwardsY {
let k = Scalar::from_hash(self.h);
let minus_A: EdwardsPoint = -self.key.point;
// Recall the (non-batched) verification equation: -[k]A + [s]B = R
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &self.signature.s)
.compress()
}
}
impl Verifier<ed25519::Signature> for VerifyingKey {
/// Verify a signature on a message with this keypair's public key.
///
/// # Return
///
/// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
self.raw_verify::<Sha512>(message, signature)
}
}
/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`None`].
#[cfg(feature = "digest")]
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for VerifyingKey
where
MsgDigest: Digest<OutputSize = U64>,
{
fn verify_digest(
&self,
msg_digest: MsgDigest,
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
self.verify_prehashed(msg_digest, None, signature)
}
}
/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`]
/// containing `self.value()`.
#[cfg(feature = "digest")]
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for Context<'_, '_, VerifyingKey>
where
MsgDigest: Digest<OutputSize = U64>,
{
fn verify_digest(
&self,
msg_digest: MsgDigest,
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
self.key()
.verify_prehashed(msg_digest, Some(self.value()), signature)
}
}
impl TryFrom<&[u8]> for VerifyingKey {
type Error = SignatureError;
#[inline]
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let bytes = bytes.try_into().map_err(|_| InternalError::BytesLength {
name: "VerifyingKey",
length: PUBLIC_KEY_LENGTH,
})?;
Self::from_bytes(bytes)
}
}
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> {
pkcs8::PublicKeyBytes::from(self).to_public_key_der()
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::spki::DynSignatureAlgorithmIdentifier for VerifyingKey {
fn signature_algorithm_identifier(
&self,
) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
// From https://datatracker.ietf.org/doc/html/rfc8410
// `id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }`
Ok(ed25519::pkcs8::spki::AlgorithmIdentifierOwned {
oid: ed25519::pkcs8::ALGORITHM_OID,
parameters: None,
})
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PublicKeyBytes> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
VerifyingKey::try_from(&pkcs8_key)
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
}
}
#[cfg(feature = "pkcs8")]
impl From<VerifyingKey> for pkcs8::PublicKeyBytes {
fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes {
pkcs8::PublicKeyBytes::from(&verifying_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<&VerifyingKey> for pkcs8::PublicKeyBytes {
fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes {
pkcs8::PublicKeyBytes(verifying_key.to_bytes())
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
pkcs8::PublicKeyBytes::try_from(public_key)?.try_into()
}
}
#[cfg(feature = "serde")]
impl Serialize for VerifyingKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.as_bytes()[..])
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for VerifyingKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
struct VerifyingKeyVisitor;
impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor {
type Value = VerifyingKey;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, concat!("An ed25519 verifying (public) key"))
}
fn visit_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>
where
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"))?;
}
let remaining = (0..)
.map(|_| seq.next_element::<u8>())
.take_while(|el| matches!(el, Ok(Some(_))))
.count();
if remaining > 0 {
return Err(serde::de::Error::invalid_length(
32 + remaining,
&"expected 32 bytes",
));
}
VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_bytes(VerifyingKeyVisitor)
}
}

View File

@@ -0,0 +1,45 @@
use curve25519_dalek::edwards::CompressedEdwardsY;
use sha2::Sha512;
use crate::verifying::RCompute;
use crate::{signature::InternalSignature, InternalError, SignatureError, VerifyingKey};
/// An IUF verifier for ed25519.
///
/// Created with [`VerifyingKey::verify_stream()`] or [`SigningKey::verify_stream()`].
///
/// [`SigningKey::verify_stream()`]: super::SigningKey::verify_stream()
#[allow(non_snake_case)]
pub struct StreamVerifier {
cr: RCompute<Sha512>,
sig_R: CompressedEdwardsY,
}
impl StreamVerifier {
/// Constructs new stream verifier.
///
/// Seeds hash state with public key and signature components.
pub(crate) fn new(public_key: VerifyingKey, signature: InternalSignature) -> Self {
Self {
cr: RCompute::new(&public_key, signature, None),
sig_R: signature.R,
}
}
/// Digest message chunk.
pub fn update(&mut self, chunk: impl AsRef<[u8]>) {
self.cr.update(chunk.as_ref());
}
/// Finalize verifier and check against candidate signature.
#[allow(non_snake_case)]
pub fn finalize_and_verify(self) -> Result<(), SignatureError> {
let expected_R = self.cr.finish();
if expected_R == self.sig_R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
}

723
vendor/ed25519-dalek/tests/ed25519.rs vendored Normal file
View File

@@ -0,0 +1,723 @@
// -*- mode: rust; -*-
//
// This file is part of ed25519-dalek.
// Copyright (c) 2017-2019 isis lovecruft
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
//! Integration tests for ed25519-dalek.
#![allow(clippy::items_after_test_module)]
use ed25519_dalek::*;
use hex::FromHex;
#[cfg(feature = "digest")]
use hex_literal::hex;
#[cfg(test)]
mod vectors {
use super::*;
use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT,
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
traits::IsIdentity,
};
#[cfg(not(feature = "digest"))]
use sha2::{digest::Digest, Sha512};
use std::{
fs::File,
io::{BufRead, BufReader},
ops::Neg,
};
// TESTVECTORS is taken from sign.input.gz in agl's ed25519 Golang
// package. It is a selection of test cases from
// http://ed25519.cr.yp.to/python/sign.input
#[test]
fn against_reference_implementation() {
// TestGolden
let mut line: String;
let mut lineno: usize = 0;
let f = File::open("TESTVECTORS");
if f.is_err() {
println!(
"This test is only available when the code has been cloned \
from the git repository, since the TESTVECTORS file is large \
and is therefore not included within the distributed crate."
);
panic!();
}
let file = BufReader::new(f.unwrap());
for l in file.lines() {
lineno += 1;
line = l.unwrap();
let parts: Vec<&str> = line.split(':').collect();
assert_eq!(parts.len(), 5, "wrong number of fields in line {}", lineno);
let sec_bytes: Vec<u8> = FromHex::from_hex(parts[0]).unwrap();
let pub_bytes: Vec<u8> = FromHex::from_hex(parts[1]).unwrap();
let msg_bytes: Vec<u8> = FromHex::from_hex(parts[2]).unwrap();
let sig_bytes: Vec<u8> = FromHex::from_hex(parts[3]).unwrap();
let sec_bytes = &sec_bytes[..SECRET_KEY_LENGTH].try_into().unwrap();
let pub_bytes = &pub_bytes[..PUBLIC_KEY_LENGTH].try_into().unwrap();
let signing_key = SigningKey::from_bytes(sec_bytes);
let expected_verifying_key = VerifyingKey::from_bytes(pub_bytes).unwrap();
assert_eq!(expected_verifying_key, signing_key.verifying_key());
// The signatures in the test vectors also include the message
// at the end, but we just want R and S.
let sig1: Signature = Signature::try_from(&sig_bytes[..64]).unwrap();
let sig2: Signature = signing_key.sign(&msg_bytes);
assert!(sig1 == sig2, "Signature bytes not equal on line {}", lineno);
assert!(
signing_key.verify(&msg_bytes, &sig2).is_ok(),
"Signature verification failed on line {}",
lineno
);
assert!(
expected_verifying_key
.verify_strict(&msg_bytes, &sig2)
.is_ok(),
"Signature strict verification failed on line {}",
lineno
);
}
}
// From https://tools.ietf.org/html/rfc8032#section-7.3
#[cfg(feature = "digest")]
#[test]
fn ed25519ph_rf8032_test_vector_prehash() {
let sec_bytes = hex!("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42");
let pub_bytes = hex!("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf");
let msg_bytes = hex!("616263");
let sig_bytes = hex!("98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406");
let signing_key = SigningKey::from_bytes(&sec_bytes);
let expected_verifying_key = VerifyingKey::from_bytes(&pub_bytes).unwrap();
assert_eq!(expected_verifying_key, signing_key.verifying_key());
let sig1 = Signature::try_from(&sig_bytes[..]).unwrap();
let mut prehash_for_signing = Sha512::default();
let mut prehash_for_verifying = Sha512::default();
prehash_for_signing.update(&msg_bytes[..]);
prehash_for_verifying.update(&msg_bytes[..]);
let sig2: Signature = signing_key
.sign_prehashed(prehash_for_signing, None)
.unwrap();
assert!(
sig1 == sig2,
"Original signature from test vectors doesn't equal signature produced:\
\noriginal:\n{:?}\nproduced:\n{:?}",
sig1,
sig2
);
assert!(
signing_key
.verify_prehashed(prehash_for_verifying.clone(), None, &sig2)
.is_ok(),
"Could not verify ed25519ph signature!"
);
assert!(
expected_verifying_key
.verify_prehashed_strict(prehash_for_verifying, None, &sig2)
.is_ok(),
"Could not strict-verify ed25519ph signature!"
);
}
//
// The remaining items in this mod are for the repudiation tests
//
// Taken from curve25519_dalek::constants::EIGHT_TORSION[4]
const EIGHT_TORSION_4: [u8; 32] = [
236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127,
];
// Computes the prehashed or non-prehashed challenge, depending on whether context is given
fn compute_challenge(
message: &[u8],
pub_key: &EdwardsPoint,
signature_r: &EdwardsPoint,
context: Option<&[u8]>,
) -> Scalar {
let mut h = Sha512::default();
if let Some(c) = context {
h.update(b"SigEd25519 no Ed25519 collisions");
h.update([1]);
h.update([c.len() as u8]);
h.update(c);
}
h.update(signature_r.compress().as_bytes());
h.update(&pub_key.compress().as_bytes()[..]);
h.update(message);
Scalar::from_hash(h)
}
fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec<u8> {
[&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat()
}
const WEAK_PUBKEY: CompressedEdwardsY = CompressedEdwardsY(EIGHT_TORSION_4);
// Pick a random Scalar
fn non_null_scalar() -> Scalar {
let mut rng = rand::rngs::OsRng;
let mut s_candidate = Scalar::random(&mut rng);
while s_candidate == Scalar::ZERO {
s_candidate = Scalar::random(&mut rng);
}
s_candidate
}
fn pick_r(s: Scalar) -> EdwardsPoint {
let r0 = s * ED25519_BASEPOINT_POINT;
// Pick a torsion point of order 2
r0 + WEAK_PUBKEY.decompress().unwrap().neg()
}
// Tests that verify_strict() rejects small-order pubkeys. We test this by explicitly
// constructing a pubkey-signature pair that verifies with respect to two distinct messages.
// This should be accepted by verify(), but rejected by verify_strict().
#[test]
fn repudiation() {
let message1 = b"Send 100 USD to Alice";
let message2 = b"Send 100000 USD to Alice";
let mut s: Scalar = non_null_scalar();
let pubkey = WEAK_PUBKEY.decompress().unwrap();
let mut r = pick_r(s);
// Find an R such that
// H(R || A || M₁) · A == A == H(R || A || M₂) · A
// This happens with high probability when A is low order.
while !(pubkey.neg() + compute_challenge(message1, &pubkey, &r, None) * pubkey)
.is_identity()
|| !(pubkey.neg() + compute_challenge(message2, &pubkey, &r, None) * pubkey)
.is_identity()
{
// We pick an s and let R = sB - A where B is the basepoint
s = non_null_scalar();
r = pick_r(s);
}
// At this point, both verification equations hold:
// sB = R + H(R || A || M₁) · A
// = R + H(R || A || M₂) · A
// Check that this is true
let signature = serialize_signature(&r, &s);
let vk = VerifyingKey::from_bytes(pubkey.compress().as_bytes()).unwrap();
let sig = Signature::try_from(&signature[..]).unwrap();
assert!(vk.verify(message1, &sig).is_ok());
assert!(vk.verify(message2, &sig).is_ok());
// Check that this public key appears as weak
assert!(vk.is_weak());
// Now check that the sigs fail under verify_strict. This is because verify_strict rejects
// small order pubkeys.
assert!(vk.verify_strict(message1, &sig).is_err());
assert!(vk.verify_strict(message2, &sig).is_err());
}
// Identical to repudiation() above, but testing verify_prehashed against
// verify_prehashed_strict. See comments above for a description of what's happening.
#[cfg(feature = "digest")]
#[test]
fn repudiation_prehash() {
let message1 = Sha512::new().chain_update(b"Send 100 USD to Alice");
let message2 = Sha512::new().chain_update(b"Send 100000 USD to Alice");
let message1_bytes = message1.clone().finalize();
let message2_bytes = message2.clone().finalize();
let mut s: Scalar = non_null_scalar();
let pubkey = WEAK_PUBKEY.decompress().unwrap();
let mut r = pick_r(s);
let context_str = Some(&b"edtest"[..]);
while !(pubkey.neg()
+ compute_challenge(&message1_bytes, &pubkey, &r, context_str) * pubkey)
.is_identity()
|| !(pubkey.neg()
+ compute_challenge(&message2_bytes, &pubkey, &r, context_str) * pubkey)
.is_identity()
{
s = non_null_scalar();
r = pick_r(s);
}
// Check that verify_prehashed succeeds on both sigs
let signature = serialize_signature(&r, &s);
let vk = VerifyingKey::from_bytes(pubkey.compress().as_bytes()).unwrap();
let sig = Signature::try_from(&signature[..]).unwrap();
assert!(vk
.verify_prehashed(message1.clone(), context_str, &sig)
.is_ok());
assert!(vk
.verify_prehashed(message2.clone(), context_str, &sig)
.is_ok());
// Check that verify_prehashed_strict fails on both sigs
assert!(vk
.verify_prehashed_strict(message1.clone(), context_str, &sig)
.is_err());
assert!(vk
.verify_prehashed_strict(message2.clone(), context_str, &sig)
.is_err());
}
}
#[cfg(feature = "rand_core")]
mod integrations {
use super::*;
use rand::rngs::OsRng;
use std::collections::HashMap;
#[test]
fn sign_verify() {
// TestSignVerify
let good: &[u8] = "test message".as_bytes();
let bad: &[u8] = "wrong message".as_bytes();
let mut csprng = OsRng;
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
let verifying_key = signing_key.verifying_key();
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(),
"Verification of a valid signature failed!"
);
assert!(
verifying_key.verify_strict(good, &good_sig).is_ok(),
"Strict verification of a valid signature failed!"
);
assert!(
signing_key.verify(good, &bad_sig).is_err(),
"Verification of a signature on a different message passed!"
);
assert!(
verifying_key.verify_strict(good, &bad_sig).is_err(),
"Strict verification of a signature on a different message passed!"
);
assert!(
signing_key.verify(bad, &good_sig).is_err(),
"Verification of a signature on a different message passed!"
);
assert!(
verifying_key.verify_strict(bad, &good_sig).is_err(),
"Strict verification of a signature on a different message passed!"
);
}
#[cfg(feature = "digest")]
#[test]
fn sign_verify_digest_equivalence() {
// TestSignVerify
let mut csprng = OsRng {};
let good: &[u8] = "test message".as_bytes();
let bad: &[u8] = "wrong message".as_bytes();
let keypair: SigningKey = SigningKey::generate(&mut csprng);
let good_sig: Signature = keypair.sign(good);
let bad_sig: Signature = keypair.sign(bad);
let mut verifier = keypair.verify_stream(&good_sig).unwrap();
verifier.update(good);
assert!(
verifier.finalize_and_verify().is_ok(),
"Verification of a valid signature failed!"
);
let mut verifier = keypair.verify_stream(&bad_sig).unwrap();
verifier.update(good);
assert!(
verifier.finalize_and_verify().is_err(),
"Verification of a signature on a different message passed!"
);
let mut verifier = keypair.verify_stream(&good_sig).unwrap();
verifier.update("test ");
verifier.update("message");
assert!(
verifier.finalize_and_verify().is_ok(),
"Verification of a valid signature failed!"
);
let mut verifier = keypair.verify_stream(&good_sig).unwrap();
verifier.update(bad);
assert!(
verifier.finalize_and_verify().is_err(),
"Verification of a signature on a different message passed!"
);
}
#[cfg(feature = "digest")]
#[test]
fn ed25519ph_sign_verify() {
let good: &[u8] = b"test message";
let bad: &[u8] = b"wrong message";
let mut csprng = OsRng;
// ugh… there's no `impl Copy for Sha512`… i hope we can all agree these are the same hashes
let mut prehashed_good1: Sha512 = Sha512::default();
prehashed_good1.update(good);
let mut prehashed_good2: Sha512 = Sha512::default();
prehashed_good2.update(good);
let mut prehashed_good3: Sha512 = Sha512::default();
prehashed_good3.update(good);
let mut prehashed_bad1: Sha512 = Sha512::default();
prehashed_bad1.update(bad);
let mut prehashed_bad2: Sha512 = Sha512::default();
prehashed_bad2.update(bad);
let context: &[u8] = b"testing testing 1 2 3";
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
let verifying_key = signing_key.verifying_key();
let good_sig: Signature = signing_key
.sign_prehashed(prehashed_good1, Some(context))
.unwrap();
let bad_sig: Signature = signing_key
.sign_prehashed(prehashed_bad1, Some(context))
.unwrap();
assert!(
signing_key
.verify_prehashed(prehashed_good2.clone(), Some(context), &good_sig)
.is_ok(),
"Verification of a valid signature failed!"
);
assert!(
verifying_key
.verify_prehashed_strict(prehashed_good2, Some(context), &good_sig)
.is_ok(),
"Strict verification of a valid signature failed!"
);
assert!(
signing_key
.verify_prehashed(prehashed_good3.clone(), Some(context), &bad_sig)
.is_err(),
"Verification of a signature on a different message passed!"
);
assert!(
verifying_key
.verify_prehashed_strict(prehashed_good3, Some(context), &bad_sig)
.is_err(),
"Strict verification of a signature on a different message passed!"
);
assert!(
signing_key
.verify_prehashed(prehashed_bad2.clone(), Some(context), &good_sig)
.is_err(),
"Verification of a signature on a different message passed!"
);
assert!(
verifying_key
.verify_prehashed_strict(prehashed_bad2, Some(context), &good_sig)
.is_err(),
"Strict verification of a signature on a different message passed!"
);
}
#[cfg(feature = "batch")]
#[test]
fn verify_batch_seven_signatures() {
let messages: [&[u8]; 7] = [
b"Watch closely everyone, I'm going to show you how to kill a god.",
b"I'm not a cryptographer I just encrypt a lot.",
b"Still not a cryptographer.",
b"This is a test of the tsunami alert system. This is only a test.",
b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.",
b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.",
b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ];
let mut csprng = OsRng;
let mut signing_keys: Vec<SigningKey> = Vec::new();
let mut signatures: Vec<Signature> = Vec::new();
for msg in messages {
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
signatures.push(signing_key.sign(msg));
signing_keys.push(signing_key);
}
let verifying_keys: Vec<VerifyingKey> =
signing_keys.iter().map(|key| key.verifying_key()).collect();
let result = verify_batch(&messages, &signatures, &verifying_keys);
assert!(result.is_ok());
}
#[test]
fn public_key_hash_trait_check() {
let mut csprng = OsRng {};
let secret: SigningKey = SigningKey::generate(&mut csprng);
let public_from_secret: VerifyingKey = (&secret).into();
let mut m = HashMap::new();
m.insert(public_from_secret, "Example_Public_Key");
m.insert(public_from_secret, "Updated Value");
let (k, &v) = m.get_key_value(&public_from_secret).unwrap();
assert_eq!(k, &public_from_secret);
assert_eq!(v, "Updated Value");
assert_eq!(m.len(), 1usize);
let second_secret: SigningKey = SigningKey::generate(&mut csprng);
let public_from_second_secret: VerifyingKey = (&second_secret).into();
assert_ne!(public_from_secret, public_from_second_secret);
m.insert(public_from_second_secret, "Second public key");
let (k, &v) = m.get_key_value(&public_from_second_secret).unwrap();
assert_eq!(k, &public_from_second_secret);
assert_eq!(v, "Second public key");
assert_eq!(m.len(), 2usize);
}
#[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"))]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(crate = "serde")]
struct Demo {
signing_key: SigningKey,
}
#[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.
static BINCODE_INT_LENGTH: usize = 8;
static PUBLIC_KEY_BYTES: [u8; PUBLIC_KEY_LENGTH] = [
130, 039, 155, 015, 062, 076, 188, 063, 124, 122, 026, 251, 233, 253, 225, 220, 014, 041,
166, 120, 108, 035, 254, 077, 160, 083, 172, 058, 219, 042, 086, 120,
];
static SECRET_KEY_BYTES: [u8; SECRET_KEY_LENGTH] = [
062, 070, 027, 163, 092, 182, 011, 003, 077, 234, 098, 004, 011, 127, 079, 228, 243, 187,
150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054,
];
/// Signature with the above signing_key of a blank message.
static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [
010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123,
080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007,
053, 185, 227, 229, 079, 108, 213, 080, 124, 252, 084, 167, 216, 085, 134, 144, 129, 149,
041, 081, 063, 120, 126, 100, 092, 059, 050, 011,
];
#[test]
fn serialize_deserialize_signature_bincode() {
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
let encoded_signature: Vec<u8> = bincode::serialize(&signature).unwrap();
let decoded_signature: Signature = bincode::deserialize(&encoded_signature).unwrap();
assert_eq!(signature, decoded_signature);
}
#[test]
fn serialize_deserialize_signature_json() {
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
let encoded_signature = serde_json::to_string(&signature).unwrap();
let decoded_signature: Signature = serde_json::from_str(&encoded_signature).unwrap();
assert_eq!(signature, decoded_signature);
}
#[test]
fn serialize_deserialize_verifying_key_bincode() {
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
let encoded_verifying_key: Vec<u8> = bincode::serialize(&verifying_key).unwrap();
let decoded_verifying_key: VerifyingKey =
bincode::deserialize(&encoded_verifying_key).unwrap();
assert_eq!(
&PUBLIC_KEY_BYTES[..],
&encoded_verifying_key[encoded_verifying_key.len() - PUBLIC_KEY_LENGTH..]
);
assert_eq!(verifying_key, decoded_verifying_key);
}
#[test]
fn serialize_deserialize_verifying_key_json() {
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
let encoded_verifying_key = serde_json::to_string(&verifying_key).unwrap();
let decoded_verifying_key: VerifyingKey =
serde_json::from_str(&encoded_verifying_key).unwrap();
assert_eq!(verifying_key, decoded_verifying_key);
}
#[test]
fn serialize_deserialize_verifying_key_json_too_long() {
// derived from `serialize_deserialize_verifying_key_json` test
// trailing zero elements makes key too long (34 bytes)
let encoded_verifying_key_too_long = "[130,39,155,15,62,76,188,63,124,122,26,251,233,253,225,220,14,41,166,120,108,35,254,77,160,83,172,58,219,42,86,120,0,0]";
let de_err = serde_json::from_str::<VerifyingKey>(encoded_verifying_key_too_long)
.unwrap_err()
.to_string();
assert!(
de_err.contains("invalid length 34"),
"expected invalid length error, got: {de_err}",
);
}
#[test]
fn serialize_deserialize_verifying_key_json_too_short() {
// derived from `serialize_deserialize_verifying_key_json` test
let encoded_verifying_key_too_long = "[130,39,155,15]";
let de_err = serde_json::from_str::<VerifyingKey>(encoded_verifying_key_too_long)
.unwrap_err()
.to_string();
assert!(
de_err.contains("invalid length 4"),
"expected invalid length error, got: {de_err}"
);
}
#[test]
fn serialize_deserialize_signing_key_bincode() {
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
let encoded_signing_key: Vec<u8> = bincode::serialize(&signing_key).unwrap();
let decoded_signing_key: SigningKey = bincode::deserialize(&encoded_signing_key).unwrap();
#[allow(clippy::needless_range_loop)]
for i in 0..SECRET_KEY_LENGTH {
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
}
}
#[test]
fn serialize_deserialize_signing_key_json() {
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
let encoded_signing_key = serde_json::to_string(&signing_key).unwrap();
let decoded_signing_key: SigningKey = serde_json::from_str(&encoded_signing_key).unwrap();
#[allow(clippy::needless_range_loop)]
for i in 0..SECRET_KEY_LENGTH {
assert_eq!(SECRET_KEY_BYTES[i], decoded_signing_key.to_bytes()[i]);
}
}
#[test]
fn serialize_deserialize_signing_key_json_too_long() {
// derived from `serialize_deserialize_signing_key_json` test
// trailing zero elements makes key too long (34 bytes)
let encoded_signing_key_too_long = "[62,70,27,163,92,182,11,3,77,234,98,4,11,127,79,228,243,187,150,73,201,137,76,22,85,251,152,2,241,42,72,54,0,0]";
let de_err = serde_json::from_str::<SigningKey>(encoded_signing_key_too_long)
.unwrap_err()
.to_string();
assert!(
de_err.contains("invalid length 34"),
"expected invalid length error, got: {de_err}",
);
}
#[test]
fn serialize_deserialize_signing_key_json_too_short() {
// derived from `serialize_deserialize_signing_key_json` test
let encoded_signing_key_too_long = "[62,70,27,163]";
let de_err = serde_json::from_str::<SigningKey>(encoded_signing_key_too_long)
.unwrap_err()
.to_string();
assert!(
de_err.contains("invalid length 4"),
"expected invalid length error, got: {de_err}"
);
}
#[test]
fn serialize_deserialize_signing_key_toml() {
let demo = Demo {
signing_key: SigningKey::from_bytes(&SECRET_KEY_BYTES),
};
println!("\n\nWrite to toml");
let demo_toml = toml::to_string(&demo).unwrap();
println!("{}", demo_toml);
let demo_toml_rebuild: Result<Demo, _> = toml::from_str(&demo_toml);
println!("{:?}", demo_toml_rebuild);
}
#[test]
fn serialize_verifying_key_size() {
let verifying_key: VerifyingKey = VerifyingKey::from_bytes(&PUBLIC_KEY_BYTES).unwrap();
assert_eq!(
bincode::serialized_size(&verifying_key).unwrap() as usize,
BINCODE_INT_LENGTH + PUBLIC_KEY_LENGTH
);
}
#[test]
fn serialize_signature_size() {
let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES);
assert_eq!(
bincode::serialized_size(&signature).unwrap() as usize,
SIGNATURE_LENGTH
);
}
#[test]
fn serialize_signing_key_size() {
let signing_key = SigningKey::from_bytes(&SECRET_KEY_BYTES);
assert_eq!(
bincode::serialized_size(&signing_key).unwrap() as usize,
BINCODE_INT_LENGTH + SECRET_KEY_LENGTH
);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

87
vendor/ed25519-dalek/tests/pkcs8.rs vendored Normal file
View File

@@ -0,0 +1,87 @@
//! PKCS#8 private key and SPKI public key tests.
//!
//! These are standard formats for storing public and private keys, defined in
//! RFC5958 (PKCS#8) and RFC5280 (SPKI).
#![cfg(feature = "pkcs8")]
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey};
use ed25519_dalek::{SigningKey, VerifyingKey};
use hex_literal::hex;
#[cfg(feature = "alloc")]
use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey};
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use ed25519_dalek::pkcs8::spki::DynSignatureAlgorithmIdentifier;
/// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER.
const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
/// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER.
const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der");
/// Ed25519 SubjectVerifyingKeyInfo encoded as ASN.1 DER.
const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der");
/// Secret key bytes.
///
/// Extracted with:
/// $ openssl asn1parse -inform der -in tests/examples/pkcs8-v1.der
const SK_BYTES: [u8; 32] = hex!("D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842");
/// Public key bytes.
const PK_BYTES: [u8; 32] = hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
#[test]
fn decode_pkcs8_v1() {
let keypair = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap();
assert_eq!(SK_BYTES, keypair.to_bytes());
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
}
#[test]
fn decode_pkcs8_v2() {
let keypair = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap();
assert_eq!(SK_BYTES, keypair.to_bytes());
assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes());
}
#[test]
fn decode_verifying_key() {
let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
assert_eq!(PK_BYTES, verifying_key.to_bytes());
}
#[test]
#[cfg(feature = "alloc")]
fn encode_pkcs8() {
let keypair = SigningKey::from_bytes(&SK_BYTES);
let pkcs8_key = keypair.to_pkcs8_der().unwrap();
let keypair2 = SigningKey::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap();
assert_eq!(keypair.to_bytes(), keypair2.to_bytes());
}
#[test]
#[cfg(feature = "alloc")]
fn encode_verifying_key() {
let verifying_key = VerifyingKey::from_bytes(&PK_BYTES).unwrap();
let verifying_key_der = verifying_key.to_public_key_der().unwrap();
let verifying_key2 = VerifyingKey::from_public_key_der(verifying_key_der.as_bytes()).unwrap();
assert_eq!(verifying_key, verifying_key2);
}
#[test]
#[cfg(feature = "alloc")]
fn get_algo_identifier() {
let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
let identifier = verifying_key.signature_algorithm_identifier().unwrap();
assert!(identifier.parameters.is_none()); // According to rfc8410 this must be None
assert_eq!(identifier.oid, ed25519::pkcs8::ALGORITHM_OID);
let signing_key = SigningKey::from_bytes(&SK_BYTES);
let identifier = signing_key.signature_algorithm_identifier().unwrap();
assert!(identifier.parameters.is_none()); // According to rfc8410 this must be None
assert_eq!(identifier.oid, ed25519::pkcs8::ALGORITHM_OID);
}

View File

@@ -0,0 +1,231 @@
use ed25519::signature::Verifier;
use ed25519_dalek::{Signature, VerifyingKey};
use serde::{de::Error as SError, Deserialize, Deserializer};
use std::{collections::BTreeSet as Set, fs::File};
/// The set of edge cases that [`VerifyingKey::verify()`] permits.
const VERIFY_ALLOWED_EDGECASES: &[Flag] = &[
Flag::LowOrderA,
Flag::LowOrderR,
Flag::NonCanonicalA,
Flag::LowOrderComponentA,
Flag::LowOrderComponentR,
// `ReencodedK` is not actually permitted by `verify()`, but it looks that way in the tests
// because it sometimes occurs with a low-order A. 1/8 of the time, the resulting signature
// will be identical the one made with a normal k. find_validation_criteria shows that indeed
// this occurs 10/58 of the time
Flag::ReencodedK,
];
/// The set of edge cases that [`VerifyingKey::verify_strict()`] permits
const VERIFY_STRICT_ALLOWED_EDGECASES: &[Flag] =
&[Flag::LowOrderComponentA, Flag::LowOrderComponentR];
/// Each variant describes a 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
#[derive(Deserialize, Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
enum Flag {
#[serde(rename = "low_order")]
LowOrder,
#[serde(rename = "low_order_A")]
LowOrderA,
#[serde(rename = "low_order_R")]
LowOrderR,
#[serde(rename = "non_canonical_A")]
NonCanonicalA,
#[serde(rename = "non_canonical_R")]
NonCanonicalR,
#[serde(rename = "low_order_component_A")]
LowOrderComponentA,
#[serde(rename = "low_order_component_R")]
LowOrderComponentR,
#[serde(rename = "low_order_residue")]
LowOrderResidue,
#[serde(rename = "reencoded_k")]
ReencodedK,
}
/// This is an intermediate representation between JSON and TestVector
#[derive(Deserialize)]
struct IntermediateTestVector {
number: usize,
#[serde(deserialize_with = "bytes_from_hex", rename = "key")]
pubkey: Vec<u8>,
#[serde(deserialize_with = "bytes_from_hex")]
sig: Vec<u8>,
msg: String,
flags: Option<Set<Flag>>,
}
/// The test vector struct from [CCTV][]. `sig` may or may not be a valid signature of `msg` with
/// respect to `pubkey`, depending on the verification function's validation criteria. `flags`
/// describes all the edge cases which this test vector falls into.
///
/// [CCTV]: https://github.com/C2SP/CCTV/tree/5ea85644bd035c555900a2f707f7e4c31ea65ced/ed25519vectors
struct TestVector {
number: usize,
pubkey: VerifyingKey,
sig: Signature,
msg: Vec<u8>,
flags: Set<Flag>,
}
impl From<IntermediateTestVector> for TestVector {
fn from(tv: IntermediateTestVector) -> Self {
let number = tv.number;
let pubkey = {
let mut buf = [0u8; 32];
buf.copy_from_slice(&tv.pubkey);
VerifyingKey::from_bytes(&buf).unwrap()
};
let sig = {
let mut buf = [0u8; 64];
buf.copy_from_slice(&tv.sig);
Signature::from_bytes(&buf)
};
let msg = tv.msg.as_bytes().to_vec();
// Unwrap the Option<Set<Flag>>
let flags = tv.flags.unwrap_or_default();
Self {
number,
pubkey,
sig,
msg,
flags,
}
}
}
// Tells serde how to deserialize bytes from hex
fn bytes_from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let mut hex_str = String::deserialize(deserializer)?;
// Prepend a 0 if it's not even length
if hex_str.len() % 2 == 1 {
hex_str.insert(0, '0');
}
hex::decode(hex_str).map_err(|e| SError::custom(format!("{:?}", e)))
}
fn get_test_vectors() -> impl Iterator<Item = TestVector> {
let f = File::open("VALIDATIONVECTORS").expect(
"This test is only available when the code has been cloned from the git repository, since
the VALIDATIONVECTORS file is large and is therefore not included within the distributed \
crate.",
);
serde_json::from_reader::<_, Vec<IntermediateTestVector>>(f)
.unwrap()
.into_iter()
.map(TestVector::from)
}
/// Tests that the verify() and verify_strict() functions succeed only on test cases whose flags
/// (i.e., edge cases it falls into) are a subset of VERIFY_ALLOWED_EDGECASES and
/// VERIFY_STRICT_ALLOWED_EDGECASES, respectively
#[test]
fn check_validation_criteria() {
let verify_allowed_edgecases = Set::from_iter(VERIFY_ALLOWED_EDGECASES.to_vec());
let verify_strict_allowed_edgecases = Set::from_iter(VERIFY_STRICT_ALLOWED_EDGECASES.to_vec());
for TestVector {
number,
pubkey,
msg,
sig,
flags,
} in get_test_vectors()
{
// If all the verify-permitted flags here are ones we permit, then verify() should succeed.
// Otherwise, it should not.
let success = pubkey.verify(&msg, &sig).is_ok();
if flags.is_subset(&verify_allowed_edgecases) {
assert!(success, "verify() expected success in testcase #{number}",);
} else {
assert!(!success, "verify() expected failure in testcase #{number}",);
}
// If all the verify_strict-permitted flags here are ones we permit, then verify_strict()
// should succeed. Otherwise, it should not.
let success = pubkey.verify_strict(&msg, &sig).is_ok();
if flags.is_subset(&verify_strict_allowed_edgecases) {
assert!(
success,
"verify_strict() expected success in testcase #{number}",
);
} else {
assert!(
!success,
"verify_strict() expected failure in testcase #{number}",
);
}
}
}
/// Prints the flags that are consistently permitted by verify() and verify_strict()
#[test]
fn find_validation_criteria() {
let mut verify_allowed_edgecases = Set::new();
let mut verify_strict_allowed_edgecases = Set::new();
// Counts the number of times a signature with a re-encoded k and a low-order A verified. This
// happens with 1/8 probability, assuming the usual verification equation(s).
let mut num_lucky_reencoded_k = 0;
let mut num_reencoded_k = 0;
for TestVector {
number: _,
pubkey,
msg,
sig,
flags,
} in get_test_vectors()
{
// If verify() was a success, add all the associated flags to verify-permitted set
let success = pubkey.verify(&msg, &sig).is_ok();
// If this is ReencodedK && LowOrderA, log some statistics
if flags.contains(&Flag::ReencodedK) && flags.contains(&Flag::LowOrderA) {
num_reencoded_k += 1;
num_lucky_reencoded_k += success as u8;
}
if success {
for flag in &flags {
// Don't count re-encoded k when A is low-order. This is because the
// re-encoded k might be a multiple of 8 by accident
if *flag == Flag::ReencodedK && flags.contains(&Flag::LowOrderA) {
continue;
} else {
verify_allowed_edgecases.insert(*flag);
}
}
}
// If verify_strict() was a success, add all the associated flags to
// verify_strict-permitted set
let success = pubkey.verify_strict(&msg, &sig).is_ok();
if success {
for flag in &flags {
verify_strict_allowed_edgecases.insert(*flag);
}
}
}
println!("VERIFY_ALLOWED_EDGECASES: {:?}", verify_allowed_edgecases);
println!(
"VERIFY_STRICT_ALLOWED_EDGECASES: {:?}",
verify_strict_allowed_edgecases
);
println!(
"re-encoded k && low-order A yielded a valid signature {}/{} of the time",
num_lucky_reencoded_k, num_reencoded_k
);
}

80
vendor/ed25519-dalek/tests/x25519.rs vendored Normal file
View File

@@ -0,0 +1,80 @@
//! Tests for converting Ed25519 keys into X25519 (Montgomery form) keys.
use curve25519_dalek::scalar::{clamp_integer, Scalar};
use ed25519_dalek::SigningKey;
use hex_literal::hex;
use sha2::{Digest, Sha512};
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 ed_secret_key_a = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
let ed_secret_key_b = hex!("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb");
let ed_signing_key_a = SigningKey::from_bytes(&ed_secret_key_a);
let ed_signing_key_b = SigningKey::from_bytes(&ed_secret_key_b);
// 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,
Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_a))
);
assert_eq!(
scalar_b,
Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_b))
);
let x_public_key_a = XPublicKey::from(&x_static_secret_a);
let x_public_key_b = XPublicKey::from(&x_static_secret_b);
assert_eq!(
x_public_key_a.to_bytes(),
hex!("d85e07ec22b0ad881537c2f44d662d1a143cf830c57aca4305d85c7a90f6b62e")
);
assert_eq!(
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!(
x_static_secret_a.diffie_hellman(&x_public_key_b).to_bytes(),
expected_shared_secret,
);
assert_eq!(
x_static_secret_b.diffie_hellman(&x_public_key_a).to_bytes(),
expected_shared_secret,
);
}