use nostr_sdk::prelude::*;
use crate::error::{Error, Result};
pub fn derive_shared_keys(admin_keys: &Keys, counterparty_pubkey: &PublicKey) -> Result<Keys> {
let shared_bytes =
nostr_sdk::util::generate_shared_key(admin_keys.secret_key(), counterparty_pubkey)
.map_err(|e| Error::ChatTransport(format!("ECDH failed: {e}")))?;
let secret = SecretKey::from_slice(&shared_bytes).map_err(|e| {
Error::ChatTransport(format!("shared-secret is not a valid SecretKey: {e}"))
})?;
Ok(Keys::new(secret))
}
pub fn derive_shared_key_hex(admin_keys: &Keys, counterparty_pubkey: &PublicKey) -> Result<String> {
let keys = derive_shared_keys(admin_keys, counterparty_pubkey)?;
Ok(keys.secret_key().to_secret_hex())
}
pub fn keys_from_shared_hex(hex: &str) -> Result<Keys> {
let secret = SecretKey::parse(hex)
.map_err(|e| Error::ChatTransport(format!("invalid shared-key hex: {e}")))?;
Ok(Keys::new(secret))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ecdh_is_symmetric() {
let admin = Keys::generate();
let party = Keys::generate();
let from_admin = derive_shared_keys(&admin, &party.public_key()).unwrap();
let from_party = derive_shared_keys(&party, &admin.public_key()).unwrap();
assert_eq!(
from_admin.secret_key().to_secret_hex(),
from_party.secret_key().to_secret_hex(),
"ECDH must yield the same secret on both sides"
);
assert_eq!(from_admin.public_key(), from_party.public_key());
}
#[test]
fn different_counterparties_produce_different_keys() {
let admin = Keys::generate();
let buyer = Keys::generate();
let seller = Keys::generate();
let b = derive_shared_key_hex(&admin, &buyer.public_key()).unwrap();
let s = derive_shared_key_hex(&admin, &seller.public_key()).unwrap();
assert_ne!(
b, s,
"different counterparties must yield distinct shared keys"
);
}
#[test]
fn hex_roundtrip_preserves_keys() {
let admin = Keys::generate();
let party = Keys::generate();
let hex = derive_shared_key_hex(&admin, &party.public_key()).unwrap();
let rebuilt = keys_from_shared_hex(&hex).unwrap();
let direct = derive_shared_keys(&admin, &party.public_key()).unwrap();
assert_eq!(
rebuilt.secret_key().to_secret_hex(),
direct.secret_key().to_secret_hex()
);
}
#[test]
fn malformed_hex_errors() {
assert!(keys_from_shared_hex("not-hex").is_err());
assert!(keys_from_shared_hex("").is_err());
}
}