From 4535599d8b30cc0c5b7304b3383c9afa96f5ff53 Mon Sep 17 00:00:00 2001 From: ngutech21 Date: Thu, 26 Oct 2023 07:52:46 +0200 Subject: [PATCH] chore: merge crypto-module into keyset --- moksha-core/src/crypto.rs | 167 ------------------------------------ moksha-core/src/dhke.rs | 59 +++++++------ moksha-core/src/keyset.rs | 173 ++++++++++++++++++++++++++++++++++++-- moksha-core/src/lib.rs | 1 - moksha-mint/src/mint.rs | 5 +- 5 files changed, 198 insertions(+), 207 deletions(-) delete mode 100644 moksha-core/src/crypto.rs diff --git a/moksha-core/src/crypto.rs b/moksha-core/src/crypto.rs deleted file mode 100644 index 367749a6..00000000 --- a/moksha-core/src/crypto.rs +++ /dev/null @@ -1,167 +0,0 @@ -use base64::{engine::general_purpose, Engine as _}; -use bitcoin_hashes::{sha256, Hash}; - -use itertools::Itertools; -use rand::RngCore; -use secp256k1::{PublicKey, Secp256k1, SecretKey}; -use std::collections::HashMap; - -use crate::error::MokshaCoreError; - -const MAX_ORDER: u64 = 64; - -pub fn generate_hash() -> String { - let mut rng = rand::thread_rng(); - let mut random = [0u8; 32]; - rng.fill_bytes(&mut random); - sha256::Hash::hash(&random).to_string() -} - -/// Derives a set of secret keys from a master key using a given derivation path. -/// -/// # Arguments -/// -/// * `master_key` - A string slice that holds the master key. -/// * `derivation_path` - A string slice that holds the derivation path. -/// -/// # Returns -/// -/// A HashMap containing the derived secret keys, where the key is a u64 value and the value is a SecretKey. -pub fn derive_keys(master_key: &str, derivation_path: &str) -> HashMap { - let mut keys = HashMap::new(); - for i in 0..MAX_ORDER { - let hash = sha256::Hash::hash(format!("{master_key}{derivation_path}{i}").as_bytes()); - let key = SecretKey::from_slice(hash.as_byte_array()).unwrap(); - keys.insert(2u64.pow(i as u32), key); - } - keys -} - -/// Derives public keys from a given set of secret keys. -/// -/// # Arguments -/// -/// * `keys` - A HashMap containing the secret keys to derive public keys from. -/// -/// # Returns -/// -/// A HashMap containing the derived public keys. -pub fn derive_pubkeys(keys: &HashMap) -> HashMap { - let secp = Secp256k1::new(); - keys.keys() - .map(|amt| (*amt, keys[amt].public_key(&secp))) - .collect() -} - -/// Derives a keyset ID from a HashMap of public keys. -/// -/// # Arguments -/// -/// * `keys` - A HashMap of public keys. -/// -/// # Returns -/// -/// A string representing the derived keyset ID. -pub fn derive_keyset_id(keys: &HashMap) -> String { - let pubkeys_concat = keys - .iter() - .sorted_by(|(amt_a, _), (amt_b, _)| amt_a.cmp(amt_b)) - .map(|(_, pubkey)| pubkey) - .join(""); - let hashed_pubkeys = sha256::Hash::hash(pubkeys_concat.as_bytes()).to_byte_array(); - general_purpose::STANDARD.encode(hashed_pubkeys)[..12].to_string() -} - -/// Derives a public key from a given seed. -/// -/// # Arguments -/// -/// * `seed` - A string slice representing the seed to derive the public key from. -/// -/// # Returns -/// -/// Returns a `Result` containing the derived `PublicKey` or a `MokshaCoreError` if an error occurs. -pub fn derive_pubkey(seed: &str) -> Result { - let hash = sha256::Hash::hash(seed.as_bytes()); - let key = SecretKey::from_slice(hash.as_byte_array())?; - let secp = Secp256k1::new(); - Ok(key.public_key(&secp)) -} - -#[cfg(test)] -mod tests { - use super::generate_hash; - use crate::crypto::derive_pubkey; - use std::collections::HashMap; - - fn public_key_from_hex(hex: &str) -> secp256k1::PublicKey { - use hex::FromHex; - let input_vec: Vec = Vec::from_hex(hex).expect("Invalid Hex String"); - secp256k1::PublicKey::from_slice(&input_vec).expect("Invalid Public Key") - } - - #[test] - fn test_generate_hash() { - let hash = generate_hash(); - assert_eq!(hash.len(), 64); - } - - #[test] - fn test_derive_pubkey() -> anyhow::Result<()> { - let result = derive_pubkey("supersecretprivatekey")?; - assert_eq!( - "03a2118b421e6b47f0656b97bb7eeea43c41096adbc0d0e511ff70de7d94dbd990", - result.to_string() - ); - Ok(()) - } - - #[test] - fn test_derive_keys_master() -> anyhow::Result<()> { - let keys = super::derive_keys("master", "0/0/0/0"); - assert!(keys.len() == 64); - - let pub_keys = super::derive_pubkeys(&keys); - let id = super::derive_keyset_id(&pub_keys); - assert_eq!("JHV8eUnoAln/", id); - assert!(id.len() == 12); - Ok(()) - } - - // uses values from cashu test_mint.py - #[test] - fn test_derive_keys_cashu_py() -> anyhow::Result<()> { - let keys = super::derive_keys("TEST_PRIVATE_KEY", "0/0/0/0"); - assert!(keys.len() == 64); - - let pub_keys = super::derive_pubkeys(&keys); - let id = super::derive_keyset_id(&pub_keys); - assert_eq!("1cCNIAZ2X/w1", id); - assert!(id.len() == 12); - Ok(()) - } - - #[test] - fn test_derive_keyset_id() -> anyhow::Result<()> { - let mut pubs = HashMap::new(); - pubs.insert( - 1, - public_key_from_hex( - "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2", - ), - ); - - pubs.insert( - 2, - public_key_from_hex( - "020000000000000000000000000000000000000000000000000000000000000001", - ), - ); - - let keyset_id = super::derive_keyset_id(&pubs); - - assert!(keyset_id.len() == 12); - assert_eq!(keyset_id, "cNbjM0O6V/Kl"); - Ok(()) - } -} diff --git a/moksha-core/src/dhke.rs b/moksha-core/src/dhke.rs index 1ec42847..37b2fe59 100644 --- a/moksha-core/src/dhke.rs +++ b/moksha-core/src/dhke.rs @@ -1,36 +1,35 @@ +//! Implementation of `` +//! +//! Bob (Mint): +//! A = a*G +//! return A +//! +//! Alice (Client): +//! Y = hash_to_curve(secret_message) +//! r = random blinding factor +//! B'= Y + r*G +//! return B' +//! +//! Bob: +//! C' = a*B' +//! (= a*Y + a*r*G) +//! return C' +//! +//! Alice: +//! C = C' - r*A +//! (= C' - a*r*G) +//! (= a*Y) +//! return C, secret_message +//! +//! Bob: +//! Y = hash_to_curve(secret_message) +//! C == a*Y +//! If true, C must have originated from Bob +//! +use crate::error::MokshaCoreError; use bitcoin_hashes::{sha256, Hash}; use secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey}; -use crate::error::MokshaCoreError; - -/// Implementation of `` -/// -/// Bob (Mint): -/// A = a*G -/// return A -/// -/// Alice (Client): -/// Y = hash_to_curve(secret_message) -/// r = random blinding factor -/// B'= Y + r*G -/// return B' -/// -/// Bob: -/// C' = a*B' -/// (= a*Y + a*r*G) -/// return C' -/// -/// Alice: -/// C = C' - r*A -/// (= C' - a*r*G) -/// (= a*Y) -/// return C, secret_message -/// -/// Bob: -/// Y = hash_to_curve(secret_message) -/// C == a*Y -/// If true, C must have originated from Bob - #[derive(Clone)] pub struct Dhke { secp: Secp256k1, diff --git a/moksha-core/src/keyset.rs b/moksha-core/src/keyset.rs index 98c412db..24e22dec 100644 --- a/moksha-core/src/keyset.rs +++ b/moksha-core/src/keyset.rs @@ -1,11 +1,23 @@ -use secp256k1::{PublicKey, SecretKey}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::{ - crypto::{self, derive_keys, derive_keyset_id, derive_pubkey, derive_pubkeys}, - error::MokshaCoreError, -}; +use base64::{engine::general_purpose, Engine as _}; +use bitcoin_hashes::{sha256, Hash}; + +use itertools::Itertools; +use rand::RngCore; +use secp256k1::{PublicKey, Secp256k1, SecretKey}; + +use crate::error::MokshaCoreError; + +const MAX_ORDER: u64 = 64; + +pub fn generate_hash() -> String { + let mut rng = rand::thread_rng(); + let mut random = [0u8; 32]; + rng.fill_bytes(&mut random); + sha256::Hash::hash(&random).to_string() +} #[derive(Debug, Clone)] pub struct MintKeyset { @@ -42,7 +54,7 @@ impl Keysets { &self, mint_keys: &HashMap, ) -> Result { - let computed_id = crypto::derive_keyset_id(mint_keys); + let computed_id = derive_keyset_id(mint_keys); if self.keysets.contains(&computed_id) { Ok(computed_id) } else { @@ -50,3 +62,152 @@ impl Keysets { } } } + +/// Derives a set of secret keys from a master key using a given derivation path. +/// +/// # Arguments +/// +/// * `master_key` - A string slice that holds the master key. +/// * `derivation_path` - A string slice that holds the derivation path. +/// +/// # Returns +/// +/// A HashMap containing the derived secret keys, where the key is a u64 value and the value is a SecretKey. +pub fn derive_keys(master_key: &str, derivation_path: &str) -> HashMap { + let mut keys = HashMap::new(); + for i in 0..MAX_ORDER { + let hash = sha256::Hash::hash(format!("{master_key}{derivation_path}{i}").as_bytes()); + let key = SecretKey::from_slice(hash.as_byte_array()).unwrap(); + keys.insert(2u64.pow(i as u32), key); + } + keys +} + +/// Derives public keys from a given set of secret keys. +/// +/// # Arguments +/// +/// * `keys` - A HashMap containing the secret keys to derive public keys from. +/// +/// # Returns +/// +/// A HashMap containing the derived public keys. +pub fn derive_pubkeys(keys: &HashMap) -> HashMap { + let secp = Secp256k1::new(); + keys.keys() + .map(|amt| (*amt, keys[amt].public_key(&secp))) + .collect() +} + +/// Derives a keyset ID from a HashMap of public keys. +/// +/// # Arguments +/// +/// * `keys` - A HashMap of public keys. +/// +/// # Returns +/// +/// A string representing the derived keyset ID. +pub fn derive_keyset_id(keys: &HashMap) -> String { + let pubkeys_concat = keys + .iter() + .sorted_by(|(amt_a, _), (amt_b, _)| amt_a.cmp(amt_b)) + .map(|(_, pubkey)| pubkey) + .join(""); + let hashed_pubkeys = sha256::Hash::hash(pubkeys_concat.as_bytes()).to_byte_array(); + general_purpose::STANDARD.encode(hashed_pubkeys)[..12].to_string() +} + +/// Derives a public key from a given seed. +/// +/// # Arguments +/// +/// * `seed` - A string slice representing the seed to derive the public key from. +/// +/// # Returns +/// +/// Returns a `Result` containing the derived `PublicKey` or a `MokshaCoreError` if an error occurs. +pub fn derive_pubkey(seed: &str) -> Result { + let hash = sha256::Hash::hash(seed.as_bytes()); + let key = SecretKey::from_slice(hash.as_byte_array())?; + let secp = Secp256k1::new(); + Ok(key.public_key(&secp)) +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use crate::keyset::{derive_pubkey, generate_hash}; + + fn public_key_from_hex(hex: &str) -> secp256k1::PublicKey { + use hex::FromHex; + let input_vec: Vec = Vec::from_hex(hex).expect("Invalid Hex String"); + secp256k1::PublicKey::from_slice(&input_vec).expect("Invalid Public Key") + } + + #[test] + fn test_generate_hash() { + let hash = generate_hash(); + assert_eq!(hash.len(), 64); + } + + #[test] + fn test_derive_pubkey() -> anyhow::Result<()> { + let result = derive_pubkey("supersecretprivatekey")?; + assert_eq!( + "03a2118b421e6b47f0656b97bb7eeea43c41096adbc0d0e511ff70de7d94dbd990", + result.to_string() + ); + Ok(()) + } + + #[test] + fn test_derive_keys_master() -> anyhow::Result<()> { + let keys = super::derive_keys("master", "0/0/0/0"); + assert!(keys.len() == 64); + + let pub_keys = super::derive_pubkeys(&keys); + let id = super::derive_keyset_id(&pub_keys); + assert_eq!("JHV8eUnoAln/", id); + assert!(id.len() == 12); + Ok(()) + } + + // uses values from cashu test_mint.py + #[test] + fn test_derive_keys_cashu_py() -> anyhow::Result<()> { + let keys = super::derive_keys("TEST_PRIVATE_KEY", "0/0/0/0"); + assert!(keys.len() == 64); + + let pub_keys = super::derive_pubkeys(&keys); + let id = super::derive_keyset_id(&pub_keys); + assert_eq!("1cCNIAZ2X/w1", id); + assert!(id.len() == 12); + Ok(()) + } + + #[test] + fn test_derive_keyset_id() -> anyhow::Result<()> { + let mut pubs = HashMap::new(); + pubs.insert( + 1, + public_key_from_hex( + "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2", + ), + ); + + pubs.insert( + 2, + public_key_from_hex( + "020000000000000000000000000000000000000000000000000000000000000001", + ), + ); + + let keyset_id = super::derive_keyset_id(&pubs); + + assert!(keyset_id.len() == 12); + assert_eq!(keyset_id, "cNbjM0O6V/Kl"); + Ok(()) + } +} diff --git a/moksha-core/src/lib.rs b/moksha-core/src/lib.rs index ba2f2f4c..94ea403f 100644 --- a/moksha-core/src/lib.rs +++ b/moksha-core/src/lib.rs @@ -1,6 +1,5 @@ pub mod amount; pub mod blind; -pub mod crypto; pub mod dhke; pub mod error; pub mod fixture; diff --git a/moksha-mint/src/mint.rs b/moksha-mint/src/mint.rs index 2adacce8..192c70e5 100644 --- a/moksha-mint/src/mint.rs +++ b/moksha-mint/src/mint.rs @@ -2,9 +2,8 @@ use std::{collections::HashSet, sync::Arc}; use moksha_core::{ blind::{BlindedMessage, BlindedSignature, TotalAmount}, - crypto, dhke::Dhke, - keyset::MintKeyset, + keyset::{generate_hash, MintKeyset}, primitives::PostSplitResponse, proof::Proofs, }; @@ -105,7 +104,7 @@ impl Mint { pub async fn create_invoice(&self, amount: u64) -> Result<(String, String), MokshaMintError> { let pr = self.lightning.create_invoice(amount).await?.payment_request; - let key = crypto::generate_hash(); + let key = generate_hash(); self.db .add_pending_invoice(key.clone(), Invoice::new(amount, pr.clone()))?; Ok((pr, key))