diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 777219c..17f056d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 06ee258..a814730 100644 --- a/Cargo.toml +++ b/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"] diff --git a/README.md b/README.md index 23dcaf5..d9b6d78 100644 --- a/README.md +++ b/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. diff --git a/benches/ed25519_benchmarks.rs b/benches/ed25519_benchmarks.rs index f1beeab..7c96853 100644 --- a/benches/ed25519_benchmarks.rs +++ b/benches/ed25519_benchmarks.rs @@ -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) { diff --git a/src/batch.rs b/src/batch.rs index ad8a413..c312917 100644 --- a/src/batch.rs +++ b/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); - fn append_message_lengths(&mut self, message_lengths: &Vec); -} - -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) { - 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) { - 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(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 = signing_keys.iter().map(|key| key.sign(&msg)).collect(); -/// let verifying_keys: Vec = 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::, _>>()?; - - // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams: Vec = (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 = 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 = messages.iter().map(|i| i.len()).collect(); - let scalars: Vec = 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 = signatures .iter() - .map(|_| Scalar::from(prng.gen::())) + .map(|_| Scalar::from(gen_u128(&mut rng))) .collect(); // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) diff --git a/src/errors.rs b/src/errors.rs index 257399b..aa4e5aa 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index edb5b98..817e954 100644 --- a/src/lib.rs +++ b/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::*; diff --git a/src/signature.rs b/src/signature.rs index fdf1700..99aa553 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -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 { // TODO: Use bytes.split_array_ref once it’s in MSRV. diff --git a/src/signing.rs b/src/signing.rs index 95c0041..7a43452 100644 --- a/src/signing.rs +++ b/src/signing.rs @@ -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(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]; diff --git a/src/verifying.rs b/src/verifying.rs index 2a07b28..b700bac 100644 --- a/src/verifying.rs +++ b/src/verifying.rs @@ -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) } diff --git a/tests/ed25519.rs b/tests/ed25519.rs index 1a65d90..f98b1bd 100644 --- a/tests/ed25519.rs +++ b/tests/ed25519.rs @@ -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 = 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()); }