Ver Fonte

refactor: hash to curve domain separated

thesimplekid há 1 ano atrás
pai
commit
72592572c5

+ 7 - 5
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<Option<Proof>, 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

+ 5 - 5
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()?;

+ 4 - 2
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

+ 29 - 24
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<k256::PublicKey, Error> {
+    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::<Vec<u8>>(),
         ) {
-            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<SecretKey>,
     ) -> 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();

+ 6 - 0
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),
     }
 }