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