thesimplekid 2 years ago
parent
commit
231bc6f286
6 changed files with 156 additions and 3 deletions
  1. 3 1
      Cargo.toml
  2. 147 0
      src/dhke.rs
  3. 3 0
      src/error.rs
  4. 1 0
      src/lib.rs
  5. 1 1
      src/types.rs
  6. 1 1
      tests/integration_test.rs

+ 3 - 1
Cargo.toml

@@ -5,9 +5,11 @@ edition = "2021"
 
 [dependencies]
 bitcoin = { version = "0.30.0", features=["serde"] }
+bitcoin_hashes = "0.12.0"
+hex = "0.4.3"
 lightning-invoice = { version = "0.22.0", features=["serde"] }
 minreq = { version = "2.7.0", features = ["json-using-serde", "https"] }
-secp256k1 = "0.27.0"
+secp256k1 = { version = "0.27.0", features = ["rand-std", "bitcoin-hashes-std"] }
 serde = { version = "1.0.160", features = ["derive"]}
 thiserror = "1.0.40"
 url = "2.3.1"

+ 147 - 0
src/dhke.rs

@@ -1 +1,148 @@
 //! Diffie-Hellmann key exchange
+
+use bitcoin_hashes::sha256;
+// use bitcoin_hashes::Hash;
+use bitcoin_hashes::Hash;
+use secp256k1::rand::rngs::OsRng;
+use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
+
+use crate::error::Error;
+// use crate::types::MintKeys;
+// use crate::types::Promise;
+// use crate::types::Proof;
+
+/// Hash to Curve
+pub fn hash_to_curve(secret_message: &[u8]) -> Result<PublicKey, Error> {
+    let mut msg = secret_message.to_vec();
+    loop {
+        let hash = sha256::Hash::hash(&msg);
+        let mut pubkey_bytes = vec![0x02];
+        pubkey_bytes.extend_from_slice(&hash[..]);
+
+        match PublicKey::from_slice(&pubkey_bytes) {
+            Ok(pubkey) => return Ok(pubkey),
+            Err(_) => {
+                msg = hash.to_byte_array().to_vec();
+            }
+        }
+    }
+}
+
+/// Blind Message
+pub fn blind_message(
+    secret: &[u8],
+    blinding_factor: Option<SecretKey>,
+) -> Result<(PublicKey, SecretKey), Error> {
+    let y = hash_to_curve(secret)?;
+
+    let secp = Secp256k1::new();
+    let r: SecretKey = match blinding_factor {
+        Some(sec_key) => sec_key,
+        None => {
+            let (secret_key, _public_key) = secp.generate_keypair(&mut OsRng);
+            secret_key
+        }
+    };
+
+    let b = y.combine(&r.public_key(&secp))?;
+
+    Ok((b, r))
+}
+
+/// Unblind Message
+pub fn unblind_message(
+    blinded_key: PublicKey,
+    r: SecretKey,
+    a: PublicKey,
+) -> Result<PublicKey, Error> {
+    let secp = Secp256k1::new();
+    let a_neg = a.negate(&secp);
+    let blinded_key = blinded_key.combine(&a_neg).unwrap();
+    let unblinded_key =
+        blinded_key.mul_tweak(&secp, &Scalar::from_be_bytes(r.secret_bytes()).unwrap())?;
+    Ok(unblinded_key)
+}
+
+/*
+/// Construct Proof
+pub fn construct_proof(
+    promises: Vec<Promise>,
+    rs: Vec<SecretKey>,
+    secrets: Vec<String>,
+    keys: MintKeys,
+) -> Result<Vec<Proof>, Error> {
+    todo!()
+}
+*/
+
+#[cfg(test)]
+mod tests {
+    use hex::decode;
+    use std::str::FromStr;
+
+    use super::*;
+
+    #[test]
+    fn test_hash_to_curve() {
+        let secret = "0000000000000000000000000000000000000000000000000000000000000000";
+        let sec_hex = decode(secret).unwrap();
+
+        let y = hash_to_curve(&sec_hex).unwrap();
+        let expected_y = PublicKey::from_str(
+            "0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925",
+        )
+        .unwrap();
+        assert_eq!(y, expected_y);
+
+        let secret = "0000000000000000000000000000000000000000000000000000000000000001";
+        let sec_hex = decode(secret).unwrap();
+        let y = hash_to_curve(&sec_hex).unwrap();
+        let expected_y = PublicKey::from_str(
+            "02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5",
+        )
+        .unwrap();
+        assert_eq!(y, expected_y);
+    }
+
+    #[test]
+    fn test_blind_message() {
+        let message = "test_message";
+        let blinding_factor = "0000000000000000000000000000000000000000000000000000000000000001";
+        let sec = SecretKey::from_str(blinding_factor).unwrap();
+
+        let (b, r) = blind_message(message.as_bytes(), Some(sec)).unwrap();
+
+        assert_eq!(
+            b.to_string(),
+            "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_string()
+        );
+
+        assert_eq!(r, sec);
+    }
+
+    #[test]
+    fn test_unblind_message() {
+        let blinded_key = PublicKey::from_str(
+            "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
+        )
+        .unwrap();
+
+        let r =
+            SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
+                .unwrap();
+        let a = PublicKey::from_str(
+            "020000000000000000000000000000000000000000000000000000000000000001",
+        )
+        .unwrap();
+
+        let unblinded = unblind_message(blinded_key, r, a).unwrap();
+
+        assert_eq!(
+            PublicKey::from_str(
+                "03c724d7e6a5443b39ac8acf11f40420adc4f99a02e7cc1b57703d9391f6d129cd"
+            )
+            .unwrap(),
+            unblinded
+        );
+    }
+}

+ 3 - 0
src/error.rs

@@ -6,4 +6,7 @@ pub enum Error {
     /// Parse Url Error
     #[error("minreq error: {0}")]
     UrlParseError(#[from] url::ParseError),
+    /// Secp245k1
+    #[error("secp256k1 error: {0}")]
+    Secpk256k1Error(#[from] secp256k1::Error),
 }

+ 1 - 0
src/lib.rs

@@ -1,3 +1,4 @@
 pub mod cashu_mint;
+pub mod dhke;
 pub mod error;
 pub mod types;

+ 1 - 1
src/types.rs

@@ -17,7 +17,7 @@ pub struct BlindedMessage {
     pub b: String,
 }
 
-/// Promise (BlindedMessage) [NIP-00]
+/// Promise (BlindedSignature) [NIP-00]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Promise {
     pub id: String,

+ 1 - 1
tests/integration_test.rs

@@ -52,7 +52,7 @@ async fn test_check_fees() {
 async fn test_get_mint_info() {
     let url = Url::from_str(MINTURL).unwrap();
     let mint = CashuMint::new(url);
-    let mint_info = mint.get_info().await.unwrap();
+    let _mint_info = mint.get_info().await.unwrap();
 
     // println!("{:?}", mint_info);
 }