mostro_core/chat/
shared_key.rs1use nostr_sdk::prelude::*;
14
15use crate::chat::keys::{derive_chat_keys_from_shared, generate_shared_key};
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 = generate_shared_key(secret, counterparty)?;
34 let secret = SecretKey::from_slice(&bytes).map_err(|e| {
35 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
36 "invalid shared secret: {e}"
37 )))
38 })?;
39 Ok(Self(Keys::new(secret)))
40 }
41
42 pub fn from_keys(keys: Keys) -> Self {
44 Self(keys)
45 }
46
47 pub fn keys(&self) -> &Keys {
51 &self.0
52 }
53
54 pub fn public_key(&self) -> PublicKey {
59 self.0.public_key()
60 }
61
62 pub fn secret_key(&self) -> &SecretKey {
64 self.0.secret_key()
65 }
66
67 pub fn chat_keys(&self) -> Result<(Keys, Keys), MostroError> {
69 derive_chat_keys_from_shared(self.secret_key().as_secret_bytes())
70 }
71
72 pub fn to_hex(&self) -> String {
74 self.0.secret_key().to_secret_hex()
75 }
76
77 pub fn from_hex(hex: &str) -> Result<Self, MostroError> {
79 let secret = SecretKey::from_hex(hex).map_err(|e| {
80 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
81 "invalid shared key hex: {e}"
82 )))
83 })?;
84 Ok(Self(Keys::new(secret)))
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn derive_is_symmetric_between_peers() {
94 let alice = Keys::generate();
95 let bob = Keys::generate();
96
97 let from_alice = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
98 let from_bob = SharedKey::derive(bob.secret_key(), &alice.public_key()).unwrap();
99
100 assert_eq!(from_alice.public_key(), from_bob.public_key());
101 assert_eq!(from_alice.to_hex(), from_bob.to_hex());
102
103 let (ac, as_) = from_alice.chat_keys().unwrap();
104 let (bc, bs) = from_bob.chat_keys().unwrap();
105 assert_eq!(ac.public_key(), bc.public_key());
106 assert_eq!(as_.public_key(), bs.public_key());
107 }
108
109 #[test]
110 fn derive_shared_key_hex_roundtrip() {
111 let alice = Keys::generate();
112 let bob = Keys::generate();
113 let derived = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
114
115 let hex = derived.to_hex();
116 let restored = SharedKey::from_hex(&hex).unwrap();
117
118 assert_eq!(derived.public_key(), restored.public_key());
119 assert_eq!(derived.to_hex(), restored.to_hex());
120 }
121
122 #[test]
123 fn derive_shared_key_different_peers_produce_different_keys() {
124 let alice = Keys::generate();
125 let bob = Keys::generate();
126 let carol = Keys::generate();
127
128 let with_bob = SharedKey::derive(alice.secret_key(), &bob.public_key()).unwrap();
129 let with_carol = SharedKey::derive(alice.secret_key(), &carol.public_key()).unwrap();
130
131 assert_ne!(with_bob.public_key(), with_carol.public_key());
132 }
133
134 #[test]
135 fn from_hex_rejects_invalid_input() {
136 let err = SharedKey::from_hex("not-a-hex-string").unwrap_err();
137 assert!(matches!(err, MostroError::MostroInternalErr(_)));
138 }
139}