1use ::hkdf::Hkdf;
16use nostr_sdk::prelude::*;
17use secp256k1::{ecdh, PublicKey as Secp256k1PublicKey};
18use sha2::Sha256;
19
20use crate::error::{MostroError, ServiceError};
21
22pub const CHAT_CONV_INFO: &[u8] = b"mostro:chat:conv:v1";
24pub const CHAT_SIGN_INFO: &[u8] = b"mostro:chat:sign:v1";
26
27pub(crate) fn generate_shared_key(
31 secret_key: &SecretKey,
32 public_key: &PublicKey,
33) -> Result<[u8; 32], MostroError> {
34 let mut compressed = [0u8; 33];
35 compressed[0] = 0x02; compressed[1..].copy_from_slice(public_key.as_bytes());
37 let normalized = Secp256k1PublicKey::from_slice(&compressed).map_err(|e| {
38 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
39 "invalid peer pubkey: {e}"
40 )))
41 })?;
42
43 let secret_key =
44 secp256k1::SecretKey::from_byte_array(&secret_key.to_secret_bytes()).map_err(|e| {
45 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
46 "invalid local secret key: {e}"
47 )))
48 })?;
49
50 let point = ecdh::shared_secret_point(&normalized, &secret_key);
51 let mut shared = [0u8; 32];
52 shared.copy_from_slice(&point[..32]);
53 Ok(shared)
54}
55
56pub fn derive_chat_keys(
61 own_trade: &Keys,
62 peer_trade: &PublicKey,
63) -> Result<(Keys, Keys), MostroError> {
64 let shared = generate_shared_key(own_trade.secret_key(), peer_trade)?;
65 derive_chat_keys_from_shared(&shared)
66}
67
68pub fn derive_chat_keys_from_shared(shared: &[u8]) -> Result<(Keys, Keys), MostroError> {
73 if shared.len() != 32 {
74 return Err(MostroError::MostroInternalErr(
75 ServiceError::EncryptionError(format!(
76 "chat shared secret must be 32 bytes, got {}",
77 shared.len()
78 )),
79 ));
80 }
81 let hkdf = Hkdf::<Sha256>::new(None, shared);
82
83 let derive = |info: &[u8]| -> Result<Keys, MostroError> {
84 for counter in 0u16..=255 {
87 let mut labelled = info.to_vec();
88 if counter > 0 {
89 labelled.push(counter as u8);
90 }
91 let mut out = [0u8; 32];
92 hkdf.expand(&labelled, &mut out).map_err(|e| {
93 MostroError::MostroInternalErr(ServiceError::EncryptionError(format!(
94 "HKDF expand failed: {e}"
95 )))
96 })?;
97 if let Ok(sk) = SecretKey::from_slice(&out) {
98 return Ok(Keys::new(sk));
99 }
100 }
101 Err(MostroError::MostroInternalErr(
102 ServiceError::EncryptionError("HKDF failed to produce a valid secret key".to_string()),
103 ))
104 };
105
106 Ok((derive(CHAT_CONV_INFO)?, derive(CHAT_SIGN_INFO)?))
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
115 fn protocol_test_vector_derived_pubkeys() {
116 let alice = Keys::parse("548f68890c49fa42f104c60352395e60ff030b0b407e955f1eed1400d6c0347a")
117 .unwrap();
118 let bob = Keys::parse("f258e73f07386d37133718b6127f873dd7c391b8f43b331ff8254034a13d2943")
119 .unwrap();
120
121 assert_eq!(
122 alice.public_key().to_hex(),
123 "000053c3b4773182e7c4c1b72b272d34be01bf4414a6a25c998977c516a46a01"
124 );
125 assert_eq!(
126 bob.public_key().to_hex(),
127 "000009ae5cff9f6ba9b05159ec5ed58c187f5882ea77c81ed5dd19163272a5d7"
128 );
129
130 let shared = generate_shared_key(alice.secret_key(), &bob.public_key()).unwrap();
131 let expected_shared =
132 SecretKey::from_hex("def6633a53d07d1e829484c4d4bdbbeed2f4b14c21743e63871c174338e39475")
133 .unwrap()
134 .to_secret_bytes();
135 assert_eq!(shared, expected_shared);
136
137 let (alice_conv, alice_sign) = derive_chat_keys(&alice, &bob.public_key()).unwrap();
138 let (bob_conv, bob_sign) = derive_chat_keys(&bob, &alice.public_key()).unwrap();
139
140 assert_eq!(alice_conv.public_key(), bob_conv.public_key());
141 assert_eq!(alice_sign.public_key(), bob_sign.public_key());
142 assert_eq!(
143 alice_conv.public_key().to_hex(),
144 "bceb1cd2a8e98ee9729122a1693edcc39c3ace04582ff96a26705c5e4078a6f2"
145 );
146 assert_eq!(
147 alice_sign.public_key().to_hex(),
148 "1dba04571059183f76b148119cfa6f8004dad30cb4e810180a6df17386a7f0b4"
149 );
150
151 let from_shared = derive_chat_keys_from_shared(&shared).unwrap();
152 assert_eq!(from_shared.0.public_key(), alice_conv.public_key());
153 assert_eq!(from_shared.1.public_key(), alice_sign.public_key());
154 }
155
156 #[test]
157 fn k_conv_cannot_derive_k_sign() {
158 let alice = Keys::generate();
159 let bob = Keys::generate();
160 let (conv, sign) = derive_chat_keys(&alice, &bob.public_key()).unwrap();
161 assert_ne!(
163 conv.secret_key().to_secret_bytes(),
164 sign.secret_key().to_secret_bytes()
165 );
166 assert_ne!(conv.public_key(), sign.public_key());
167 }
168
169 #[test]
170 fn derive_from_shared_rejects_wrong_length() {
171 let err = derive_chat_keys_from_shared(&[0u8; 16]).unwrap_err();
172 assert!(matches!(err, MostroError::MostroInternalErr(_)));
173 }
174}