diff --git a/Cargo.toml b/Cargo.toml index 5aa46e3..da561f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ curve25519-dalek = { package = "curve25519-dalek-ng", version = "4.1", default-f serde = { version = "1", optional = true, features = ["derive"] } zeroize = { version = "1.1", default-features = false } thiserror = { version = "1", optional = true } +ed25519 = { version = "2", default-features = false } [dev-dependencies] rand = "0.8" @@ -31,6 +32,7 @@ once_cell = "1.4" [features] std = ["thiserror"] default = ["serde", "std"] +serde = ["dep:serde", "ed25519/serde"] [[test]] name = "rfc8032" diff --git a/README.md b/README.md index bd21004..0169ebb 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,10 @@ let (vk_bytes, sig_bytes) = { }; // Verify the signature + assert!( VerificationKey::try_from(vk_bytes) - .and_then(|vk| vk.verify(&sig_bytes.into(), msg)) + .and_then(|vk| vk.verify(msg, &sig_bytes.into())) .is_ok() ); ``` @@ -79,4 +80,4 @@ assert!( [RFC8032]: https://tools.ietf.org/html/rfc8032 [zebra]: https://github.com/ZcashFoundation/zebra [ZIP215]: https://github.com/zcash/zips/blob/master/zip-0215.rst -[blog]: https://hdevalence.ca/blog/2020-10-04-its-25519am \ No newline at end of file +[blog]: https://hdevalence.ca/blog/2020-10-04-its-25519am diff --git a/benches/bench.rs b/benches/bench.rs index b51ab63..6a5fc4a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -34,7 +34,7 @@ fn bench_batch_verify(c: &mut Criterion) { b.iter(|| { for (vk_bytes, sig) in sigs.iter() { let _ = - VerificationKey::try_from(*vk_bytes).and_then(|vk| vk.verify(sig, b"")); + VerificationKey::try_from(*vk_bytes).and_then(|vk| vk.verify(b"", sig)); } }) }, diff --git a/src/batch.rs b/src/batch.rs index d925425..676dbf5 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -85,7 +85,7 @@ impl<'msg, M: AsRef<[u8]> + ?Sized> From<(VerificationKeyBytes, Signature, &'msg // Compute k now to avoid dependency on the msg lifetime. let k = Scalar::from_hash( Sha512::default() - .chain(&sig.R_bytes[..]) + .chain(sig.r_bytes()) .chain(&vk_bytes.0[..]) .chain(msg), ); @@ -187,10 +187,11 @@ impl Verifier { let mut A_coeff = Scalar::zero(); for (k, sig) in sigs.iter() { - let R = CompressedEdwardsY(sig.R_bytes) + let R = CompressedEdwardsY(*sig.r_bytes()) .decompress() .ok_or(Error::InvalidSignature)?; - let s = Scalar::from_canonical_bytes(sig.s_bytes).ok_or(Error::InvalidSignature)?; + let s = + Scalar::from_canonical_bytes(*sig.s_bytes()).ok_or(Error::InvalidSignature)?; let z = Scalar::from(gen_u128(&mut rng)); B_coeff -= z * s; Rs.push(R); diff --git a/src/lib.rs b/src/lib.rs index 414a094..c7f131f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,11 +6,10 @@ #[cfg(feature = "std")] pub mod batch; mod error; -mod signature; mod signing_key; mod verification_key; +pub use ed25519::{signature, Signature}; pub use error::Error; -pub use signature::Signature; pub use signing_key::SigningKey; pub use verification_key::{VerificationKey, VerificationKeyBytes}; diff --git a/src/signature.rs b/src/signature.rs deleted file mode 100644 index 5fe9662..0000000 --- a/src/signature.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::Error; -use core::convert::TryFrom; - -/// An Ed25519 signature. -#[derive(Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[allow(non_snake_case)] -pub struct Signature { - pub(crate) R_bytes: [u8; 32], - pub(crate) s_bytes: [u8; 32], -} - -impl core::fmt::Debug for Signature { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - fmt.debug_struct("Signature") - .field("R_bytes", &hex::encode(&self.R_bytes)) - .field("s_bytes", &hex::encode(&self.s_bytes)) - .finish() - } -} - -impl From<[u8; 64]> for Signature { - #[allow(non_snake_case)] - fn from(bytes: [u8; 64]) -> Signature { - let mut R_bytes = [0; 32]; - R_bytes.copy_from_slice(&bytes[0..32]); - let mut s_bytes = [0; 32]; - s_bytes.copy_from_slice(&bytes[32..64]); - Signature { R_bytes, s_bytes } - } -} - -impl TryFrom<&[u8]> for Signature { - type Error = Error; - - fn try_from(slice: &[u8]) -> Result { - if slice.len() == 64 { - let mut bytes = [0u8; 64]; - bytes[..].copy_from_slice(slice); - Ok(bytes.into()) - } else { - Err(Error::InvalidSliceLength) - } - } -} - -impl From for [u8; 64] { - fn from(sig: Signature) -> [u8; 64] { - sig.to_bytes() - } -} - -impl Signature { - /// Returns the bytes of the signature. - /// - /// This is the same as `.into()`, but does not require type inference. - pub fn to_bytes(&self) -> [u8; 64] { - let mut bytes = [0; 64]; - bytes[0..32].copy_from_slice(&self.R_bytes[..]); - bytes[32..64].copy_from_slice(&self.s_bytes[..]); - bytes - } -} diff --git a/src/signing_key.rs b/src/signing_key.rs index 4c51756..954c2e8 100644 --- a/src/signing_key.rs +++ b/src/signing_key.rs @@ -1,6 +1,7 @@ use core::convert::TryFrom; use curve25519_dalek::{constants, scalar::Scalar}; +use ed25519::signature::{self, Signer}; use rand_core::{CryptoRng, RngCore}; use sha2::{Digest, Sha512}; @@ -156,7 +157,6 @@ impl SigningKey { } /// Create a signature on `msg` using this key. - #[allow(non_snake_case)] pub fn sign(&self, msg: &[u8]) -> Signature { let r = Scalar::from_hash(Sha512::default().chain(&self.prefix[..]).chain(msg)); @@ -173,6 +173,13 @@ impl SigningKey { let s_bytes = (r + k * self.s).to_bytes(); - Signature { R_bytes, s_bytes } + Signature::from_components(R_bytes, s_bytes) + } +} + +impl Signer for SigningKey { + #[allow(non_snake_case)] + fn try_sign(&self, msg: &[u8]) -> signature::Result { + Ok(self.sign(msg)) } } diff --git a/src/verification_key.rs b/src/verification_key.rs index e7453ae..7039aed 100644 --- a/src/verification_key.rs +++ b/src/verification_key.rs @@ -5,6 +5,7 @@ use curve25519_dalek::{ scalar::Scalar, traits::IsIdentity, }; +use ed25519::signature::{self, Verifier}; use sha2::{Digest, Sha512}; use crate::{Error, Signature}; @@ -27,7 +28,7 @@ use crate::{Error, Signature}; /// # let sig = sk.sign(msg); /// # let vk_bytes = VerificationKeyBytes::from(&sk); /// VerificationKey::try_from(vk_bytes) -/// .and_then(|vk| vk.verify(&sig, msg)); +/// .and_then(|vk| vk.verify( msg,&sig)); /// ``` #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -204,10 +205,10 @@ impl VerificationKey { /// /// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519 /// [ZIP215]: https://github.com/zcash/zips/blob/master/zip-0215.rst - pub fn verify(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> { + pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { let k = Scalar::from_hash( Sha512::default() - .chain(&signature.R_bytes[..]) + .chain(signature.r_bytes()) .chain(&self.A_bytes.0[..]) .chain(msg), ); @@ -219,9 +220,10 @@ impl VerificationKey { #[allow(non_snake_case)] pub(crate) fn verify_prehashed(&self, signature: &Signature, k: Scalar) -> Result<(), Error> { // `s_bytes` MUST represent an integer less than the prime `l`. - let s = Scalar::from_canonical_bytes(signature.s_bytes).ok_or(Error::InvalidSignature)?; + let s = + Scalar::from_canonical_bytes(*signature.s_bytes()).ok_or(Error::InvalidSignature)?; // `R_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519. - let R = CompressedEdwardsY(signature.R_bytes) + let R = CompressedEdwardsY(*signature.r_bytes()) .decompress() .ok_or(Error::InvalidSignature)?; // We checked the encoding of A_bytes when constructing `self`. @@ -239,3 +241,10 @@ impl VerificationKey { } } } + +impl Verifier for VerificationKey { + fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { + self.verify(msg, signature) + .map_err(|_| signature::Error::new()) + } +} diff --git a/tests/rfc8032.rs b/tests/rfc8032.rs index 0384de3..a575fa2 100644 --- a/tests/rfc8032.rs +++ b/tests/rfc8032.rs @@ -13,7 +13,7 @@ fn rfc8032_test_case(sk_bytes: Vec, pk_bytes: Vec, sig_bytes: Vec, m let pk: VerificationKey = bincode::deserialize(&pk_bytes).expect("pk should deserialize"); let sig: Signature = bincode::deserialize(&sig_bytes).expect("sig should deserialize"); - assert!(pk.verify(&sig, &msg).is_ok(), "verification failed"); + assert!(pk.verify(&msg, &sig).is_ok(), "verification failed"); let pk_from_sk = VerificationKey::from(&sk); assert_eq!( diff --git a/tests/small_order.rs b/tests/small_order.rs index f7f4a34..b180ef8 100644 --- a/tests/small_order.rs +++ b/tests/small_order.rs @@ -94,7 +94,7 @@ fn individual_matches_batch_verification() -> Result<(), Report> { let sig = Signature::from(case.sig_bytes); let vkb = VerificationKeyBytes::from(case.vk_bytes); let individual_verification = - VerificationKey::try_from(vkb).and_then(|vk| vk.verify(&sig, msg)); + VerificationKey::try_from(vkb).and_then(|vk| vk.verify(msg, &sig)); let mut bv = batch::Verifier::new(); bv.queue((vkb, sig, msg)); let batch_verification = bv.verify(rand::thread_rng()); diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs index 720b711..d060666 100644 --- a/tests/unit_tests.rs +++ b/tests/unit_tests.rs @@ -49,5 +49,5 @@ fn sign_and_verify() { let sig = sk.sign(&msg[..]); - assert_eq!(pk.verify(&sig, &msg[..]), Ok(())) + assert_eq!(pk.verify(&msg[..], &sig), Ok(())) } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index ff9d78b..98e29f6 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -58,7 +58,7 @@ impl TestCase { fn check_zip215(&self) -> Result<(), Report> { use ed25519_consensus::{Signature, VerificationKey}; let sig = Signature::from(self.sig_bytes); - VerificationKey::try_from(self.vk_bytes).and_then(|vk| vk.verify(&sig, b"Zcash"))?; + VerificationKey::try_from(self.vk_bytes).and_then(|vk| vk.verify(b"Zcash", &sig))?; Ok(()) } }