diff --git a/Cargo.toml b/Cargo.toml index 0d29db4..32166be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ ascii-armor = "0.7.1" baid64 = "0.2.2" base64 = "0.22.1" secp256k1 = { version = "0.29.0", features = ["rand", "global-context", "rand-std"] } -ec25519 = "0.1.0" +ec25519 = { version = "0.1.0", features = ["blind-keys"] } rand = "0.8.5" chrono = "0.4.38" clap = { version = "4.5.4", features = ["derive"], optional = true } diff --git a/src/encrypt.rs b/src/encrypt.rs index 1de7dec..bfba19f 100644 --- a/src/encrypt.rs +++ b/src/encrypt.rs @@ -24,9 +24,10 @@ use std::str::FromStr; use aes_gcm::aead::{Aead, Nonce, OsRng}; use aes_gcm::{AeadCore, Aes256Gcm, KeyInit}; use amplify::confinement::{Confined, SmallOrdMap, U64 as U64MAX}; -use amplify::{Bytes32, Wrapper}; +use amplify::hex::ToHex; +use amplify::Bytes32; use armor::{ArmorHeader, ArmorParseError, AsciiArmor}; -use ec25519::edwards25519; +use ec25519::{edwards25519, KeyPair, Seed}; use rand::random; use sha2::{Digest, Sha256}; use strict_encoding::{StrictDeserialize, StrictSerialize}; @@ -77,7 +78,7 @@ impl SymmetricKey { #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_SSI)] pub struct Encrypted { - pub keys: SmallOrdMap, + pub keys: SmallOrdMap, pub nonce: [u8; 12], pub data: Confined, 0, U64MAX>, } @@ -120,11 +121,10 @@ impl Encrypted { let key = SymmetricKey::new(); let mut keys = bmap![]; for pk in receivers { - keys.insert( - pk, - pk.encrypt_key(&key) - .map_err(|_| EncryptionError::InvalidPubkey(pk))?, - ); + let (msg, c1) = pk + .encrypt_key(&key) + .map_err(|_| EncryptionError::InvalidPubkey(pk))?; + keys.insert(pk, (msg, Bytes32::from_slice_unsafe(c1.as_slice()))); } let (nonce, msg) = encrypt(source, key); Ok(Self { @@ -134,53 +134,83 @@ impl Encrypted { }) } - pub fn decrypt(&self, pair: SsiPair) -> Result, DecryptionError> { - let key = self + pub fn decrypt(&self, pair: impl Into) -> Result, DecryptionError> { + let pair = pair.into(); + let (msg, c1) = self .keys .iter() .find(|(pk, _)| *pk == &pair.pk) .map(|(_, secret)| secret) - .ok_or(DecryptionError::KeyMismatch(pair.pk))? - .copy(); + .ok_or(DecryptionError::KeyMismatch(pair.pk))?; + let c1 = ec25519::PublicKey::new(c1.to_byte_array()); let key = pair - .decrypt_key(key) + .decrypt_key(*msg, c1) .map_err(|_| DecryptionError::InvalidPubkey(pair.pk))?; Ok(decrypt(self.data.as_slice(), self.nonce.into(), key)?) } } impl SsiPub { - pub fn encrypt_key(&self, key: &SymmetricKey) -> Result { + pub fn encrypt_key( + &self, + key: &SymmetricKey, + ) -> Result<(Bytes32, ec25519::PublicKey), InvalidPubkey> { match self.algo() { Algo::Ed25519 => self.encrypt_key_ed25519(key), Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey), } } - pub fn encrypt_key_ed25519(&self, key: &SymmetricKey) -> Result { - let ge = + pub fn encrypt_key_ed25519( + &self, + message: &SymmetricKey, + ) -> Result<(Bytes32, ec25519::PublicKey), InvalidPubkey> { + let pair = KeyPair::from_seed(Seed::generate()); + let y = pair.sk; + let c1 = pair.pk; + + let h = edwards25519::GeP3::from_bytes_vartime(&self.to_byte_array()).ok_or(InvalidPubkey)?; + let s = edwards25519::ge_scalarmult(&y.seed().scalar(), &h); + + println!("Shared secret generated: {}", s.to_bytes().to_hex()); + let c2 = edwards25519::sc_mul(message.as_ref(), &s.to_bytes()); + { + let s_neg = edwards25519::sc_invert(&s.to_bytes()); + let key = edwards25519::sc_mul(&c2, &s_neg); + assert_eq!(key, message.0.to_byte_array()) + } - Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge) - .to_bytes() - .into()) + // let c2 = edwards25519::ge_scalarmult(message.as_ref(), &s); + Ok((c2.into(), c1)) } } impl SsiPair { - pub fn decrypt_key(&self, key: Bytes32) -> Result { + pub fn decrypt_key( + &self, + encrypted_message: Bytes32, + c1: ec25519::PublicKey, + ) -> Result { match self.pk.algo() { - Algo::Ed25519 => self.decrypt_key_ed25519(key), + Algo::Ed25519 => self.decrypt_key_ed25519(encrypted_message, c1), Algo::Bip340 | Algo::Other(_) => Err(InvalidPubkey), } } - pub fn decrypt_key_ed25519(&self, key: Bytes32) -> Result { - let ge = edwards25519::GeP3::from_bytes_negate_vartime(&self.pk.to_byte_array()) - .ok_or(InvalidPubkey)?; - Ok(edwards25519::ge_scalarmult(key.as_ref(), &ge) - .to_bytes() - .into()) + pub fn decrypt_key_ed25519( + &self, + encrypted_message: Bytes32, + c1: ec25519::PublicKey, + ) -> Result { + let c1 = edwards25519::GeP3::from_bytes_vartime(&c1).ok_or(InvalidPubkey)?; + let s = edwards25519::ge_scalarmult(&self.sk.secret_bytes(), &c1); + println!("Shared secret recovered: {}", s.to_bytes().to_hex()); + let s_neg = + edwards25519::GeP3::from_bytes_negate_vartime(&s.to_bytes()).expect("valid pubkey"); + let key = edwards25519::sc_mul(encrypted_message.as_ref(), &s_neg.to_bytes()); + // let key = edwards25519::ge_scalarmult(encrypted_message.as_ref(), &s_neg); + Ok(SymmetricKey::from(key)) } } @@ -228,8 +258,8 @@ mod test { let sk = SsiSecret::new(Algo::Ed25519, Chain::Bitcoin); let pair = SsiPair::from(sk); let key = SymmetricKey::new(); - let encrypted = pair.pk.encrypt_key(&key).unwrap(); - let decrypted = pair.decrypt_key(encrypted).unwrap(); + let (encrypted, c1) = pair.pk.encrypt_key(&key).unwrap(); + let decrypted = pair.decrypt_key(encrypted, c1).unwrap(); assert_eq!(key.0, decrypted.0); } diff --git a/src/secret.rs b/src/secret.rs index 9066b35..3a16fcd 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -215,7 +215,7 @@ impl SsiSecret { } pub fn conceal(&self, passwd: impl AsRef) -> EncryptedSecret { - let (nonce, key) = encrypt(self.as_secret_bytes().to_vec(), passwd.as_ref()); + let (nonce, key) = encrypt(self.secret_bytes().to_vec(), passwd.as_ref()); EncryptedSecret { fp: self.to_public().fingerprint(), nonce, @@ -224,10 +224,10 @@ impl SsiSecret { } } - pub fn as_secret_bytes(&self) -> &[u8] { + pub fn secret_bytes(&self) -> [u8; 32] { match self { - SsiSecret::Bip340(sk) => sk.0.as_ref(), - SsiSecret::Ed25519(sk) => sk.0.as_ref(), + SsiSecret::Bip340(sk) => sk.0.secret_bytes(), + SsiSecret::Ed25519(sk) => sk.0.seed().scalar(), } } }