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
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 }}
- 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

View File

@@ -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"]

View File

@@ -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.

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) {
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) {

View File

@@ -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)

View File

@@ -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,

View File

@@ -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::*;

View File

@@ -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 its in MSRV.

View File

@@ -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];

View File

@@ -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)
}

View File

@@ -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());
}