diff --git a/crates/rooch-types/src/crypto.rs b/crates/rooch-types/src/crypto.rs index e4cc208bb1..202cf7a842 100644 --- a/crates/rooch-types/src/crypto.rs +++ b/crates/rooch-types/src/crypto.rs @@ -6,30 +6,31 @@ use crate::{ error::{RoochError, RoochResult}, }; use derive_more::{AsMut, AsRef, From}; +use ed25519_dalek::{ + Keypair as Ed25519KeyPair, PublicKey as Ed25519PublicKey, Signature as Ed25519Signature, + PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, +}; pub use enum_dispatch::enum_dispatch; use eyre::eyre; -use fastcrypto::{encoding::{Base64, Encoding}, traits::AllowedRng}; -use fastcrypto::hash::{Blake2b256, HashFunction}; +use fastcrypto::encoding::{Base64, Encoding}; use fastcrypto::error::FastCryptoError; -pub use fastcrypto::traits::{ - EncodeDecodeBase64, ToFromBytes, -}; -use ed25519_dalek::{Keypair as Ed25519KeyPair, PublicKey as Ed25519PublicKey, Signature as Ed25519Signature, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH}; -use secp256k1::{Secp256k1, KeyPair as Secp256k1KeyPair, PublicKey as Secp256k1PublicKey, schnorr::Signature as SchnorrSignature, ecdsa::Signature as ECDSASignature, XOnlyPublicKey}; -use secp256k1::constants::{SCHNORR_PUBLIC_KEY_SIZE, SCHNORR_SIGNATURE_SIZE, PUBLIC_KEY_SIZE, COMPACT_SIGNATURE_SIZE}; +use fastcrypto::hash::{Blake2b256, HashFunction}; use moveos_types::{h256::H256, serde::Readable}; -use rand::{rngs::StdRng, SeedableRng}; +use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng}; use schemars::JsonSchema; -use serde::{ser::Serializer, de::DeserializeOwned}; +use secp256k1::constants::{ + COMPACT_SIGNATURE_SIZE, PUBLIC_KEY_SIZE, SCHNORR_PUBLIC_KEY_SIZE, SCHNORR_SIGNATURE_SIZE, +}; +use secp256k1::{ + ecdsa::Signature as ECDSASignature, schnorr::Signature as SchnorrSignature, + KeyPair as Secp256k1KeyPair, Message, PublicKey as Secp256k1PublicKey, Secp256k1, + XOnlyPublicKey, +}; +use serde::{de::DeserializeOwned, ser::Serializer}; use serde::{Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, Bytes}; -use std::{hash::Hash, str::FromStr, fmt::Display, borrow::Borrow}; +use std::{fmt::Display, hash::Hash, str::FromStr}; use strum_macros::EnumString; -// pub use dyn_clone::DynClone; -// pub use traitobject::traitobject; -// pub use dyn_trait::dyn_trait; -// use anyhow::anyhow; -// use std::any::Any; pub type DefaultHash = Blake2b256; @@ -146,7 +147,7 @@ impl EncodeDecodeBase64 for RoochKeyPair { &Secp256k1::new(), bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, )?)) - }, + } BuiltinScheme::Schnorr => { Ok(RoochKeyPair::Schnorr(Secp256k1KeyPair::from_seckey_slice( &Secp256k1::new(), @@ -259,7 +260,7 @@ impl<'de> Deserialize<'de> for PublicKey { impl PublicKey { pub fn flag(&self) -> u8 { - Ed25519RoochSignature::SCHEME.flag() + BuiltinScheme::Ed25519.flag() } pub fn try_from_bytes( scheme: BuiltinScheme, @@ -281,9 +282,9 @@ impl PublicKey { pub fn scheme(&self) -> BuiltinScheme { match self { - PublicKey::Ed25519(_) => Ed25519RoochSignature::SCHEME, - PublicKey::ECDSA(_) => ECDSARoochSignature::SCHEME, - PublicKey::Schnorr(_) => SchnorrRoochSignature::SCHEME, + PublicKey::Ed25519(_) => BuiltinScheme::Ed25519, + PublicKey::ECDSA(_) => BuiltinScheme::ECDSA, + PublicKey::Schnorr(_) => BuiltinScheme::Schnorr, } } } @@ -323,7 +324,6 @@ impl From<&PublicKey> for RoochAddress { RoochAddress(H256(g_arr.digest)) } } - pub trait Authenticator: ToFromBytes + Display + Serialize + DeserializeOwned + Send + Sync + 'static + Clone { @@ -332,40 +332,39 @@ pub trait Authenticator: const LENGTH: usize; } - pub trait VerifyingKey: Serialize + DeserializeOwned + std::hash::Hash + Display - + Eq // required to make some cached bytes representations explicit. - + Ord // required to put keys in BTreeMap. + + Eq + + Ord + ToFromBytes - + for<'a> From<&'a Self::PrivKey> // conversion PrivateKey -> PublicKey. + + for<'a> From<&'a Self::PrivKey> + Send + Sync + 'static + Clone { - type PrivKey: SigningKey; - type Sig: Authenticator; + type PrivKey: SigningKey; + type Sig: Authenticator; const LENGTH: usize; - /// Use Self to verify that the provided signature for a given message bytestring is authentic. - /// Returns Error if it is inauthentic, or otherwise returns (). fn verify(&self, msg: &[u8], signature: &Self::Sig) -> Result<(), FastCryptoError>; - // Expected to be overridden by implementations - /// Batch verification over the same message. Implementations of this method can be fast, - /// assuming rogue key checks have already been performed. - /// TODO: take as input a flag to denote if rogue key protection already took place. #[cfg(any(test, feature = "experimental"))] - fn verify_batch_empty_fail(msg: &[u8], pks: &[Self], sigs: &[Self::Sig]) -> Result<(), eyre::Report> { + fn verify_batch_empty_fail( + msg: &[u8], + pks: &[Self], + sigs: &[Self::Sig], + ) -> Result<(), eyre::Report> { if sigs.is_empty() { return Err(eyre!("Critical Error! This behaviour can signal something dangerous, and that someone may be trying to bypass signature verification through providing empty batches.")); } if pks.len() != sigs.len() { - return Err(eyre!("Mismatch between number of signatures and public keys provided")); + return Err(eyre!( + "Mismatch between number of signatures and public keys provided" + )); } pks.iter() .zip(sigs) @@ -373,17 +372,22 @@ pub trait VerifyingKey: .map_err(|_| eyre!("Signature verification failed")) } - // Expected to be overridden by implementations - /// Batch verification over different messages. Implementations of this method can be fast, - /// assuming rogue key checks have already been performed. - /// TODO: take as input a flag to denote if rogue key protection already took place. #[cfg(any(test, feature = "experimental"))] - fn verify_batch_empty_fail_different_msg<'a, M>(msgs: &[M], pks: &[Self], sigs: &[Self::Sig]) -> Result<(), eyre::Report> where M: Borrow<[u8]> + 'a { + fn verify_batch_empty_fail_different_msg<'a, M>( + msgs: &[M], + pks: &[Self], + sigs: &[Self::Sig], + ) -> Result<(), eyre::Report> + where + M: std::borrow::Borrow<[u8]> + 'a, + { if sigs.is_empty() { return Err(eyre!("Critical Error! This behaviour can signal something dangerous, and that someone may be trying to bypass signature verification through providing empty batches.")); } if pks.len() != sigs.len() || pks.len() != msgs.len() { - return Err(eyre!("Mismatch between number of messages, signatures and public keys provided")); + return Err(eyre!( + "Mismatch between number of messages, signatures and public keys provided" + )); } pks.iter() .zip(sigs) @@ -400,61 +404,85 @@ pub trait SigningKey: ToFromBytes + Serialize + DeserializeOwned + Send + Sync + } pub trait Signer { - /// Create a new signature over a message. fn sign(&self, msg: &[u8]) -> Sig; } -pub trait KeypairTraits: Sized + From + Signer + EncodeDecodeBase64 + FromStr -{ - // Define the methods required for the KeypairTraits trait - /// Trait impl'd by a public / private key pair in asymmetric cryptography. +pub trait ToFromBytes: AsRef<[u8]> + Sized { + /// Parse an object from its byte representation + fn from_bytes(bytes: &[u8]) -> Result; + + /// Borrow a byte slice representing the serialized form of this object + fn as_bytes(&self) -> &[u8] { + self.as_ref() + } +} +pub trait EncodeDecodeBase64: Sized { + fn encode_base64(&self) -> String; + fn decode_base64(value: &str) -> Result; +} + +pub trait AllowedRng: CryptoRng + RngCore {} + +pub trait KeypairTraits: + Sized + From + Signer + EncodeDecodeBase64 + FromStr +{ type PubKey: VerifyingKey; type PrivKey: SigningKey; type Sig: Authenticator; - /// Get the public key. fn public(&'_ self) -> &'_ Self::PubKey; - /// Get the private key. fn private(self) -> Self::PrivKey; #[cfg(feature = "copy_key")] fn copy(&self) -> Self; - /// Generate a new keypair using the given RNG. fn generate(rng: &mut R) -> Self; } -// #[enum_dispatch(RoochSignatureInner)] -// pub enum RoochSignatureWrapper { -// // Define the possible wrapper variants -// } - -// impl RoochSignatureInner for RoochSignatureWrapper { -// type Sig = dyn Authenticator; -// type PubKey = dyn VerifyingKey + RoochPublicKey; -// type KeyPair = dyn KeypairTraits; +pub trait RoochSignatureInner: + Sized + ToFromBytes + PartialEq + Eq + Hash +{ + fn get_verification_inputs(&self, author: RoochAddress) -> RoochResult<(Sig, PubKey)>; + fn new(kp: &KeyPair, message: &[u8]) -> Self; +} -// fn get_verification_inputs(&self, author: RoochAddress) -> RoochResult<(Self::Sig, Self::PubKey)> { +// // +// // Account Signatures +// // +// // This struct exists due to the limitations of the `enum_dispatch` library. +// // +// pub trait RoochSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { +// type Sig: Authenticator; +// type PubKey: VerifyingKey + RoochPublicKey; +// type KeyPair: KeypairTraits; + +// const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; +// const SCHEME: BuiltinScheme = Self::PubKey::SIGNATURE_SCHEME; + +// fn get_verification_inputs( +// &self, +// author: RoochAddress, +// ) -> RoochResult<(Self::Sig, Self::PubKey)> { // // Is this signature emitted by the expected author? // let bytes = self.public_key_bytes(); // let pk = Self::PubKey::from_bytes(bytes) // .map_err(|_| RoochError::KeyConversionError("Invalid public key".to_owned()))?; - + // let received_addr = RoochAddress::from(&pk); // if received_addr != author { // return Err(RoochError::IncorrectSigner { // error: format!("Signature get_verification_inputs() failure. Author is {}, received address is {}", author, received_addr) // }); // } - + // // deserialize the signature // let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { // RoochError::InvalidSignature { // error: "Fail to get pubkey and sig".to_owned(), // } // })?; - + // Ok((signature, pk)) // } @@ -463,7 +491,7 @@ pub trait KeypairTraits: Sized + From + Signer + Encod // let mut signature_bytes: Vec = Vec::new(); // signature_bytes -// .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); +// .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); // signature_bytes.extend_from_slice(sig.as_ref()); // signature_bytes.extend_from_slice(kp.public().as_ref()); @@ -472,68 +500,6 @@ pub trait KeypairTraits: Sized + From + Signer + Encod // } // } -// impl From for RoochSignatureWrapper -// where -// T: RoochSignatureInner, -// { -// fn from(inner: T) -> Self { -// RoochSignatureWrapper::Inner(inner) -// } -// } - -// -// Account Signatures -// -// This struct exists due to the limitations of the `enum_dispatch` library. -// -pub trait RoochSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { - type Sig: Authenticator; - type PubKey: VerifyingKey + RoochPublicKey; - type KeyPair: KeypairTraits; - - const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; - const SCHEME: BuiltinScheme = Self::PubKey::SIGNATURE_SCHEME; - - fn get_verification_inputs( - &self, - author: RoochAddress, - ) -> RoochResult<(Self::Sig, Self::PubKey)> { - // Is this signature emitted by the expected author? - let bytes = self.public_key_bytes(); - let pk = Self::PubKey::from_bytes(bytes) - .map_err(|_| RoochError::KeyConversionError("Invalid public key".to_owned()))?; - - let received_addr = RoochAddress::from(&pk); - if received_addr != author { - return Err(RoochError::IncorrectSigner { - error: format!("Signature get_verification_inputs() failure. Author is {}, received address is {}", author, received_addr) - }); - } - - // deserialize the signature - let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { - RoochError::InvalidSignature { - error: "Fail to get pubkey and sig".to_owned(), - } - })?; - - Ok((signature, pk)) - } - - fn new(kp: &Self::KeyPair, message: &[u8]) -> Self { - let sig = Signer::sign(kp, message); - - let mut signature_bytes: Vec = Vec::new(); - signature_bytes - .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); - - signature_bytes.extend_from_slice(sig.as_ref()); - signature_bytes.extend_from_slice(kp.public().as_ref()); - Self::from_bytes(&signature_bytes[..]) - .expect("Serialized signature did not have expected size") - } -} - // Enums for signature scheme signatures #[enum_dispatch] #[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)] @@ -599,26 +565,22 @@ impl Signature { let bytes = self.signature_bytes(); match self.scheme() { BuiltinScheme::Ed25519 => Ok(CompressedSignature::Ed25519( - (&Ed25519Signature::from_bytes(bytes).map_err(|_| { - RoochError::InvalidSignature { + Ed25519Signature::from_bytes(bytes) + .map_err(|_| RoochError::InvalidSignature { error: "Cannot parse sig".to_owned(), - } - })?) + })? .into(), )), BuiltinScheme::ECDSA => Ok(CompressedSignature::ECDSA( - (&ECDSASignature::from_compact(bytes).map_err(|_| { - RoochError::InvalidSignature { + ECDSASignature::from_compact(bytes) + .map_err(|_| RoochError::InvalidSignature { error: "Cannot parse sig".to_owned(), - } - })?) + })? .into(), )), BuiltinScheme::Schnorr => Ok(CompressedSignature::Schnorr( - SchnorrSignature::from_slice(bytes).map_err(|_| { - RoochError::InvalidSignature { - error: "Cannot parse sig".to_owned(), - } + SchnorrSignature::from_slice(bytes).map_err(|_| RoochError::InvalidSignature { + error: "Cannot parse sig".to_owned(), })?, )), _ => Err(RoochError::UnsupportedFeatureError { @@ -633,20 +595,20 @@ impl Signature { let bytes = self.public_key_bytes(); match self.scheme() { BuiltinScheme::Ed25519 => Ok(PublicKey::Ed25519( - (&Ed25519PublicKey::from_bytes(bytes) - .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) - .into(), - )), + Ed25519PublicKey::from_bytes(bytes) + .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?, + ) + .into()), BuiltinScheme::ECDSA => Ok(PublicKey::ECDSA( - (&Secp256k1PublicKey::from_bytes(bytes) - .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) - .into(), - )), + Secp256k1PublicKey::from_slice(bytes) + .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?, + ) + .into()), BuiltinScheme::Schnorr => Ok(PublicKey::Schnorr( - (XOnlyPublicKey::from_slice(bytes) - .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) - .into(), - )), + XOnlyPublicKey::from_slice(bytes) + .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?, + ) + .into()), _ => Err(RoochError::UnsupportedFeatureError { error: "Unsupported signature scheme in MultiSig".to_owned(), }), @@ -677,11 +639,11 @@ impl ToFromBytes for Signature { fn from_bytes(bytes: &[u8]) -> Result { match bytes.first() { Some(x) => { - if x == &Ed25519RoochSignature::SCHEME.flag() { + if x == &BuiltinScheme::Ed25519.flag() { Ok(::from_bytes(bytes)?.into()) - } else if x == &ECDSARoochSignature::SCHEME.flag() { + } else if x == &BuiltinScheme::ECDSA.flag() { Ok(::from_bytes(bytes)?.into()) - } else if x == &SchnorrRoochSignature::SCHEME.flag() { + } else if x == &BuiltinScheme::Schnorr.flag() { Ok(::from_bytes(bytes)?.into()) } else { Err(FastCryptoError::InvalidInput) @@ -721,21 +683,56 @@ pub trait RoochSignature: Sized + ToFromBytes { T: Serialize; } -impl RoochSignature for S { +impl RoochSignature for ECDSARoochSignature { + fn signature_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_bytes()[1..1 + PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE] + } + + fn public_key_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_bytes()[PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1..] + } + + fn scheme(&self) -> BuiltinScheme { + BuiltinScheme::ECDSA + } + + fn verify_secure(&self, value: &T, author: RoochAddress) -> Result<(), RoochError> + where + T: Serialize, + { + let mut hasher = DefaultHash::default(); + hasher.update(&bcs::to_bytes(&value).expect("Message serialization should not fail")); + let digest = hasher.finalize().digest; + + let (sig, pk) = &self.get_verification_inputs(author)?; + let message = Message::from_slice(&digest).unwrap(); + Secp256k1::verify_ecdsa(&Secp256k1::new(), &message, sig, pk).map_err(|e| { + RoochError::InvalidSignature { + error: format!("Fail to verify user sig {}", e), + } + }) + } +} + +impl RoochSignature for Ed25519RoochSignature { fn signature_bytes(&self) -> &[u8] { // Access array slice is safe because the array bytes is initialized as // flag || signature || pubkey with its defined length. - &self.as_ref()[1..1 + S::Sig::LENGTH] + &self.as_bytes()[1..1 + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH] } fn public_key_bytes(&self) -> &[u8] { // Access array slice is safe because the array bytes is initialized as // flag || signature || pubkey with its defined length. - &self.as_ref()[S::Sig::LENGTH + 1..] + &self.as_bytes()[PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH + 1..] } fn scheme(&self) -> BuiltinScheme { - S::PubKey::SIGNATURE_SCHEME + BuiltinScheme::Ed25519 } fn verify_secure(&self, value: &T, author: RoochAddress) -> Result<(), RoochError> @@ -747,13 +744,48 @@ impl RoochSignature for S { let digest = hasher.finalize().digest; let (sig, pk) = &self.get_verification_inputs(author)?; - pk.verify(&digest, sig) + pk.verify_strict(&digest, sig) .map_err(|e| RoochError::InvalidSignature { error: format!("Fail to verify user sig {}", e), }) } } +impl RoochSignature for SchnorrRoochSignature { + fn signature_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_bytes()[1..1 + SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE] + } + + fn public_key_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_bytes()[SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE + 1..] + } + + fn scheme(&self) -> BuiltinScheme { + BuiltinScheme::Schnorr + } + + fn verify_secure(&self, value: &T, author: RoochAddress) -> Result<(), RoochError> + where + T: Serialize, + { + let mut hasher = DefaultHash::default(); + hasher.update(&bcs::to_bytes(&value).expect("Message serialization should not fail")); + let digest = hasher.finalize().digest; + + let (sig, pk) = &self.get_verification_inputs(author)?; + let message = Message::from_slice(&digest).unwrap(); + Secp256k1::verify_schnorr(&Secp256k1::new(), sig, &message, pk.into()).map_err(|e| { + RoochError::InvalidSignature { + error: format!("Fail to verify user sig {}", e), + } + }) + } +} + // // Schnorr Rooch Signature port // @@ -776,20 +808,65 @@ impl Default for SchnorrRoochSignature { impl ToFromBytes for SchnorrRoochSignature { fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + if bytes.len() != SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE + 1 { + return Err(FastCryptoError::InputLengthWrong( + SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE + 1, + )); } - let mut sig_bytes = [0; Self::LENGTH]; + let mut sig_bytes = [0; SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE + 1]; sig_bytes.copy_from_slice(bytes); Ok(Self(sig_bytes)) } } -impl RoochSignatureInner for SchnorrRoochSignature { - type Sig = SchnorrSignature; - type PubKey = XOnlyPublicKey; - type KeyPair = Secp256k1KeyPair; - const LENGTH: usize = SCHNORR_PUBLIC_KEY_SIZE + SCHNORR_SIGNATURE_SIZE + 1; +impl Signer for Secp256k1KeyPair { + fn sign(&self, msg: &[u8]) -> Signature { + SchnorrRoochSignature::new(self, msg).into() + } +} + +impl RoochSignatureInner + for SchnorrRoochSignature +{ + fn get_verification_inputs( + &self, + author: RoochAddress, + ) -> RoochResult<(SchnorrSignature, XOnlyPublicKey)> { + // Is this signature emitted by the expected author? + let bytes = self.public_key_bytes(); + + let pk = XOnlyPublicKey::from_slice(bytes) + .map_err(|_| RoochError::KeyConversionError("Invalid public key".to_owned()))?; + + let received_addr = RoochAddress::from(&pk); + if received_addr != author { + return Err(RoochError::IncorrectSigner { + error: format!("Signature get_verification_inputs() failure. Author is {}, received address is {}", author, received_addr) + }); + } + + // deserialize the signature + let signature = SchnorrSignature::from_slice(self.signature_bytes()).map_err(|_| { + RoochError::InvalidSignature { + error: "Fail to get pubkey and sig".to_owned(), + } + })?; + + Ok((signature, pk)) + } + + fn new(kp: &Secp256k1KeyPair, message: &[u8]) -> Self { + let sig: SchnorrSignature = SchnorrSignature::from_slice(message).unwrap().into(); + + let mut signature_bytes: Vec = Vec::new(); + signature_bytes + .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); + + signature_bytes.extend_from_slice(sig.as_ref()); + signature_bytes.extend_from_slice(kp.x_only_public_key().0.serialize().as_ref()); + Self::from_bytes(&signature_bytes[..]) + .expect("Serialized signature did not have expected size") + } } // @@ -814,10 +891,12 @@ impl Default for Ed25519RoochSignature { impl ToFromBytes for Ed25519RoochSignature { fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + if bytes.len() != PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH + 1 { + return Err(FastCryptoError::InputLengthWrong( + PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH + 1, + )); } - let mut sig_bytes = [0; Self::LENGTH]; + let mut sig_bytes = [0; PUBLIC_KEY_LENGTH + SIGNATURE_LENGTH + 1]; sig_bytes.copy_from_slice(bytes); Ok(Self(sig_bytes)) } @@ -829,11 +908,48 @@ impl Signer for Ed25519KeyPair { } } -impl RoochSignatureInner for Ed25519RoochSignature { - type Sig = Ed25519Signature; - type PubKey = Ed25519PublicKey; - type KeyPair = Ed25519KeyPair; - const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1; +impl RoochSignatureInner + for Ed25519RoochSignature +{ + fn get_verification_inputs( + &self, + author: RoochAddress, + ) -> RoochResult<(Ed25519Signature, Ed25519PublicKey)> { + // Is this signature emitted by the expected author? + let bytes = self.public_key_bytes(); + + let pk = Ed25519PublicKey::from_bytes(bytes) + .map_err(|_| RoochError::KeyConversionError("Invalid public key".to_owned()))?; + + let received_addr = RoochAddress::from(&pk); + if received_addr != author { + return Err(RoochError::IncorrectSigner { + error: format!("Signature get_verification_inputs() failure. Author is {}, received address is {}", author, received_addr) + }); + } + + // deserialize the signature + let signature = Ed25519Signature::from_bytes(self.signature_bytes()).map_err(|_| { + RoochError::InvalidSignature { + error: "Fail to get pubkey and sig".to_owned(), + } + })?; + + Ok((signature, pk)) + } + + fn new(kp: &Ed25519KeyPair, message: &[u8]) -> Self { + let sig: Ed25519Signature = Ed25519Signature::from_bytes(message).unwrap().into(); + + let mut signature_bytes: Vec = Vec::new(); + signature_bytes + .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); + + signature_bytes.extend_from_slice(sig.to_bytes().as_ref()); + signature_bytes.extend_from_slice(kp.public.as_ref()); + Self::from_bytes(&signature_bytes[..]) + .expect("Serialized signature did not have expected size") + } } // @@ -849,28 +965,74 @@ pub struct ECDSARoochSignature( [u8; PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1], ); -impl RoochSignatureInner for ECDSARoochSignature { - type Sig = ECDSASignature; - type PubKey = Secp256k1PublicKey; - type KeyPair = Secp256k1KeyPair; - const LENGTH: usize = PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1; +// Implementation useful for simplify testing when mock signature is needed +impl Default for ECDSARoochSignature { + fn default() -> Self { + Self([0; PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1]) + } } impl ToFromBytes for ECDSARoochSignature { fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + if bytes.len() != PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1 { + return Err(FastCryptoError::InputLengthWrong( + PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1, + )); } - let mut sig_bytes = [0; Self::LENGTH]; + let mut sig_bytes = [0; PUBLIC_KEY_SIZE + COMPACT_SIGNATURE_SIZE + 1]; sig_bytes.copy_from_slice(bytes); Ok(Self(sig_bytes)) } } -// TODO decide keypair sig ECDSA or Schnorr? -impl Signer for Secp256k1KeyPair { - fn sign(&self, msg: &[u8]) -> Signature { - ECDSARoochSignature::new(self, msg).into() +// TODO generalize Secp256k1KeyPair +// impl Signer for Secp256k1KeyPair { +// fn sign(&self, msg: &[u8]) -> Signature { +// ECDSARoochSignature::new(self, msg).into() +// } +// } + +impl RoochSignatureInner + for ECDSARoochSignature +{ + fn get_verification_inputs( + &self, + author: RoochAddress, + ) -> RoochResult<(ECDSASignature, Secp256k1PublicKey)> { + // Is this signature emitted by the expected author? + let bytes = self.public_key_bytes(); + + let pk = Secp256k1PublicKey::from_slice(bytes) + .map_err(|_| RoochError::KeyConversionError("Invalid public key".to_owned()))?; + + let received_addr = RoochAddress::from(&pk); + if received_addr != author { + return Err(RoochError::IncorrectSigner { + error: format!("Signature get_verification_inputs() failure. Author is {}, received address is {}", author, received_addr) + }); + } + + // deserialize the signature + let signature = ECDSASignature::from_der(self.signature_bytes()).map_err(|_| { + RoochError::InvalidSignature { + error: "Fail to get pubkey and sig".to_owned(), + } + })?; + + Ok((signature, pk)) + } + + fn new(kp: &Secp256k1KeyPair, message: &[u8]) -> Self { + let sig: ECDSASignature = ECDSASignature::from_der(message).unwrap().into(); + + let mut signature_bytes: Vec = Vec::new(); + signature_bytes + .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); + + signature_bytes.extend_from_slice(sig.serialize_compact().as_ref()); + signature_bytes.extend_from_slice(kp.public_key().serialize().as_ref()); + Self::from_bytes(&signature_bytes[..]) + .expect("Serialized signature did not have expected size") } } @@ -886,21 +1048,16 @@ where #[cfg(test)] mod tests { - use crate::address::{RoochAddress, NostrAddress, self}; - use fastcrypto::{ - ed25519::{Ed25519KeyPair, Ed25519PrivateKey}, - traits::{KeyPair, ToFromBytes}, - }; - use secp256k1::{SecretKey, KeyPair as Secp256k1KeyPair, Secp256k1}; + use crate::address::{self, NostrAddress, RoochAddress}; + use secp256k1::{KeyPair as Secp256k1KeyPair, Secp256k1, SecretKey}; // this test ensure the public key to address keep the same as the old version // we should also keep the public key to address algorithm the same as the move version #[test] fn test_public_key_to_address() { - let private_key = Ed25519PrivateKey::from_bytes(&[0u8; 32]).unwrap(); - let keypair: Ed25519KeyPair = private_key.into(); + let keypair = Secp256k1KeyPair::from_seckey_slice(&Secp256k1::new(), &[0u8; 32]).unwrap(); //println!("public_key: {}", hex::encode(keypair.public().as_bytes())); - let address: RoochAddress = keypair.public().into(); + let address: RoochAddress = keypair.public_key().into(); //println!("address: {:?}", address); assert_eq!( address.to_string(), @@ -909,11 +1066,14 @@ mod tests { } #[test] - fn test_x_only_public_key_to_address() { + fn test_x_only_public_key_to_nostr_address() { let secret_key: SecretKey = SecretKey::from_slice(&[0u8; 32]).unwrap(); let secp = Secp256k1::new(); let keypair: Secp256k1KeyPair = secret_key.keypair(&secp); - println!("public_key: {}", hex::encode(keypair.x_only_public_key().0.serialize())); + println!( + "public_key: {}", + hex::encode(keypair.x_only_public_key().0.serialize()) + ); let address: NostrAddress = address::NostrAddress(keypair.x_only_public_key().0); println!("address: {:?}", address); assert_eq!( diff --git a/crates/rooch-types/src/transaction/authenticator.rs b/crates/rooch-types/src/transaction/authenticator.rs index 65856af09b..56aca5da5a 100644 --- a/crates/rooch-types/src/transaction/authenticator.rs +++ b/crates/rooch-types/src/transaction/authenticator.rs @@ -11,15 +11,12 @@ use crate::crypto::{BuiltinScheme, Signature}; use anyhow::Result; #[cfg(any(test, feature = "fuzzing"))] -use ethers::types::U256; -#[cfg(any(test, feature = "fuzzing"))] -use fastcrypto::ed25519::Ed25519KeyPair; +use ed25519_dalek::Keypair; #[cfg(any(test, feature = "fuzzing"))] -use fastcrypto::traits::KeyPair; +use ethers::types::U256; #[cfg(any(test, feature = "fuzzing"))] use proptest::{collection::vec, prelude::*}; #[cfg(any(test, feature = "fuzzing"))] -use rand::{rngs::StdRng, SeedableRng}; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, str::FromStr}; @@ -56,13 +53,12 @@ impl Arbitrary for Ed25519Authenticator { #[cfg(any(test, feature = "fuzzing"))] prop_compose! { fn arb_ed25519_authenticator()( - seed in any::(), message in vec(any::(), 1..1000) ) -> Ed25519Authenticator { - let mut rng = StdRng::seed_from_u64(seed); - let ed25519_keypair: Ed25519KeyPair = Ed25519KeyPair::generate(&mut rng); + let mut csprng = OsRng{}; + let keypair: Keypair = Keypair::generate(&mut csprng); Ed25519Authenticator { - signature: Signature::new_hashed(&message, &ed25519_keypair) + signature: Signature::new_hashed(&message, &keypair.into()) } } } @@ -70,20 +66,20 @@ prop_compose! { // TODO: MultiEd25519 #[derive(Clone, Debug)] -pub struct Secp256k1Authenticator { +pub struct ECDSAAuthenticator { pub signature: ethers::core::types::Signature, } -impl BuiltinAuthenticator for Secp256k1Authenticator { +impl BuiltinAuthenticator for ECDSAAuthenticator { fn scheme(&self) -> BuiltinScheme { - BuiltinScheme::Secp256k1 + BuiltinScheme::ECDSA } fn encode(&self) -> Vec { bcs::to_bytes(self).expect("encode should success.") } } -impl Serialize for Secp256k1Authenticator { +impl Serialize for ECDSAAuthenticator { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, @@ -100,46 +96,114 @@ impl Serialize for Secp256k1Authenticator { } } -impl<'de> Deserialize<'de> for Secp256k1Authenticator { +impl<'de> Deserialize<'de> for ECDSAAuthenticator { fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { #[derive(::serde::Deserialize)] - #[serde(rename = "Secp256k1Authenticator")] + #[serde(rename = "ECDSAAuthenticator")] struct Value { signature: Vec, } let value = Value::deserialize(deserializer)?; let signature = ethers::core::types::Signature::try_from(value.signature.as_slice()) .map_err(|e| serde::de::Error::custom(e.to_string()))?; - Ok(Secp256k1Authenticator { signature }) + Ok(ECDSAAuthenticator { signature }) } } #[cfg(any(test, feature = "fuzzing"))] -impl Arbitrary for Secp256k1Authenticator { +impl Arbitrary for ECDSAAuthenticator { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - arb_secp256k1_authenticator().boxed() + arb_ecdsa_authenticator().boxed() } type Strategy = BoxedStrategy; } #[cfg(any(test, feature = "fuzzing"))] prop_compose! { - fn arb_secp256k1_authenticator()( + fn arb_ecdsa_authenticator()( r in vec(any::(), 4..=4).prop_map(|v| U256(v.try_into().unwrap())), s in vec(any::(), 4..=4).prop_map(|v| U256(v.try_into().unwrap())), // Although v is an u64 type, it is actually an u8 value. v in any::().prop_map(::from), - ) -> Secp256k1Authenticator { - Secp256k1Authenticator { + ) -> ECDSAAuthenticator { + ECDSAAuthenticator { signature: ethers::core::types::Signature {r, s, v}, } } } +#[derive(Clone, Debug)] +pub struct SchnorrAuthenticator { + pub signature: secp256k1::schnorr::Signature, +} + +impl BuiltinAuthenticator for SchnorrAuthenticator { + fn scheme(&self) -> BuiltinScheme { + BuiltinScheme::Schnorr + } + fn encode(&self) -> Vec { + bcs::to_bytes(self).expect("encode should success.") + } +} + +impl Serialize for SchnorrAuthenticator { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + #[derive(::serde::Serialize)] + #[serde(rename = "SchnorrAuthenticator")] + struct Value { + signature: secp256k1::schnorr::Signature, + } + Value { + signature: self.signature.into(), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for SchnorrAuthenticator { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + #[derive(::serde::Deserialize)] + #[serde(rename = "SchnorrAuthenticator")] + struct Value { + signature: secp256k1::schnorr::Signature, + } + let value = Value::deserialize(deserializer)?; + let signature = secp256k1::schnorr::Signature::try_from(value.signature) + .map_err(|e| serde::de::Error::custom(e.to_string()))?; + Ok(SchnorrAuthenticator { signature }) + } +} + +#[cfg(any(test, feature = "fuzzing"))] +impl Arbitrary for SchnorrAuthenticator { + type Parameters = (); + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + arb_schnorr_authenticator().boxed() + } + type Strategy = BoxedStrategy; +} + +#[cfg(any(test, feature = "fuzzing"))] +prop_compose! { + fn arb_schnorr_authenticator()( + sig in vec(any::(), 4..=4).prop_map(|v| (v)), + ) -> SchnorrAuthenticator { + SchnorrAuthenticator { + signature: secp256k1::schnorr::Signature::from_slice(&sig).unwrap(), + } + } +} + impl From for Authenticator where T: BuiltinAuthenticator, @@ -155,8 +219,9 @@ impl From for Authenticator { fn from(sign: Signature) -> Self { match sign.to_public_key().unwrap().scheme() { BuiltinScheme::Ed25519 => Authenticator::ed25519(sign), - BuiltinScheme::Secp256k1 => todo!(), + BuiltinScheme::ECDSA => todo!(), BuiltinScheme::MultiEd25519 => todo!(), + BuiltinScheme::Schnorr => todo!(), } } } @@ -178,9 +243,14 @@ impl Authenticator { Ed25519Authenticator { signature }.into() } - /// Create a single-signature secp256k1 authenticator - pub fn secp256k1(signature: ethers::core::types::Signature) -> Self { - Secp256k1Authenticator { signature }.into() + /// Create a single-signature ecdsa authenticator + pub fn ecdsa(signature: ethers::core::types::Signature) -> Self { + ECDSAAuthenticator { signature }.into() + } + + /// Create a single-signature schnorr authenticator + pub fn schnorr(signature: secp256k1::schnorr::Signature) -> Self { + SchnorrAuthenticator { signature }.into() } /// Create a custom authenticator @@ -216,9 +286,9 @@ mod tests { proptest! { #[test] - fn test_secp256k1_authenticator_serialize_deserialize(authenticator in any::()) { + fn test_secp256k1_authenticator_serialize_deserialize(authenticator in any::()) { let serialized = serde_json::to_string(&authenticator).unwrap(); - let deserialized: super:: Secp256k1Authenticator = serde_json::from_str(&serialized).unwrap(); + let deserialized: super:: ECDSAAuthenticator = serde_json::from_str(&serialized).unwrap(); assert_eq!(authenticator.signature, deserialized.signature); } @@ -228,5 +298,12 @@ mod tests { let deserialized: super::Ed25519Authenticator = serde_json::from_str(&serialized).unwrap(); assert_eq!(authenticator.signature, deserialized.signature); } + + #[test] + fn test_schnorr_authenticator_serialize_deserialize(authenticator in any::()) { + let serialized = serde_json::to_string(&authenticator).unwrap(); + let deserialized: super::SchnorrAuthenticator = serde_json::from_str(&serialized).unwrap(); + assert_eq!(authenticator.signature, deserialized.signature); + } } } diff --git a/crates/rooch-types/src/transaction/ethereum.rs b/crates/rooch-types/src/transaction/ethereum.rs index 10df804353..09ab7e7129 100644 --- a/crates/rooch-types/src/transaction/ethereum.rs +++ b/crates/rooch-types/src/transaction/ethereum.rs @@ -49,7 +49,7 @@ impl AbstractTransaction for EthereumTransaction { AuthenticatorInfo { //TODO should change the seqence_number to u256? seqence_number: self.0.nonce.as_u64(), - authenticator: Authenticator::secp256k1(ethers::core::types::Signature { + authenticator: Authenticator::ecdsa(ethers::core::types::Signature { r: self.0.r, s: self.0.s, v: self.0.v.as_u64(), diff --git a/crates/rooch-types/src/transaction/rooch.rs b/crates/rooch-types/src/transaction/rooch.rs index d8b5d9ca97..4d76c76cac 100644 --- a/crates/rooch-types/src/transaction/rooch.rs +++ b/crates/rooch-types/src/transaction/rooch.rs @@ -75,10 +75,10 @@ impl RoochTransaction { pub fn mock() -> RoochTransaction { use crate::address::RoochSupportedAddress; use crate::crypto::Signature; - use fastcrypto::ed25519::Ed25519KeyPair; - use fastcrypto::traits::KeyPair; + use ed25519_dalek::Keypair; use move_core_types::{identifier::Identifier, language_storage::ModuleId}; use moveos_types::move_types::FunctionId; + use rand::rngs::OsRng; let sender: RoochAddress = RoochAddress::random(); let sequence_number = 0; @@ -92,10 +92,10 @@ impl RoochTransaction { ); let transaction_data = RoochTransactionData::new(sender, sequence_number, payload); - let mut rng = rand::thread_rng(); - let ed25519_keypair: Ed25519KeyPair = Ed25519KeyPair::generate(&mut rng); + let mut csprng = OsRng {}; + let keypair: Keypair = Keypair::generate(&mut csprng); let auth = - Signature::new_hashed(transaction_data.hash().as_bytes(), &ed25519_keypair).into(); + Signature::new_hashed(transaction_data.hash().as_bytes(), &keypair.into()).into(); RoochTransaction::new(transaction_data, auth) } }