Make verify_batch deterministic (#256)

Also removed `batch_deterministic` feature
This commit is contained in:
Michael Rosenberg 2023-01-14 21:26:39 -05:00 committed by GitHub
parent 4f6b4b247f
commit b5dc40bedf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 234 deletions

View file

@ -30,7 +30,6 @@ jobs:
- run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib
- run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --features batch - 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 serde
- run: cargo test --target ${{ matrix.target }} --features pem - run: cargo test --target ${{ matrix.target }} --features pem
@ -68,7 +67,6 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: cargo build --benches --features batch - run: cargo build --benches --features batch
- run: cargo build --benches --features batch_deterministic
rustfmt: rustfmt:
name: Check formatting name: Check formatting

View file

@ -24,10 +24,9 @@ rustdoc-args = ["--cfg", "docsrs"]
features = ["nightly", "batch", "pkcs8"] features = ["nightly", "batch", "pkcs8"]
[dependencies] [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 } ed25519 = { version = "=2.0.0-rc.0", default-features = false }
merlin = { version = "3", default-features = false, optional = true } 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 } rand_core = { version = "0.6.4", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true }
serde_bytes = { version = "0.11", 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 } rand_core = { version = "0.6.4", default-features = false }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = { version = "0.5" } toml = { version = "0.5" }
curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = ["digest", "rand_core"] }
[[bench]] [[bench]]
name = "ed25519_benchmarks" name = "ed25519_benchmarks"
harness = false harness = false
[features] [features]
default = ["std", "rand", "zeroize"] default = ["std", "rand_core", "zeroize"]
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "rand?/alloc", "serde?/alloc", "zeroize?/alloc"] alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"]
std = ["alloc", "ed25519/std", "rand?/std", "serde?/std", "sha2/std"] std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"]
asm = ["sha2/asm"] asm = ["sha2/asm"]
batch = ["alloc", "merlin", "rand"] batch = ["alloc", "merlin", "rand_core"]
# This feature enables deterministic batch verification.
batch_deterministic = ["alloc", "merlin", "rand"]
# This features turns off stricter checking for scalar malleability in signatures # This features turns off stricter checking for scalar malleability in signatures
legacy_compatibility = [] legacy_compatibility = []
pkcs8 = ["ed25519/pkcs8"] pkcs8 = ["ed25519/pkcs8"]
pem = ["alloc", "ed25519/pem", "pkcs8"] pem = ["alloc", "ed25519/pem", "pkcs8"]
rand = ["dep:rand", "dep:rand_core"]
serde = ["dep:serde", "serde_bytes", "ed25519/serde"] serde = ["dep:serde", "serde_bytes", "ed25519/serde"]
zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"]

View file

