mirror of
https://github.com/dalek-cryptography/ed25519-dalek.git
synced 2026-01-08 18:57:54 -05:00
Make verify_batch deterministic (#256)
Also removed `batch_deterministic` feature
This commit is contained in:
committed by
GitHub
parent
4f6b4b247f
commit
b5dc40bedf
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@@ -30,7 +30,6 @@ jobs:
|
||||
- run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib
|
||||
- run: cargo test --target ${{ matrix.target }}
|
||||
- run: cargo test --target ${{ matrix.target }} --features batch
|
||||
- run: cargo test --target ${{ matrix.target }} --features batch_deterministic
|
||||
- run: cargo test --target ${{ matrix.target }} --features serde
|
||||
- run: cargo test --target ${{ matrix.target }} --features pem
|
||||
|
||||
@@ -68,7 +67,6 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo build --benches --features batch
|
||||
- run: cargo build --benches --features batch_deterministic
|
||||
|
||||
rustfmt:
|
||||
name: Check formatting
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -24,10 +24,9 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
features = ["nightly", "batch", "pkcs8"]
|
||||
|
||||
[dependencies]
|
||||
curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] }
|
||||
curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest"] }
|
||||
ed25519 = { version = "=2.0.0-rc.0", default-features = false }
|
||||
merlin = { version = "3", default-features = false, optional = true }
|
||||
rand = { version = "0.8", default-features = false, optional = true }
|
||||
rand_core = { version = "0.6.4", default-features = false, optional = true }
|
||||
serde = { version = "1.0", default-features = false, optional = true }
|
||||
serde_bytes = { version = "0.11", optional = true }
|
||||
@@ -44,25 +43,23 @@ rand = "0.8"
|
||||
rand_core = { version = "0.6.4", default-features = false }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = { version = "0.5" }
|
||||
curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] }
|
||||
|
||||
[[bench]]
|
||||
name = "ed25519_benchmarks"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
default = ["std", "rand", "zeroize"]
|
||||
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize?/alloc"]
|
||||
std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"]
|
||||
default = ["std", "rand_core", "zeroize"]
|
||||
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"]
|
||||
std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"]
|
||||
|
||||
asm = ["sha2/asm"]
|
||||
batch = ["alloc", "merlin", "rand"]
|
||||
# This feature enables deterministic batch verification.
|
||||
batch_deterministic = ["alloc", "merlin", "rand"]
|
||||
batch = ["alloc", "merlin", "rand_core"]
|
||||
# This features turns off stricter checking for scalar malleability in signatures
|
||||
legacy_compatibility = []
|
||||
pkcs8 = ["ed25519/pkcs8"]
|
||||
pem = ["alloc", "ed25519/pem", "pkcs8"]
|
||||
rand = ["dep:rand", "dep:rand_core"]
|
||||
serde = ["dep:serde", "serde_bytes", "ed25519/serde"]
|
||||
zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"]
|
||||
|
||||
|
||||
50
README.md
50
README.md
@@ -170,55 +170,19 @@ transactions.
|
||||
The scalar component of a signature is not the only source of signature
|
||||
malleability, however. Both the public key used for signature verification and
|
||||
the group element component of the signature are malleable, as they may contain
|
||||
a small torsion component as a consquence of the curve25519 group not being of
|
||||
a small torsion component as a consequence of the curve25519 group not being of
|
||||
prime order, but having a small cofactor of 8.
|
||||
|
||||
If you wish to also eliminate this source of signature malleability, please
|
||||
review the
|
||||
[documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict).
|
||||
|
||||
# A Note on Randomness Generation
|
||||
|
||||
The original paper's specification and the standarisation of RFC8032 do not
|
||||
specify precisely how randomness is to be generated, other than using a CSPRNG
|
||||
(Cryptographically Secure Random Number Generator). Particularly in the case of
|
||||
signature verification, where the security proof _relies_ on the uniqueness of
|
||||
the blinding factors/nonces, it is paramount that these samples of randomness be
|
||||
unguessable to an adversary. Because of this, a current growing belief among
|
||||
cryptographers is that it is safer to prefer _synthetic randomness_.
|
||||
|
||||
To explain synthetic randomness, we should first explain how `ed25519-dalek`
|
||||
handles generation of _deterministic randomness_. This mode is disabled by
|
||||
default due to a tiny-but-not-nonexistent chance that this mode will open users
|
||||
up to fault attacks, wherein an adversary who controls all of the inputs to
|
||||
batch verification (i.e. the public keys, signatures, and messages) can craft
|
||||
them in a specialised manner such as to induce a fault (e.g. causing a
|
||||
mistakenly flipped bit in RAM, overheating a processor, etc.). In the
|
||||
deterministic mode, we seed the PRNG which generates our blinding factors/nonces
|
||||
by creating
|
||||
[a PRNG based on the Fiat-Shamir transform of the public inputs](https://merlin.cool/transcript/rng.html).
|
||||
This mode is potentially useful to protocols which require strong auditability
|
||||
guarantees, as well as those which do not have access to secure system-/chip-
|
||||
provided randomness. This feature can be enabled via
|
||||
`--features='batch_deterministic'`. Note that we _do not_ support deterministic
|
||||
signing, due to the numerous pitfalls therein, including a re-used nonce
|
||||
accidentally revealing the secret key.
|
||||
|
||||
In the default mode, we do as above in the fully deterministic mode, but we
|
||||
ratchet the underlying keccak-f1600 function (used for the provided
|
||||
transcript-based PRNG) forward additionally based on some system-/chip- provided
|
||||
randomness. This provides _synthetic randomness_, that is, randomness based on
|
||||
both deterministic and undeterinistic data. The reason for doing this is to
|
||||
prevent badly seeded system RNGs from ruining the security of the signature
|
||||
verification scheme.
|
||||
|
||||
# Features
|
||||
|
||||
## #![no_std]
|
||||
|
||||
This library aims to be `#![no_std]` compliant. If batch verification is
|
||||
required (`--features='batch'`), please enable either of the `std` or `alloc`
|
||||
features.
|
||||
This library aims is fully `#![no_std]` compliant. No features need to be
|
||||
enabled or disabled to suppose no-std.
|
||||
|
||||
## Nightly Compilers
|
||||
|
||||
@@ -264,11 +228,3 @@ with potentially many different public keys over potentially many different
|
||||
messages) is available via the `batch` feature. It uses synthetic randomness, as
|
||||
noted above. Batch verification requires allocation, so this won't function in
|
||||
heapless settings.
|
||||
|
||||
Batch verification is slightly faster with the `std` feature enabled, since it
|
||||
permits us to use `rand::thread_rng`.
|
||||
|
||||
### Deterministic Batch Signature Verification
|
||||
|
||||
The same notion of batch signature verification as above, but with purely
|
||||
deterministic randomness can be enabled via the `batch_deterministic` feature.
|
||||
|
||||
@@ -47,7 +47,7 @@ mod ed25519_benches {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
fn verify_batch_signatures(c: &mut Criterion) {
|
||||
use ed25519_dalek::verify_batch;
|
||||
|
||||
@@ -75,7 +75,7 @@ mod ed25519_benches {
|
||||
}
|
||||
|
||||
// If the above function isn't defined, make a placeholder function
|
||||
#[cfg(not(any(feature = "batch", feature = "batch_deterministic")))]
|
||||
#[cfg(not(feature = "batch"))]
|
||||
fn verify_batch_signatures(_: &mut Criterion) {}
|
||||
|
||||
fn key_generation(c: &mut Criterion) {
|
||||
|
||||
199
src/batch.rs
199
src/batch.rs
@@ -9,9 +9,6 @@
|
||||
|
||||
//! Batch signature verification.
|
||||
|
||||
#[cfg(all(feature = "batch", feature = "batch_deterministic"))]
|
||||
compile_error!("`batch` and `batch_deterministic` features are mutually exclusive");
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
@@ -27,7 +24,7 @@ pub use curve25519_dalek::digest::Digest;
|
||||
|
||||
use merlin::Transcript;
|
||||
|
||||
use rand::Rng;
|
||||
use rand_core::RngCore;
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -36,59 +33,11 @@ use crate::errors::SignatureError;
|
||||
use crate::signature::InternalSignature;
|
||||
use crate::VerifyingKey;
|
||||
|
||||
/// Gets an RNG from the system, or the zero RNG if we're in deterministic mode. If available, we
|
||||
/// prefer `thread_rng`, since it's faster than `OsRng`.
|
||||
fn get_rng() -> impl rand_core::CryptoRngCore {
|
||||
#[cfg(all(feature = "batch_deterministic", not(feature = "batch")))]
|
||||
return ZeroRng;
|
||||
|
||||
#[cfg(all(feature = "batch", feature = "std"))]
|
||||
return rand::thread_rng();
|
||||
|
||||
#[cfg(all(feature = "batch", not(feature = "std")))]
|
||||
return rand::rngs::OsRng;
|
||||
}
|
||||
|
||||
trait BatchTranscript {
|
||||
fn append_scalars(&mut self, scalars: &Vec<Scalar>);
|
||||
fn append_message_lengths(&mut self, message_lengths: &Vec<usize>);
|
||||
}
|
||||
|
||||
impl BatchTranscript for Transcript {
|
||||
/// Append some `scalars` to this batch verification sigma protocol transcript.
|
||||
///
|
||||
/// For ed25519 batch verification, we include the following as scalars:
|
||||
///
|
||||
/// * All of the computed `H(R||A||M)`s to the protocol transcript, and
|
||||
/// * All of the `s` components of each signature.
|
||||
///
|
||||
/// Each is also prefixed with their index in the vector.
|
||||
fn append_scalars(&mut self, scalars: &Vec<Scalar>) {
|
||||
for (i, scalar) in scalars.iter().enumerate() {
|
||||
self.append_u64(b"", i as u64);
|
||||
self.append_message(b"hram", scalar.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
/// Append the lengths of the messages into the transcript.
|
||||
///
|
||||
/// This is done out of an (potential over-)abundance of caution, to guard against the unlikely
|
||||
/// event of collisions. However, a nicer way to do this would be to append the message length
|
||||
/// before the message, but this is messy w.r.t. the calculations of the `H(R||A||M)`s above.
|
||||
fn append_message_lengths(&mut self, message_lengths: &Vec<usize>) {
|
||||
for (i, len) in message_lengths.iter().enumerate() {
|
||||
self.append_u64(b"", i as u64);
|
||||
self.append_u64(b"mlen", *len as u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of `rand_core::RngCore` which does nothing, to provide purely deterministic
|
||||
/// transcript-based nonces, rather than synthetically random nonces.
|
||||
#[cfg(feature = "batch_deterministic")]
|
||||
/// 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;
|
||||
|
||||
#[cfg(feature = "batch_deterministic")]
|
||||
impl rand_core::RngCore for ZeroRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
rand_core::impls::next_u32_via_fill(self)
|
||||
@@ -114,9 +63,16 @@ impl rand_core::RngCore for ZeroRng {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "batch_deterministic")]
|
||||
// `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
|
||||
@@ -131,84 +87,49 @@ impl rand_core::CryptoRng for ZeroRng {}
|
||||
/// `SignatureError` containing a description of the internal error which
|
||||
/// occured.
|
||||
///
|
||||
/// # Notes on Nonce Generation & Malleability
|
||||
///
|
||||
/// ## On Synthetic Nonces
|
||||
///
|
||||
/// This library defaults to using what is called "synthetic" nonces, which
|
||||
/// means that a mixture of deterministic (per any unique set of inputs to this
|
||||
/// function) data and system randomness is used to seed the CSPRNG for nonce
|
||||
/// generation. For more of the background theory on why many cryptographers
|
||||
/// currently believe this to be superior to either purely deterministic
|
||||
/// generation or purely relying on the system's randomness, see [this section
|
||||
/// of the Merlin design](https://merlin.cool/transcript/rng.html) by Henry de
|
||||
/// Valence, isis lovecruft, and Oleg Andreev, as well as Trevor Perrin's
|
||||
/// [designs for generalised
|
||||
/// EdDSA](https://moderncrypto.org/mail-archive/curves/2017/000925.html).
|
||||
///
|
||||
/// ## On Deterministic Nonces
|
||||
///
|
||||
/// In order to be ammenable to protocols which require stricter third-party
|
||||
/// auditability trails, such as in some financial cryptographic settings, this
|
||||
/// library also supports a `--features=batch_deterministic` setting, where the
|
||||
/// nonces for batch signature verification are derived purely from the inputs
|
||||
/// to this function themselves.
|
||||
///
|
||||
/// **This is not recommended for use unless you have several cryptographers on
|
||||
/// staff who can advise you in its usage and all the horrible, terrible,
|
||||
/// awful ways it can go horribly, terribly, awfully wrong.**
|
||||
/// 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 (both with synthetic and deterministic nonce
|
||||
/// generation), we include the following as scalars in the protocol transcript:
|
||||
/// 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.
|
||||
///
|
||||
/// Each is also prefixed with their index in the vector.
|
||||
///
|
||||
/// 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 only found in deterministic batch
|
||||
/// signature verification (i.e. only when compiling `ed25519-dalek` with
|
||||
/// `--features batch_deterministic`) wherein an adversary, without access
|
||||
/// 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 contitute a signature forgery, merely
|
||||
/// `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.)
|
||||
///
|
||||
/// For an additional way in which signatures can be made to probablistically
|
||||
/// falsely "pass" the synthethic batch verification equation *for the same
|
||||
/// inputs*, but *only some crafted inputs* will pass the deterministic batch
|
||||
/// single, and neither of these will ever pass single signature verification,
|
||||
/// see the documentation for [`VerifyingKey.validate()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ed25519_dalek::verify_batch;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::VerifyingKey;
|
||||
/// use ed25519_dalek::Signer;
|
||||
/// use ed25519_dalek::Signature;
|
||||
/// 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<&[u8]> = (0..64).map(|_| msg).collect();
|
||||
/// let signatures: Vec<Signature> = signing_keys.iter().map(|key| key.sign(&msg)).collect();
|
||||
/// let verifying_keys: Vec<VerifyingKey> = signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
/// 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[..]);
|
||||
/// let result = verify_batch(&messages, &signatures, &verifying_keys);
|
||||
/// assert!(result.is_ok());
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -234,43 +155,61 @@ pub fn verify_batch(
|
||||
.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().try_into().unwrap()
|
||||
})
|
||||
.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<_>, _>>()?;
|
||||
|
||||
// Compute H(R || A || M) for each (signature, public_key, message) triplet
|
||||
let hrams: Vec<Scalar> = (0..signatures.len())
|
||||
.map(|i| {
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
h.update(signatures[i].R.as_bytes());
|
||||
h.update(verifying_keys[i].as_bytes());
|
||||
h.update(&messages[i]);
|
||||
Scalar::from_hash(h)
|
||||
})
|
||||
// Convert the H(R || A || M) values into scalars
|
||||
let hrams: Vec<Scalar> = hrams
|
||||
.iter()
|
||||
.map(Scalar::from_bytes_mod_order_wide)
|
||||
.collect();
|
||||
|
||||
// Collect the message lengths and the scalar portions of the signatures, and add them into the
|
||||
// transcript.
|
||||
let message_lengths: Vec<usize> = messages.iter().map(|i| i.len()).collect();
|
||||
let scalars: Vec<Scalar> = signatures.iter().map(|i| i.s).collect();
|
||||
|
||||
// Build a PRNG based on a transcript of the H(R || A || M)s seen thus far. This provides
|
||||
// synthethic randomness in the default configuration, and purely deterministic in the case of
|
||||
// compiling with the "batch_deterministic" feature.
|
||||
let mut transcript: Transcript = Transcript::new(b"ed25519 batch verification");
|
||||
|
||||
transcript.append_scalars(&hrams);
|
||||
transcript.append_message_lengths(&message_lengths);
|
||||
transcript.append_scalars(&scalars);
|
||||
|
||||
let mut prng = transcript.build_rng().finalize(&mut get_rng());
|
||||
|
||||
// Select a random 128-bit scalar for each signature.
|
||||
let zs: Vec<Scalar> = signatures
|
||||
.iter()
|
||||
.map(|_| Scalar::from(prng.gen::<u128>()))
|
||||
.map(|_| Scalar::from(gen_u128(&mut rng)))
|
||||
.collect();
|
||||
|
||||
// Compute the basepoint coefficient, ∑ s[i]z[i] (mod l)
|
||||
|
||||
@@ -38,7 +38,7 @@ pub(crate) enum InternalError {
|
||||
Verify,
|
||||
/// Two arrays did not match in size, making the called signature
|
||||
/// verification method impossible.
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
ArrayLength {
|
||||
name_a: &'static str,
|
||||
length_a: usize,
|
||||
@@ -62,7 +62,7 @@ impl Display for InternalError {
|
||||
write!(f, "{} must be {} bytes in length", n, l)
|
||||
}
|
||||
InternalError::Verify => write!(f, "Verification equation was not satisfied"),
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
InternalError::ArrayLength {
|
||||
name_a: na,
|
||||
length_a: la,
|
||||
|
||||
38
src/lib.rs
38
src/lib.rs
@@ -18,8 +18,8 @@
|
||||
//! secure pseudorandom number generator (CSPRNG). For this example, we'll use
|
||||
//! the operating system's builtin PRNG:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use ed25519_dalek::SigningKey;
|
||||
@@ -32,8 +32,8 @@
|
||||
//!
|
||||
//! We can now use this `signing_key` to sign a message:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
@@ -48,8 +48,8 @@
|
||||
//! As well as to verify that this is, indeed, a valid signature on
|
||||
//! that `message`:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![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};
|
||||
@@ -65,8 +65,8 @@
|
||||
//! Anyone else, given the `public` half of the `signing_key` can also easily
|
||||
//! verify this signature:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # fn main() {
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use ed25519_dalek::SigningKey;
|
||||
@@ -91,8 +91,8 @@
|
||||
//! secret key to anyone else, since they will only need the public key to
|
||||
//! verify your signatures!)
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![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};
|
||||
@@ -111,8 +111,8 @@
|
||||
//!
|
||||
//! And similarly, decoded from bytes with `::from_bytes()`:
|
||||
//!
|
||||
#![cfg_attr(feature = "rand", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand"), doc = "```ignore")]
|
||||
#![cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
//! # use std::convert::TryFrom;
|
||||
//! # use rand::rngs::OsRng;
|
||||
//! # use std::convert::TryInto;
|
||||
@@ -189,8 +189,8 @@
|
||||
//! 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", feature = "serde"), doc = "```")]
|
||||
#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")]
|
||||
#![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};
|
||||
@@ -210,8 +210,8 @@
|
||||
//! After sending the `encoded_verifying_key` and `encoded_signature`, the
|
||||
//! recipient may deserialise them and verify:
|
||||
//!
|
||||
#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")]
|
||||
#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")]
|
||||
#![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};
|
||||
@@ -245,7 +245,7 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -254,7 +254,7 @@ extern crate std;
|
||||
|
||||
pub use ed25519;
|
||||
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
mod batch;
|
||||
mod constants;
|
||||
mod errors;
|
||||
@@ -264,7 +264,7 @@ mod verifying;
|
||||
|
||||
pub use curve25519_dalek::digest::Digest;
|
||||
|
||||
#[cfg(any(feature = "batch", feature = "batch_deterministic"))]
|
||||
#[cfg(feature = "batch")]
|
||||
pub use crate::batch::*;
|
||||
pub use crate::constants::*;
|
||||
pub use crate::errors::*;
|
||||
|
||||
@@ -160,7 +160,7 @@ impl InternalSignature {
|
||||
///
|
||||
/// However, by the time this was standardised, most libraries in use were
|
||||
/// only checking the most significant three bits. (See also the
|
||||
/// documentation for `PublicKey.verify_strict`.)
|
||||
/// documentation for [`crate::VerifyingKey::verify_strict`].)
|
||||
#[inline]
|
||||
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> {
|
||||
// TODO: Use bytes.split_array_ref once it’s in MSRV.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::pkcs8::{self, DecodePrivateKey};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg(feature = "rand_core")]
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -183,7 +183,7 @@ impl SigningKey {
|
||||
/// The standard hash function used for most ed25519 libraries is SHA-512,
|
||||
/// which is available with `use sha2::Sha512` as in the example above.
|
||||
/// Other suitable hash functions include Keccak-512 and Blake2b-512.
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg(feature = "rand_core")]
|
||||
pub fn generate<R: CryptoRngCore + ?Sized>(csprng: &mut R) -> SigningKey {
|
||||
let mut secret = SecretKey::default();
|
||||
csprng.fill_bytes(&mut secret);
|
||||
@@ -252,7 +252,8 @@ impl SigningKey {
|
||||
/// Let's add a context for good measure (remember, you'll want to choose
|
||||
/// your own!):
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
/// # use ed25519_dalek::Digest;
|
||||
/// # use ed25519_dalek::SigningKey;
|
||||
/// # use ed25519_dalek::Signature;
|
||||
@@ -325,7 +326,8 @@ impl SigningKey {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "rand_core", doc = "```")]
|
||||
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
|
||||
/// use ed25519_dalek::Digest;
|
||||
/// use ed25519_dalek::SigningKey;
|
||||
/// use ed25519_dalek::Signature;
|
||||
@@ -655,21 +657,6 @@ impl Drop for ExpandedSecretKey {
|
||||
}
|
||||
|
||||
impl From<&SecretKey> for ExpandedSecretKey {
|
||||
/// Construct an `ExpandedSecretKey` from a `SecretKey`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// use rand::rngs::OsRng;
|
||||
/// use ed25519_dalek::{SecretKey, ExpandedSecretKey};
|
||||
///
|
||||
/// let mut csprng = OsRng{};
|
||||
/// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
|
||||
/// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
|
||||
let mut h: Sha512 = Sha512::default();
|
||||
let mut hash: [u8; 64] = [0u8; 64];
|
||||
|
||||
@@ -38,6 +38,7 @@ use crate::signature::*;
|
||||
use crate::signing::*;
|
||||
|
||||
/// An ed25519 public key.
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
#[derive(Copy, Clone, Default, Eq, PartialEq)]
|
||||
pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
|
||||
|
||||
@@ -121,6 +122,7 @@ impl VerifyingKey {
|
||||
.decompress()
|
||||
.ok_or(InternalError::PointDecompression)?;
|
||||
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
Ok(VerifyingKey(compressed, point))
|
||||
}
|
||||
|
||||
@@ -138,6 +140,7 @@ impl VerifyingKey {
|
||||
let point = EdwardsPoint::mul_base(&scalar);
|
||||
let compressed = point.compress();
|
||||
|
||||
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
|
||||
VerifyingKey(compressed, point)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use ed25519_dalek::*;
|
||||
use hex::FromHex;
|
||||
use hex_literal::hex;
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg(feature = "rand_core")]
|
||||
use sha2::Sha512;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -281,7 +281,7 @@ mod vectors {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
#[cfg(feature = "rand_core")]
|
||||
mod integrations {
|
||||
use super::*;
|
||||
use rand::rngs::OsRng;
|
||||
@@ -425,7 +425,7 @@ mod integrations {
|
||||
let verifying_keys: Vec<VerifyingKey> =
|
||||
signing_keys.iter().map(|key| key.verifying_key()).collect();
|
||||
|
||||
let result = verify_batch(&messages, &signatures[..], &verifying_keys[..]);
|
||||
let result = verify_batch(&messages, &signatures, &verifying_keys);
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user