#[cfg(test)]
#[path = "crypto_test.rs"]
#[allow(clippy::explicit_auto_deref)]
mod crypto_test;
use serde::{Deserialize, Serialize};
use starknet_types_core::felt::Felt;
use starknet_types_core::hash::{Pedersen, Poseidon, StarkHash as CoreStarkHash};
use thiserror::Error;
use crate::hash::StarkHash;
#[derive(thiserror::Error, Clone, Debug)]
pub enum CryptoError {
#[error("Invalid public key {0:#x}.")]
InvalidPublicKey(PublicKey),
#[error("Invalid message hash {0:#x}.")]
InvalidMessageHash(Felt),
#[error("Invalid r {0}.")]
InvalidR(Felt),
#[error("Invalid s {0}.")]
InvalidS(Felt),
}
#[derive(
Debug, Default, derive_more::Deref, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize,
)]
pub struct PublicKey(pub Felt);
impl std::fmt::LowerHex for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::LowerHex::fmt(&self.0, f)
}
}
#[derive(
Debug, Default, derive_more::Deref, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize,
)]
pub struct PrivateKey(pub Felt);
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub struct Signature {
pub r: Felt,
pub s: Felt,
}
pub fn verify_message_hash_signature(
message_hash: &Felt,
signature: &Signature,
public_key: &PublicKey,
) -> Result<bool, CryptoError> {
starknet_crypto::verify(&public_key.0, message_hash, &signature.r, &signature.s).map_err(
|err| match err {
starknet_crypto::VerifyError::InvalidPublicKey => {
CryptoError::InvalidPublicKey(*public_key)
}
starknet_crypto::VerifyError::InvalidMessageHash => {
CryptoError::InvalidMessageHash(*message_hash)
}
starknet_crypto::VerifyError::InvalidR => CryptoError::InvalidR(signature.r),
starknet_crypto::VerifyError::InvalidS => CryptoError::InvalidS(signature.s),
},
)
}
pub(crate) struct HashChain {
elements: Vec<Felt>,
}
impl HashChain {
pub fn new() -> HashChain {
HashChain { elements: Vec::new() }
}
pub fn chain(mut self, felt: &Felt) -> Self {
self.elements.push(*felt);
self
}
pub fn chain_if_fn<F: Fn() -> Option<Felt>>(self, f: F) -> Self {
match f() {
Some(felt) => self.chain(&felt),
None => self,
}
}
pub fn chain_iter<'a>(self, felts: impl Iterator<Item = &'a Felt>) -> Self {
felts.fold(self, |current, felt| current.chain(felt))
}
pub fn chain_size_and_elements(self, felts: &[Felt]) -> Self {
self.chain(&felts.len().into()).chain_iter(felts.iter())
}
pub fn extend(mut self, chain: HashChain) -> Self {
self.elements.extend(chain.elements);
self
}
pub fn get_pedersen_hash(&self) -> StarkHash {
Pedersen::hash_array(self.elements.as_slice())
}
pub fn get_poseidon_hash(&self) -> StarkHash {
Poseidon::hash_array(self.elements.as_slice())
}
}
#[derive(
Debug, Default, derive_more::Deref, Clone, Eq, PartialEq, Hash, Deserialize, Serialize,
)]
pub struct Message(pub Vec<Felt>);
#[derive(
Clone, Debug, Default, derive_more::Deref, Eq, PartialEq, Hash, Serialize, Deserialize,
)]
pub struct RawSignature(pub Vec<Felt>);
impl From<starknet_crypto::Signature> for RawSignature {
fn from(signature: starknet_crypto::Signature) -> Self {
Self(vec![signature.r, signature.s])
}
}
impl From<starknet_crypto::ExtendedSignature> for RawSignature {
fn from(signature: starknet_crypto::ExtendedSignature) -> Self {
Self(vec![signature.r, signature.s])
}
}
#[derive(Clone, Debug, Error, Serialize, Deserialize, Eq, PartialEq)]
pub enum SignatureConversionError {
#[error("expected a 2-element signature, but got length {0}")]
InvalidLength(usize),
}
impl TryFrom<RawSignature> for starknet_crypto::Signature {
type Error = SignatureConversionError;
fn try_from(signature: RawSignature) -> Result<Self, Self::Error> {
let signature_length = signature.len();
let [r, s]: [Felt; 2] = signature
.0
.try_into()
.map_err(|_| SignatureConversionError::InvalidLength(signature_length))?;
Ok(starknet_crypto::Signature { r, s })
}
}