1use calimero_primitives::identity::{PrivateKey, PublicKey};
2use ed25519_dalek::{SecretKey, SigningKey};
3use ring::aead;
4use thiserror::Error;
5
6pub const NONCE_LEN: usize = 12;
7
8pub type Nonce = [u8; NONCE_LEN];
9
10#[derive(Debug, Error)]
12#[non_exhaustive]
13pub enum SharedKeyError {
14 #[error("invalid public key: not a valid Edwards Y coordinate")]
16 InvalidPublicKey,
17}
18
19#[derive(Copy, Clone, Debug)]
20pub struct SharedKey {
21 key: SecretKey,
22}
23
24impl SharedKey {
25 pub fn new(sk: &PrivateKey, pk: &PublicKey) -> Result<Self, SharedKeyError> {
32 let decompressed = curve25519_dalek::edwards::CompressedEdwardsY(**pk)
33 .decompress()
34 .ok_or(SharedKeyError::InvalidPublicKey)?;
35
36 Ok(Self {
37 key: (SigningKey::from_bytes(sk).to_scalar() * decompressed)
38 .compress()
39 .to_bytes(),
40 })
41 }
42
43 #[must_use]
44 pub fn from_sk(sk: &PrivateKey) -> Self {
45 Self { key: **sk }
46 }
47
48 #[must_use]
49 pub fn encrypt(&self, payload: Vec<u8>, nonce: Nonce) -> Option<Vec<u8>> {
50 let encryption_key =
51 aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_256_GCM, &self.key).ok()?);
52
53 let mut cipher_text = payload;
54 encryption_key
55 .seal_in_place_append_tag(
56 aead::Nonce::assume_unique_for_key(nonce),
57 aead::Aad::empty(),
58 &mut cipher_text,
59 )
60 .ok()?;
61
62 Some(cipher_text)
63 }
64
65 #[must_use]
66 pub fn decrypt(&self, cipher_text: Vec<u8>, nonce: Nonce) -> Option<Vec<u8>> {
67 let decryption_key =
68 aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_256_GCM, &self.key).ok()?);
69
70 let mut payload = cipher_text;
71 let decrypted_len = decryption_key
72 .open_in_place(
73 aead::Nonce::assume_unique_for_key(nonce),
74 aead::Aad::empty(),
75 &mut payload,
76 )
77 .ok()?
78 .len();
79
80 payload.truncate(decrypted_len);
81
82 Some(payload)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use eyre::OptionExt;
89 use rand::thread_rng;
90
91 use super::*;
92
93 #[test]
94 fn test_encrypt_decrypt() -> eyre::Result<()> {
95 let mut csprng = thread_rng();
96
97 let signer = PrivateKey::random(&mut csprng);
98 let verifier = PrivateKey::random(&mut csprng);
99
100 let signer_shared_key = SharedKey::new(&signer, &verifier.public_key())?;
101 let verifier_shared_key = SharedKey::new(&verifier, &signer.public_key())?;
102
103 let payload = b"privacy is important";
104 let nonce = [0u8; NONCE_LEN];
105
106 let encrypted_payload = signer_shared_key
107 .encrypt(payload.to_vec(), nonce)
108 .ok_or_eyre("encryption failed")?;
109
110 let decrypted_payload = verifier_shared_key
111 .decrypt(encrypted_payload, nonce)
112 .ok_or_eyre("decryption failed")?;
113
114 assert_eq!(decrypted_payload, payload);
115 assert_ne!(decrypted_payload, b"privacy is not important");
116
117 Ok(())
118 }
119
120 #[test]
121 fn test_decrypt_with_invalid_key() -> eyre::Result<()> {
122 let mut csprng = thread_rng();
123
124 let signer = PrivateKey::random(&mut csprng);
125 let verifier = PrivateKey::random(&mut csprng);
126 let invalid = PrivateKey::random(&mut csprng);
127
128 let signer_shared_key = SharedKey::new(&signer, &verifier.public_key())?;
129 let invalid_shared_key = SharedKey::new(&invalid, &invalid.public_key())?;
130
131 let token = b"privacy is important";
132 let nonce = [0u8; NONCE_LEN];
133
134 let encrypted_token = signer_shared_key
135 .encrypt(token.to_vec(), nonce)
136 .ok_or_eyre("encryption failed")?;
137
138 let decrypted_data = invalid_shared_key.decrypt(encrypted_token, nonce);
139
140 assert!(decrypted_data.is_none());
141
142 Ok(())
143 }
144
145 #[test]
146 fn test_new_with_invalid_public_key() {
147 let mut csprng = thread_rng();
148 let signer = PrivateKey::random(&mut csprng);
149
150 let mut invalid_pk_bytes = [0u8; 32];
155 invalid_pk_bytes[0] = 2;
156 let invalid_pk = PublicKey::from(invalid_pk_bytes);
157
158 let result = SharedKey::new(&signer, &invalid_pk);
159 assert!(result.is_err());
160 assert!(matches!(result, Err(SharedKeyError::InvalidPublicKey)));
161 }
162}