vector_core/community/
cipher.rs1use nostr_sdk::prelude::nip44::v2::{decrypt_to_bytes, encrypt_to_bytes_with_nonce, ConversationKey};
7use rand::RngCore;
8
9pub fn encrypt_with_random_nonce(
16 ck: &ConversationKey,
17 plaintext: &[u8],
18) -> Result<Vec<u8>, String> {
19 let mut nonce = [0u8; 32];
20 rand::rngs::OsRng.fill_bytes(&mut nonce);
21 encrypt_to_bytes_with_nonce(ck, plaintext, nonce).map_err(|e| e.to_string())
22}
23
24pub fn seal(key: &[u8; 32], plaintext: &[u8]) -> Result<String, String> {
26 let ck = ConversationKey::new(*key);
27 let ciphertext = encrypt_with_random_nonce(&ck, plaintext)?;
28 Ok(base64_simd::STANDARD.encode_to_string(&ciphertext))
29}
30
31pub fn open(key: &[u8; 32], content_b64: &str) -> Result<Vec<u8>, String> {
34 let ciphertext = base64_simd::STANDARD
35 .decode_to_vec(content_b64.as_bytes())
36 .map_err(|e| e.to_string())?;
37 let ck = ConversationKey::new(*key);
38 decrypt_to_bytes(&ck, &ciphertext).map_err(|e| e.to_string())
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn round_trip() {
47 let key = [0x5au8; 32];
48 let sealed = seal(&key, b"hello community").unwrap();
49 assert_eq!(open(&key, &sealed).unwrap(), b"hello community");
50 }
51
52 #[test]
53 fn wrong_key_fails() {
54 let sealed = seal(&[1u8; 32], b"secret").unwrap();
55 assert!(open(&[2u8; 32], &sealed).is_err());
56 }
57
58 #[test]
59 fn distinct_ciphertext_per_call() {
60 let key = [9u8; 32];
61 assert_ne!(seal(&key, b"x").unwrap(), seal(&key, b"x").unwrap());
62 }
63}