mostro_core/chat/
shared_key.rs1use nostr_sdk::prelude::*;
12
13use crate::error::{MostroError, ServiceError};
14
15#[derive(Debug, Clone)]
22pub struct SharedKey(Keys);
23
24impl SharedKey {
25 pub fn derive(secret: &SecretKey, counterparty: &PublicKey) -> Result<Self, MostroError> {
32 let bytes = nostr_sdk::util::generate_shared_key(secret, counterparty).map_err(|e| {
33 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
34 "shared key derivation failed: {e}"
35 )))
36 })?;
37 let secret = SecretKey::from_slice(&bytes).map_err(|e| {
38 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
39 "invalid shared secret: {e}"
40 )))
41 })?;
42 Ok(Self(Keys::new(secret)))
43 }
44
45 pub fn from_keys(keys: Keys) -> Self {
50 Self(keys)
51 }
52
53 pub fn keys(&self) -> &Keys {
55 &self.0
56 }
57
58 pub fn public_key(&self) -> PublicKey {
61 self.0.public_key()
62 }
63
64 pub fn secret_key(&self) -> &SecretKey {
66 self.0.secret_key()
67 }
68
69 pub fn to_hex(&self) -> String {
72 self.0.secret_key().to_secret_hex()
73 }
74
75 pub fn from_hex(hex: &str) -> Result<Self, MostroError> {
78 let secret = SecretKey::from_hex(hex).map_err(|e| {
79 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
80 "invalid shared key hex: {e}"
81 )))
82 })?;
83 Ok(Self(Keys::new(secret)))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn derive_is_symmetric_between_peers() {
93 let alice = Keys::generate();
94 let bob = Keys::generate();
95
96 let from_alice = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
97 let from_bob = SharedKey::derive(bob.secret_key(), &alice.public_key()).unwrap();
98
99 assert_eq!(from_alice.public_key(), from_bob.public_key());
100 assert_eq!(from_alice.to_hex(), from_bob.to_hex());
101 }
102
103 #[test]
104 fn derive_shared_key_hex_roundtrip() {
105 let alice = Keys::generate();
106 let bob = Keys::generate();
107 let derived = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
108
109 let hex = derived.to_hex();
110 let restored = SharedKey::from_hex(&hex).unwrap();
111
112 assert_eq!(derived.public_key(), restored.public_key());
113 assert_eq!(derived.to_hex(), restored.to_hex());
114 }
115
116 #[test]
117 fn derive_shared_key_different_peers_produce_different_keys() {
118 let alice = Keys::generate();
119 let bob = Keys::generate();
120 let carol = Keys::generate();
121
122 let with_bob = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
123 let with_carol = SharedKey::derive(alice.secret_key(), &carol.public_key()).unwrap();
124
125 assert_ne!(with_bob.public_key(), with_carol.public_key());
126 }
127
128 #[test]
129 fn from_hex_rejects_invalid_input() {
130 let err = SharedKey::from_hex("not-a-hex-string").unwrap_err();
131 assert!(matches!(err, MostroError::MostroInternalErr(_)));
132 }
133}