@ -170,55 +170,19 @@ transactions.
The scalar component of a signature is not the only source of signature The scalar component of a signature is not the only source of signature
malleability, however. Both the public key used for signature verification and malleability, however. Both the public key used for signature verification and
the group element component of the signature are malleable, as they may contain 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. prime order, but having a small cofactor of 8.
If you wish to also eliminate this source of signature malleability, please If you wish to also eliminate this source of signature malleability, please
review the review the
[documentation for the `verify_strict()` function](https://doc.dalek.rs/ed25519_dalek/struct.PublicKey.html#method.verify_strict). [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 # Features
## #![no_std] ## #![no_std]
This library aims to be `#![no_std]` compliant. If batch verification is This library aims is fully `#![no_std]` compliant. No features need to be
required (`--features='batch'`), please enable either of the `std` or `alloc` enabled or disabled to suppose no-std.
features.
## Nightly Compilers ## 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 messages) is available via the `batch` feature. It uses synthetic randomness, as
noted above. Batch verification requires allocation, so this won't function in noted above. Batch verification requires allocation, so this won't function in
heapless settings. 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.

View file

@ -47,7 +47,7 @@ mod ed25519_benches {
}); });
} }
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
fn verify_batch_signatures(c: &mut Criterion) { fn verify_batch_signatures(c: &mut Criterion) {
use ed25519_dalek::verify_batch; use ed25519_dalek::verify_batch;
@ -75,7 +75,7 @@ mod ed25519_benches {
} }
// If the above function isn't defined, make a placeholder function // 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 verify_batch_signatures(_: &mut Criterion) {}
fn key_generation(c: &mut Criterion) { fn key_generation(c: &mut Criterion) {

View file

@ -9,9 +9,6 @@
//! Batch signature verification. //! 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 alloc::vec::Vec;
use core::convert::TryFrom; use core::convert::TryFrom;
@ -27,7 +24,7 @@ pub use curve25519_dalek::digest::Digest;
use merlin::Transcript; use merlin::Transcript;
use rand::Rng; use rand_core::RngCore;
use sha2::Sha512; use sha2::Sha512;
@ -36,59 +33,11 @@ use crate::errors::SignatureError;
use crate::signature::InternalSignature; use crate::signature::InternalSignature;
use crate::VerifyingKey; use crate::VerifyingKey;
/// Gets an RNG from the system, or the zero RNG if we're in deterministic mode. If available, we /// An implementation of `rand_core::RngCore` which does nothing. This is necessary because merlin
/// prefer `thread_rng`, since it's faster than `OsRng`. /// demands an `Rng` as input to `TranscriptRngBuilder::finalize()`. Using this with `finalize()`
fn get_rng() -> impl rand_core::CryptoRngCore { /// yields a PRG whose input is the hashed transcript.
#[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")]
struct ZeroRng; struct ZeroRng;
#[cfg(feature = "batch_deterministic")]
impl rand_core::RngCore for ZeroRng { impl rand_core::RngCore for ZeroRng {
fn next_u32(&mut self) -> u32 { fn next_u32(&mut self) -> u32 {
rand_core::impls::next_u32_via_fill(self) 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 {} 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`. /// Verify a batch of `signatures` on `messages` with their respective `verifying_keys`.
/// ///
/// # Inputs /// # Inputs
@ -131,84 +87,49 @@ impl rand_core::CryptoRng for ZeroRng {}
/// `SignatureError` containing a description of the internal error which /// `SignatureError` containing a description of the internal error which
/// occured. /// 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 /// ## On Deterministic Nonces
/// ///
/// In order to be ammenable to protocols which require stricter third-party /// The nonces for batch signature verification are derived purely from the inputs to this function
/// auditability trails, such as in some financial cryptographic settings, this /// themselves.
/// 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.**
/// ///
/// In any sigma protocol it is wise to include as much context pertaining /// 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 /// to the public state in the protocol as possible, to avoid malleability
/// attacks where an adversary alters publics in an algebraic manner that /// attacks where an adversary alters publics in an algebraic manner that
/// manages to satisfy the equations for the protocol in question. /// manages to satisfy the equations for the protocol in question.
/// ///
/// For ed25519 batch verification (both with synthetic and deterministic nonce /// For ed25519 batch verification we include the following as scalars in the protocol transcript:
/// generation), 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 computed `H(R||A||M)`s to the protocol transcript, and
/// * All of the `s` components of each signature. /// * 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 /// 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 /// `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. /// `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 /// The latter prevents a malleability attack wherein an adversary, without access
/// signature verification (i.e. only when compiling `ed25519-dalek` with
/// `--features batch_deterministic`) wherein an adversary, without access
/// to the signing key(s), can take any valid signature, `(s,R)`, and swap /// 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 /// a vulnerability, as the resulting signature will not pass single
/// signature verification. (Thanks to Github users @real_or_random and /// signature verification. (Thanks to Github users @real_or_random and
/// @jonasnick for pointing out this malleability issue.) /// @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 /// # Examples
/// ///
/// ``` /// ```
/// use ed25519_dalek::verify_batch; /// use ed25519_dalek::{
/// use ed25519_dalek::SigningKey; /// verify_batch, SigningKey, VerifyingKey, Signer, Signature,
/// use ed25519_dalek::VerifyingKey; /// };
/// use ed25519_dalek::Signer;
/// use ed25519_dalek::Signature;
/// use rand::rngs::OsRng; /// use rand::rngs::OsRng;
/// ///
/// # fn main() { /// # fn main() {
/// let mut csprng = OsRng; /// let mut csprng = OsRng;
/// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect(); /// let signing_keys: Vec<_> = (0..64).map(|_| SigningKey::generate(&mut csprng)).collect();
/// let msg: &[u8] = b"They're good dogs Brant"; /// let msg: &[u8] = b"They're good dogs Brant";
/// let messages: Vec<&[u8]> = (0..64).map(|_| msg).collect(); /// let messages: Vec<_> = (0..64).map(|_| msg).collect();
/// let signatures: Vec<Signature> = signing_keys.iter().map(|key| key.sign(&msg)).collect(); /// let signatures: Vec<_> = signing_keys.iter().map(|key| key.sign(&msg)).collect();
/// let verifying_keys: Vec<VerifyingKey> = signing_keys.iter().map(|key| key.verifying_key()).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()); /// assert!(result.is_ok());
/// # } /// # }
/// ``` /// ```
@ -234,43 +155,61 @@ pub fn verify_batch(
.into()); .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` // Convert all signatures to `InternalSignature`
let signatures = signatures let signatures = signatures
.iter() .iter()
.map(InternalSignature::try_from) .map(InternalSignature::try_from)
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
// Convert the H(R || A || M) values into scalars
// Compute H(R || A || M) for each (signature, public_key, message) triplet let hrams: Vec<Scalar> = hrams
let hrams: Vec<Scalar> = (0..signatures.len()) .iter()
.map(|i| { .map(Scalar::from_bytes_mod_order_wide)
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)
})
.collect(); .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. // Select a random 128-bit scalar for each signature.
let zs: Vec<Scalar> = signatures let zs: Vec<Scalar> = signatures
.iter() .iter()
.map(|_| Scalar::from(prng.gen::<u128>())) .map(|_| Scalar::from(gen_u128(&mut rng)))
.collect(); .collect();
// Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l)

View file

@ -38,7 +38,7 @@ pub(crate) enum InternalError {
Verify, Verify,
/// Two arrays did not match in size, making the called signature /// Two arrays did not match in size, making the called signature
/// verification method impossible. /// verification method impossible.
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
ArrayLength { ArrayLength {
name_a: &'static str, name_a: &'static str,
length_a: usize, length_a: usize,
@ -62,7 +62,7 @@ impl Display for InternalError {
write!(f, "{} must be {} bytes in length", n, l) write!(f, "{} must be {} bytes in length", n, l)
} }
InternalError::Verify => write!(f, "Verification equation was not satisfied"), InternalError::Verify => write!(f, "Verification equation was not satisfied"),
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
InternalError::ArrayLength { InternalError::ArrayLength {
name_a: na, name_a: na,
length_a: la, length_a: la,

View file

@ -18,8 +18,8 @@
//! secure pseudorandom number generator (CSPRNG). For this example, we'll use //! secure pseudorandom number generator (CSPRNG). For this example, we'll use
//! the operating system's builtin PRNG: //! the operating system's builtin PRNG:
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! use rand::rngs::OsRng; //! use rand::rngs::OsRng;
//! use ed25519_dalek::SigningKey; //! use ed25519_dalek::SigningKey;
@ -32,8 +32,8 @@
//! //!
//! We can now use this `signing_key` to sign a message: //! We can now use this `signing_key` to sign a message:
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::SigningKey; //! # use ed25519_dalek::SigningKey;
@ -48,8 +48,8 @@
//! As well as to verify that this is, indeed, a valid signature on //! As well as to verify that this is, indeed, a valid signature on
//! that `message`: //! that `message`:
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer}; //! # use ed25519_dalek::{SigningKey, Signature, Signer};
@ -65,8 +65,8 @@
//! Anyone else, given the `public` half of the `signing_key` can also easily //! Anyone else, given the `public` half of the `signing_key` can also easily
//! verify this signature: //! verify this signature:
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::SigningKey; //! # use ed25519_dalek::SigningKey;
@ -91,8 +91,8 @@
//! secret key to anyone else, since they will only need the public key to //! secret key to anyone else, since they will only need the public key to
//! verify your signatures!) //! verify your signatures!)
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey}; //! # use ed25519_dalek::{SigningKey, Signature, Signer, VerifyingKey};
@ -111,8 +111,8 @@
//! //!
//! And similarly, decoded from bytes with `::from_bytes()`: //! And similarly, decoded from bytes with `::from_bytes()`:
//! //!
#![cfg_attr(feature = "rand", doc = "```")] #![cfg_attr(feature = "rand_core", doc = "```")]
#![cfg_attr(not(feature = "rand"), doc = "```ignore")] #![cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
//! # use std::convert::TryFrom; //! # use std::convert::TryFrom;
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use std::convert::TryInto; //! # use std::convert::TryInto;
@ -189,8 +189,8 @@
//! They can be then serialised into any of the wire formats which serde supports. //! They can be then serialised into any of the wire formats which serde supports.
//! For example, using [bincode](https://github.com/TyOverby/bincode): //! For example, using [bincode](https://github.com/TyOverby/bincode):
//! //!
#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] #![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] #![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey};
@ -210,8 +210,8 @@
//! After sending the `encoded_verifying_key` and `encoded_signature`, the //! After sending the `encoded_verifying_key` and `encoded_signature`, the
//! recipient may deserialise them and verify: //! recipient may deserialise them and verify:
//! //!
#![cfg_attr(all(feature = "rand", feature = "serde"), doc = "```")] #![cfg_attr(all(feature = "rand_core", feature = "serde"), doc = "```")]
#![cfg_attr(not(all(feature = "rand", feature = "serde")), doc = "```ignore")] #![cfg_attr(not(all(feature = "rand_core", feature = "serde")), doc = "```ignore")]
//! # fn main() { //! # fn main() {
//! # use rand::rngs::OsRng; //! # use rand::rngs::OsRng;
//! # use ed25519_dalek::{SigningKey, Signature, Signer, Verifier, VerifyingKey}; //! # 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, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
extern crate alloc; extern crate alloc;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
@ -254,7 +254,7 @@ extern crate std;
pub use ed25519; pub use ed25519;
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
mod batch; mod batch;
mod constants; mod constants;
mod errors; mod errors;
@ -264,7 +264,7 @@ mod verifying;
pub use curve25519_dalek::digest::Digest; pub use curve25519_dalek::digest::Digest;
#[cfg(any(feature = "batch", feature = "batch_deterministic"))] #[cfg(feature = "batch")]
pub use crate::batch::*; pub use crate::batch::*;
pub use crate::constants::*; pub use crate::constants::*;
pub use crate::errors::*; pub use crate::errors::*;

View file

@ -160,7 +160,7 @@ impl InternalSignature {
/// ///
/// However, by the time this was standardised, most libraries in use were /// However, by the time this was standardised, most libraries in use were
/// only checking the most significant three bits. (See also the /// only checking the most significant three bits. (See also the
/// documentation for `PublicKey.verify_strict`.) /// documentation for [`crate::VerifyingKey::verify_strict`].)
#[inline] #[inline]
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> { pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> {
// TODO: Use bytes.split_array_ref once its in MSRV. // TODO: Use bytes.split_array_ref once its in MSRV.

View file

@ -12,7 +12,7 @@
#[cfg(feature = "pkcs8")] #[cfg(feature = "pkcs8")]
use ed25519::pkcs8::{self, DecodePrivateKey}; use ed25519::pkcs8::{self, DecodePrivateKey};
#[cfg(feature = "rand")] #[cfg(feature = "rand_core")]
use rand_core::CryptoRngCore; use rand_core::CryptoRngCore;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -183,7 +183,7 @@ impl SigningKey {
/// The standard hash function used for most ed25519 libraries is SHA-512, /// The standard hash function used for most ed25519 libraries is SHA-512,
/// which is available with `use sha2::Sha512` as in the example above. /// which is available with `use sha2::Sha512` as in the example above.
/// Other suitable hash functions include Keccak-512 and Blake2b-512. /// 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 { pub fn generate<R: CryptoRngCore + ?Sized>(csprng: &mut R) -> SigningKey {
let mut secret = SecretKey::default(); let mut secret = SecretKey::default();
csprng.fill_bytes(&mut secret); 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 /// Let's add a context for good measure (remember, you'll want to choose
/// your own!): /// your own!):
/// ///
/// ``` #[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
/// # use ed25519_dalek::Digest; /// # use ed25519_dalek::Digest;
/// # use ed25519_dalek::SigningKey; /// # use ed25519_dalek::SigningKey;
/// # use ed25519_dalek::Signature; /// # use ed25519_dalek::Signature;
@ -325,7 +326,8 @@ impl SigningKey {
/// ///
/// # Examples /// # Examples
/// ///
/// ``` #[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
/// use ed25519_dalek::Digest; /// use ed25519_dalek::Digest;
/// use ed25519_dalek::SigningKey; /// use ed25519_dalek::SigningKey;
/// use ed25519_dalek::Signature; /// use ed25519_dalek::Signature;
@ -655,21 +657,6 @@ impl Drop for ExpandedSecretKey {
} }
impl From<&SecretKey> 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 { fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
let mut h: Sha512 = Sha512::default(); let mut h: Sha512 = Sha512::default();
let mut hash: [u8; 64] = [0u8; 64]; let mut hash: [u8; 64] = [0u8; 64];

View file

@ -38,6 +38,7 @@ use crate::signature::*;
use crate::signing::*; use crate::signing::*;
/// An ed25519 public key. /// An ed25519 public key.
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
#[derive(Copy, Clone, Default, Eq, PartialEq)] #[derive(Copy, Clone, Default, Eq, PartialEq)]
pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint); pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
@ -121,6 +122,7 @@ impl VerifyingKey {
.decompress() .decompress()
.ok_or(InternalError::PointDecompression)?; .ok_or(InternalError::PointDecompression)?;
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
Ok(VerifyingKey(compressed, point)) Ok(VerifyingKey(compressed, point))
} }
@ -138,6 +140,7 @@ impl VerifyingKey {
let point = EdwardsPoint::mul_base(&scalar); let point = EdwardsPoint::mul_base(&scalar);
let compressed = point.compress(); let compressed = point.compress();
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
VerifyingKey(compressed, point) VerifyingKey(compressed, point)
} }

View file

@ -16,7 +16,7 @@ use ed25519_dalek::*;
use hex::FromHex; use hex::FromHex;
use hex_literal::hex; use hex_literal::hex;
#[cfg(feature = "rand")] #[cfg(feature = "rand_core")]
use sha2::Sha512; use sha2::Sha512;
#[cfg(test)] #[cfg(test)]
@ -281,7 +281,7 @@ mod vectors {
} }
} }
#[cfg(feature = "rand")] #[cfg(feature = "rand_core")]
mod integrations { mod integrations {
use super::*; use super::*;
use rand::rngs::OsRng; use rand::rngs::OsRng;
@ -425,7 +425,7 @@ mod integrations {
let verifying_keys: Vec<VerifyingKey> = let verifying_keys: Vec<VerifyingKey> =
signing_keys.iter().map(|key| key.verifying_key()).collect(); 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()); assert!(result.is_ok());
} }