diff --git a/Cargo.lock b/Cargo.lock index c022e4b19..892faa07b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,10 +143,15 @@ dependencies = [ name = "bp256" version = "0.7.0-pre" dependencies = [ + "blobby", "ecdsa", "elliptic-curve", + "hex-literal", "primefield", "primeorder", + "proptest", + "rand_chacha", + "rand_core", "sha2", ] diff --git a/bp256/Cargo.toml b/bp256/Cargo.toml index 147a7d086..4a4c240ea 100644 --- a/bp256/Cargo.toml +++ b/bp256/Cargo.toml @@ -23,7 +23,7 @@ primeorder = { version = "=0.14.0-pre.7", optional = true } sha2 = { version = "0.11.0-rc.0", optional = true, default-features = false } [features] -default = ["pkcs8", "std"] +default = ["pkcs8", "std", "ecdsa"] alloc = ["ecdsa?/alloc", "elliptic-curve/alloc", "primeorder?/alloc"] std = ["alloc", "ecdsa?/std", "elliptic-curve/std"] @@ -32,6 +32,15 @@ pkcs8 = ["ecdsa/pkcs8", "elliptic-curve/pkcs8"] serde = ["ecdsa/serde", "elliptic-curve/serde"] sha256 = ["ecdsa/digest", "ecdsa/hazmat", "sha2"] arithmetic = ["dep:primefield", "dep:primeorder"] +ecdsa = ["arithmetic", "ecdsa/signing", "ecdsa/verifying", "sha256"] + +[dev-dependencies] +blobby = "0.3" +ecdsa = { version = "0.17.0-rc.4", features = ["dev"] } +hex-literal = "1.0.0" +proptest = "1" +rand_chacha = "0.9.0" +rand_core = "0.9.3" [package.metadata.docs.rs] all-features = true diff --git a/bp256/src/r1/ecdsa.rs b/bp256/src/r1/ecdsa.rs index 5dc2d03a3..28696d4d0 100644 --- a/bp256/src/r1/ecdsa.rs +++ b/bp256/src/r1/ecdsa.rs @@ -1,18 +1,68 @@ //! Elliptic Curve Digital Signature Algorithm (ECDSA) +//! +//! This module contains support for computing and verifying ECDSA signatures. +//! To use it, you will need to enable the following Cargo feature: `ecdsa` +//! +//! ## Signing/Verification Example +//! +//! This example requires the `ecdsa` Cargo feature is enabled: +//! +//! ``` +//! # #[cfg(feature = "ecdsa")] +//! # { +//! use bp256::{ +//! r1::ecdsa::{SigningKey, Signature, signature::Signer}, +//! }; +//! use rand_core::OsRng; // requires 'os_rng' feature +//! +//! // Signing +//! let signing_key = SigningKey::try_from_rng(&mut OsRng).unwrap(); // Serialize with `::to_bytes()` +//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message"; +//! let signature: Signature = signing_key.sign(message); +//! +//! // Verification +//! use bp256::r1::ecdsa::{VerifyingKey, signature::Verifier}; +//! +//! let verifying_key = VerifyingKey::from(&signing_key); // Serialize with `::to_encoded_point()` +//! assert!(verifying_key.verify(message, &signature).is_ok()); +//! # } +//! ``` -pub use super::BrainpoolP256r1; +pub use ecdsa::{ + RecoveryId, + signature::{self, Error}, +}; -/// ECDSA/brainpoolP256r1 signature (fixed-size) +use super::BrainpoolP256r1; +use ecdsa::EcdsaCurve; + +/// ECDSA/Brainpool-256r1 signature (fixed-size) pub type Signature = ecdsa::Signature; -/// ECDSA/brainpoolP256r1 signature (ASN.1 DER encoded) +/// ECDSA/Brainpool-256r1 signature (ASN.1 DER encoded) pub type DerSignature = ecdsa::der::Signature; -impl ecdsa::EcdsaCurve for BrainpoolP256r1 { +impl EcdsaCurve for BrainpoolP256r1 { const NORMALIZE_S: bool = false; } +/// ECDSA/Brainpool-256r1 signing key +#[cfg(feature = "ecdsa")] +pub type SigningKey = ecdsa::SigningKey; + +/// ECDSA/Brainpool-256r1 verification key (i.e. public key) +#[cfg(feature = "ecdsa")] +pub type VerifyingKey = ecdsa::VerifyingKey; + #[cfg(feature = "sha256")] impl ecdsa::hazmat::DigestAlgorithm for BrainpoolP256r1 { type Digest = sha2::Sha256; } + +#[cfg(all(test, feature = "ecdsa"))] +mod tests { + mod wycheproof { + use crate::BrainpoolP256r1; + ecdsa::new_wycheproof_test!(wycheproof, "wycheproof", BrainpoolP256r1); + } +} diff --git a/bp256/src/r1/test_vectors/data/wycheproof.blb b/bp256/src/r1/test_vectors/data/wycheproof.blb new file mode 100644 index 000000000..4d796322b Binary files /dev/null and b/bp256/src/r1/test_vectors/data/wycheproof.blb differ diff --git a/bp256/tests/ecdsa.rs b/bp256/tests/ecdsa.rs new file mode 100644 index 000000000..5bcb55ea7 --- /dev/null +++ b/bp256/tests/ecdsa.rs @@ -0,0 +1,36 @@ +//! ECDSA tests. + +#![cfg(feature = "ecdsa")] + +use bp256::r1::ecdsa::{ + SigningKey, VerifyingKey, + signature::{Signer, Verifier}, +}; +use proptest::prelude::*; +use rand_chacha::ChaCha8Rng; +use rand_core::SeedableRng; + +prop_compose! { + fn signing_key()(seed in any::<[u8; 32]>()) -> SigningKey { + let mut rng = ChaCha8Rng::from_seed(seed); + SigningKey::try_from_rng(&mut rng).unwrap() + } +} + +proptest! { + #[test] + fn recover_from_msg(sk in signing_key()) { + let msg = b"example"; + let (signature, v) = sk.sign_recoverable(msg).unwrap(); + let recovered_vk = VerifyingKey::recover_from_msg(msg, &signature, v).unwrap(); + prop_assert_eq!(sk.verifying_key(), &recovered_vk); + } + + #[test] + fn sign_roundtrip(sk in signing_key()) { + let msg = b"example"; + let (signature, _v) = sk.try_sign(msg).unwrap(); + let vk = sk.verifying_key(); + prop_assert!(vk.verify(msg, &signature).is_ok()); + } +}