diff --git a/crates/cashu-sdk/src/mint/localstore/memory.rs b/crates/cashu-sdk/src/mint/localstore/memory.rs index 0ffdff67..6c5bfb7e 100644 --- a/crates/cashu-sdk/src/mint/localstore/memory.rs +++ b/crates/cashu-sdk/src/mint/localstore/memory.rs @@ -49,6 +49,7 @@ impl MemoryLocalStore { .map(|p| { ( hash_to_curve(&p.secret.to_bytes().unwrap()) + .unwrap() .to_sec1_bytes() .to_vec(), p, @@ -62,6 +63,7 @@ impl MemoryLocalStore { .map(|p| { ( hash_to_curve(&p.secret.to_bytes().unwrap()) + .unwrap() .to_sec1_bytes() .to_vec(), p, @@ -154,7 +156,7 @@ impl LocalStore for MemoryLocalStore { } async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error> { - let secret_point = hash_to_curve(&proof.secret.to_bytes()?); + let secret_point = hash_to_curve(&proof.secret.to_bytes()?)?; self.spent_proofs .lock() .await @@ -167,7 +169,7 @@ impl LocalStore for MemoryLocalStore { .spent_proofs .lock() .await - .get(&hash_to_curve(&secret.to_bytes()?).to_sec1_bytes().to_vec()) + .get(&hash_to_curve(&secret.to_bytes()?)?.to_sec1_bytes().to_vec()) .cloned()) } @@ -185,7 +187,7 @@ impl LocalStore for MemoryLocalStore { async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error> { self.pending_proofs.lock().await.insert( - hash_to_curve(&proof.secret.to_bytes()?) + hash_to_curve(&proof.secret.to_bytes()?)? .to_sec1_bytes() .to_vec(), proof, @@ -194,7 +196,7 @@ impl LocalStore for MemoryLocalStore { } async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result, Error> { - let secret_point = hash_to_curve(&secret.to_bytes()?); + let secret_point = hash_to_curve(&secret.to_bytes()?)?; Ok(self .pending_proofs .lock() @@ -216,7 +218,7 @@ impl LocalStore for MemoryLocalStore { } async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error> { - let secret_point = hash_to_curve(&secret.to_bytes()?); + let secret_point = hash_to_curve(&secret.to_bytes()?)?; self.pending_proofs .lock() .await diff --git a/crates/cashu-sdk/src/mint/localstore/redb_store.rs b/crates/cashu-sdk/src/mint/localstore/redb_store.rs index 69507fb8..918f00f0 100644 --- a/crates/cashu-sdk/src/mint/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/mint/localstore/redb_store.rs @@ -277,7 +277,7 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(SPENT_PROOFS_TABLE)?; table.insert( - hash_to_curve(&proof.secret.to_bytes()?) + hash_to_curve(&proof.secret.to_bytes()?)? .to_sec1_bytes() .as_ref(), serde_json::to_string(&proof)?.as_str(), @@ -308,7 +308,7 @@ impl LocalStore for RedbLocalStore { let read_txn = db.begin_read()?; let table = read_txn.open_table(SPENT_PROOFS_TABLE)?; - let secret_hash = hash_to_curve(&secret.to_bytes()?); + let secret_hash = hash_to_curve(&secret.to_bytes()?)?; let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?; @@ -325,7 +325,7 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?; table.insert( - hash_to_curve(&proof.secret.to_bytes()?) + hash_to_curve(&proof.secret.to_bytes()?)? .to_sec1_bytes() .as_ref(), serde_json::to_string(&proof)?.as_str(), @@ -354,7 +354,7 @@ impl LocalStore for RedbLocalStore { let read_txn = db.begin_read()?; let table = read_txn.open_table(PENDING_PROOFS_TABLE)?; - let secret_hash = hash_to_curve(&secret.to_bytes()?); + let secret_hash = hash_to_curve(&secret.to_bytes()?)?; let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?; @@ -368,7 +368,7 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?; - let secret_hash = hash_to_curve(&secret.to_bytes()?); + let secret_hash = hash_to_curve(&secret.to_bytes()?)?; table.remove(secret_hash.to_sec1_bytes().as_ref())?; } write_txn.commit()?; diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index bfe4b07d..a4632a08 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -358,7 +358,8 @@ impl Mint { .inputs .iter() .flat_map(|p| p.secret.to_bytes()) - .map(|p| hash_to_curve(&p).to_sec1_bytes().to_vec()) + .flat_map(|p| hash_to_curve(&p)) + .map(|p| p.to_sec1_bytes().to_vec()) .collect(); // Check that there are no duplicate proofs in request @@ -561,7 +562,8 @@ impl Mint { .inputs .iter() .flat_map(|p| p.secret.to_bytes()) - .map(|p| hash_to_curve(&p).to_sec1_bytes().to_vec()) + .flat_map(|p| hash_to_curve(&p)) + .map(|p| p.to_sec1_bytes().to_vec()) .collect(); // Ensure proofs are unique and not being double spent diff --git a/crates/cashu/src/dhke.rs b/crates/cashu/src/dhke.rs index 4100010e..1631cf9b 100644 --- a/crates/cashu/src/dhke.rs +++ b/crates/cashu/src/dhke.rs @@ -6,14 +6,22 @@ pub use mint::{sign_message, verify_message}; #[cfg(feature = "wallet")] pub use wallet::{blind_message, construct_proofs, unblind_message}; +use crate::error::Error; + const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_"; -pub fn hash_to_curve(message: &[u8]) -> k256::PublicKey { - let mut msg_to_hash = [DOMAIN_SEPARATOR, message].concat(); +pub fn hash_to_curve(message: &[u8]) -> Result { + let msg_to_hash = [DOMAIN_SEPARATOR, message].concat(); + + let msg_hash = sha256::Hash::hash(&msg_to_hash).to_byte_array(); let mut counter = 0; - loop { - let hash = sha256::Hash::hash(&[msg_to_hash, counter.to_string().into_bytes()].concat()); + while counter < 2_u32.pow(16) { + let mut bytes_to_hash = Vec::with_capacity(36); + bytes_to_hash.extend_from_slice(&msg_hash); + bytes_to_hash.extend_from_slice(&counter.to_le_bytes()); + + let hash = sha256::Hash::hash(&bytes_to_hash); match k256::PublicKey::from_sec1_bytes( &[0x02u8] .iter() @@ -21,13 +29,14 @@ pub fn hash_to_curve(message: &[u8]) -> k256::PublicKey { .cloned() .collect::>(), ) { - Ok(pubkey) => return pubkey, + Ok(pubkey) => return Ok(pubkey), Err(_) => { counter += 1; - msg_to_hash = hash.to_byte_array().to_vec(); } } } + + Err(Error::NoValidPoint) } #[cfg(feature = "wallet")] @@ -45,7 +54,7 @@ mod wallet { secret: &[u8], blinding_factor: Option, ) -> Result<(PublicKey, SecretKey), error::wallet::Error> { - let y = hash_to_curve(secret); + let y = hash_to_curve(secret)?; let r: SecretKey = match blinding_factor { Some(sec_key) => sec_key, @@ -136,7 +145,7 @@ mod mint { msg: &Secret, ) -> Result<(), error::mint::Error> { // Y - let y = hash_to_curve(&msg.to_bytes()?); + let y = hash_to_curve(&msg.to_bytes()?)?; if unblinded_message == k256::PublicKey::try_from(*y.as_affine() * Scalar::from(a.as_scalar_primitive()))? @@ -167,9 +176,9 @@ mod tests { let secret = "0000000000000000000000000000000000000000000000000000000000000000"; let sec_hex = decode(secret).unwrap(); - let y = hash_to_curve(&sec_hex); + let y = hash_to_curve(&sec_hex).unwrap(); let expected_y = k256::PublicKey::from_sec1_bytes( - &hex::decode("02c03ade6f7345a213ea11acde3fda8514f2b7d836a32dfac38f9596c07258f9a9") + &hex::decode("024cce997d3b518f739663b757deaec95bcd9473c30a14ac2fd04023a739d1a725") .unwrap(), ) .unwrap(); @@ -178,20 +187,20 @@ mod tests { let secret = "0000000000000000000000000000000000000000000000000000000000000001"; let sec_hex = decode(secret).unwrap(); - let y = hash_to_curve(&sec_hex); + let y = hash_to_curve(&sec_hex).unwrap(); let expected_y = k256::PublicKey::from_sec1_bytes( - &hex::decode("02a5525df57a880f880f28903f32b421df848b3dc1d2cf0bf3d718d7bd772c2df9") + &hex::decode("022e7158e11c9506f1aa4248bf531298daa7febd6194f003edcd9b93ade6253acf") .unwrap(), ) .unwrap(); println!("{}", hex::encode(y.to_sec1_bytes())); assert_eq!(y, expected_y); - + /* // Note that this message will take a few iterations of the loop before finding // a valid point let secret = "0000000000000000000000000000000000000000000000000000000000000002"; let sec_hex = decode(secret).unwrap(); - let y = hash_to_curve(&sec_hex); + let y = hash_to_curve(&sec_hex).unwrap(); let expected_y = k256::PublicKey::from_sec1_bytes( &hex::decode("0277834447374a42908b34940dc2affc5f0fc4bbddb2e3b209c5c0b18438abf764") .unwrap(), @@ -199,6 +208,7 @@ mod tests { .unwrap(); println!("{}", hex::encode(y.to_sec1_bytes())); assert_eq!(y, expected_y); + */ } #[test] @@ -209,8 +219,6 @@ mod tests { ) .unwrap(); - println!("{}", sec.to_hex()); - let (b, r) = blind_message(&hex::decode(message).unwrap(), Some(sec.clone().into())).unwrap(); @@ -221,7 +229,7 @@ mod tests { PublicKey::from( k256::PublicKey::from_sec1_bytes( &hex::decode( - "03039eb7fb76a0db827d7b978a508e3319db03cde6ca8744ef32d0b4e4f455f5dc" + "033b1a9737a40cc3fd9b6af4b723632b76a67a36782596304612a6c2bfb5197e6d" ) .unwrap() ) @@ -236,8 +244,6 @@ mod tests { ) .unwrap(); - println!("{}", sec.to_hex()); - let (b, r) = blind_message(&hex::decode(message).unwrap(), Some(sec.clone().into())).unwrap(); @@ -248,7 +254,7 @@ mod tests { PublicKey::from( k256::PublicKey::from_sec1_bytes( &hex::decode( - "036498fe9280b09e071c6f838a185d9f0caa1bf84fe9b5cafe595f1898c8c23f9e" + "029bdf2d716ee366eddf599ba252786c1033f47e230248a4612a5670ab931f1763" ) .unwrap() ) @@ -316,7 +322,7 @@ mod tests { signed, k256::PublicKey::from_sec1_bytes( &hex::decode( - "03342e7f3dd691e1e82ede680f51a826991fb9b261a051860dd493a713ae61a84b" + "025cc16fe33b953e2ace39653efb3e7a7049711ae1d8a2f7a9108753f1cdea742b" ) .unwrap() ) @@ -332,12 +338,11 @@ mod tests { // C_ let signed = sign_message(bob_sec.into(), blinded_message.into()).unwrap(); - println!("{}", hex::encode(signed.to_sec1_bytes())); assert_eq!( signed, k256::PublicKey::from_sec1_bytes( &hex::decode( - "039387dbf13b55919606ba42b5302bd97895bf0ee6bfcff5c0fe8efe5eb2ce50da" + "027726f0e5757b4202a27198369a3477a17bc275b7529da518fc7cb4a1d927cc0d" ) .unwrap() ) @@ -359,7 +364,7 @@ mod tests { let x = Secret::new(); // Y - let y = hash_to_curve(&x.to_bytes().unwrap()); + let y = hash_to_curve(&x.to_bytes().unwrap()).unwrap(); // B_ let blinded = blind_message(&y.to_sec1_bytes(), None).unwrap(); diff --git a/crates/cashu/src/error.rs b/crates/cashu/src/error.rs index 09f7c519..50616fd6 100644 --- a/crates/cashu/src/error.rs +++ b/crates/cashu/src/error.rs @@ -34,6 +34,8 @@ pub enum Error { InvoiceAmountUndefined, #[error("Proof missing required field")] MissingProofField, + #[error("No valid point found")] + NoValidPoint, /// Custom error #[error("`{0}`")] CustomError(String), @@ -86,6 +88,8 @@ pub mod wallet { UrlParse, #[error("`{0}`")] Secret(#[from] crate::secret::Error), + #[error("`{0}`")] + Cashu(#[from] super::Error), /// Custom Error message #[error("`{0}`")] CustomError(String), @@ -129,6 +133,8 @@ pub mod mint { #[error("`{0}`")] Secret(#[from] crate::secret::Error), #[error("`{0}`")] + Cashu(#[from] super::Error), + #[error("`{0}`")] CustomError(String), } }