noah_crypto/basic/
hybrid_encryption.rs1use aes::{
2 cipher::{generic_array::GenericArray, KeyIvInit, StreamCipher},
3 Aes256,
4};
5use curve25519_dalek::edwards::CompressedEdwardsY;
6use ed25519_dalek::{ExpandedSecretKey, PublicKey, SecretKey};
7use noah_algebra::errors::NoahError;
8use noah_algebra::prelude::*;
9use noah_algebra::ristretto::RistrettoScalar;
10use serde::Serializer;
11use sha2::Digest;
12use wasm_bindgen::prelude::*;
13
14type Aes256Ctr = ctr::Ctr64BE<Aes256>;
15
16#[wasm_bindgen]
17#[derive(Debug, Clone)]
18pub struct XPublicKey {
20 pub(crate) key: x25519_dalek::PublicKey,
21}
22
23impl NoahFromToBytes for XPublicKey {
24 fn noah_to_bytes(&self) -> Vec<u8> {
25 self.key.as_bytes().to_vec()
26 }
27
28 fn noah_from_bytes(bytes: &[u8]) -> Result<Self> {
29 if bytes.len() != 32 {
30 Err(eg!(NoahError::DeserializationError))
31 } else {
32 let mut array = [0u8; 32];
33 array.copy_from_slice(bytes);
34 Ok(XPublicKey {
35 key: x25519_dalek::PublicKey::from(array),
36 })
37 }
38 }
39}
40
41serialize_deserialize!(XPublicKey);
42
43impl XPublicKey {
44 pub fn from(sk: &XSecretKey) -> XPublicKey {
46 XPublicKey {
47 key: x25519_dalek::PublicKey::from(&sk.key),
48 }
49 }
50}
51
52impl PartialEq for XPublicKey {
53 fn eq(&self, other: &Self) -> bool {
54 self.key.as_bytes() == other.key.as_bytes()
55 }
56}
57
58impl Eq for XPublicKey {}
59
60#[wasm_bindgen]
61#[derive(Clone)]
62pub struct XSecretKey {
64 pub(crate) key: x25519_dalek::StaticSecret,
65}
66
67impl NoahFromToBytes for XSecretKey {
68 fn noah_to_bytes(&self) -> Vec<u8> {
69 self.key.to_bytes().to_vec()
70 }
71
72 fn noah_from_bytes(bytes: &[u8]) -> Result<Self> {
73 if bytes.len() != 32 {
74 Err(eg!(NoahError::DeserializationError))
75 } else {
76 let mut array = [0u8; 32];
77 array.copy_from_slice(bytes);
78 Ok(XSecretKey {
79 key: x25519_dalek::StaticSecret::from(array),
80 })
81 }
82 }
83}
84
85serialize_deserialize!(XSecretKey);
86
87impl XSecretKey {
88 pub fn new<R: CryptoRng + RngCore>(prng: &mut R) -> XSecretKey {
90 XSecretKey {
91 key: x25519_dalek::StaticSecret::new(prng),
92 }
93 }
94}
95
96impl PartialEq for XSecretKey {
97 fn eq(&self, other: &Self) -> bool {
98 self.key.to_bytes() == other.key.to_bytes()
99 }
100}
101
102impl Eq for XSecretKey {}
103
104#[derive(Clone, Debug, Default, PartialEq, Eq)]
105pub struct Ctext(pub Vec<u8>);
107impl NoahFromToBytes for Ctext {
108 fn noah_to_bytes(&self) -> Vec<u8> {
109 self.0.clone()
110 }
111
112 fn noah_from_bytes(bytes: &[u8]) -> Result<Self> {
113 Ok(Ctext(bytes.to_vec()))
114 }
115}
116serialize_deserialize!(Ctext);
117
118#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
119pub struct NoahHybridCiphertext {
121 pub(crate) ciphertext: Ctext,
122 pub(crate) ephemeral_public_key: XPublicKey,
123}
124
125impl NoahFromToBytes for NoahHybridCiphertext {
126 fn noah_to_bytes(&self) -> Vec<u8> {
127 let mut bytes = vec![];
128 bytes.append(&mut self.ephemeral_public_key.noah_to_bytes());
129 bytes.append(&mut self.ciphertext.noah_to_bytes());
130 bytes
131 }
132
133 fn noah_from_bytes(bytes: &[u8]) -> Result<Self> {
134 if bytes.len() < 32 {
135 Err(eg!(NoahError::DeserializationError))
136 } else {
137 let ephemeral_public_key = XPublicKey::noah_from_bytes(&bytes[0..32])?;
138 let ciphertext = Ctext::noah_from_bytes(&bytes[32..])?;
139 Ok(Self {
140 ciphertext,
141 ephemeral_public_key,
142 })
143 }
144 }
145}
146
147pub fn hybrid_encrypt_x25519<R: CryptoRng + RngCore>(
149 prng: &mut R,
150 pub_key: &XPublicKey,
151 message: &[u8],
152) -> NoahHybridCiphertext {
153 let (key, ephemeral_key) = symmetric_key_from_x25519_public_key(prng, &pub_key.key);
154 let ciphertext = symmetric_encrypt(&key, message);
155 NoahHybridCiphertext {
156 ciphertext,
157 ephemeral_public_key: XPublicKey { key: ephemeral_key },
158 }
159}
160
161pub fn hybrid_encrypt_ed25519<R: CryptoRng + RngCore>(
163 prng: &mut R,
164 pub_key: &PublicKey,
165 message: &[u8],
166) -> NoahHybridCiphertext {
167 let (key, ephemeral_key) = symmetric_key_from_ed25519_public_key(prng, pub_key);
168 let ciphertext = symmetric_encrypt(&key, message);
169
170 NoahHybridCiphertext {
171 ciphertext,
172 ephemeral_public_key: XPublicKey { key: ephemeral_key },
173 }
174}
175
176pub fn hybrid_decrypt_with_x25519_secret_key(
178 ctext: &NoahHybridCiphertext,
179 sec_key: &XSecretKey,
180) -> Vec<u8> {
181 let key = symmetric_key_from_x25519_secret_key(&sec_key.key, &ctext.ephemeral_public_key.key);
182 symmetric_decrypt(&key, &ctext.ciphertext)
183}
184
185pub fn hybrid_decrypt_with_ed25519_secret_key(
187 ctext: &NoahHybridCiphertext,
188 sec_key: &SecretKey,
189) -> Vec<u8> {
190 let key = symmetric_key_from_ed25519_secret_key(sec_key, &ctext.ephemeral_public_key.key);
191 symmetric_decrypt(&key, &ctext.ciphertext)
192}
193
194fn shared_secret_to_symmetric_key(shared_secret: &x25519_dalek::SharedSecret) -> [u8; 32] {
196 let mut hasher = sha2::Sha256::new();
197 hasher.update(shared_secret.as_bytes());
198 let hash = hasher.finalize();
199 let mut symmetric_key = [0u8; 32];
200 symmetric_key.copy_from_slice(hash.as_slice());
201 symmetric_key
202}
203
204fn symmetric_key_from_x25519_public_key<R: CryptoRng + RngCore>(
206 prng: &mut R,
207 public_key: &x25519_dalek::PublicKey,
208) -> ([u8; 32], x25519_dalek::PublicKey) {
209 let ephemeral = x25519_dalek::EphemeralSecret::new(prng);
211 let dh_pk = x25519_dalek::PublicKey::from(&ephemeral);
212
213 let shared = ephemeral.diffie_hellman(public_key);
214
215 let symmetric_key = shared_secret_to_symmetric_key(&shared);
216 (symmetric_key, dh_pk)
217}
218
219fn symmetric_key_from_ed25519_public_key<R>(
221 prng: &mut R,
222 public_key: &PublicKey,
223) -> ([u8; 32], x25519_dalek::PublicKey)
224where
225 R: CryptoRng + RngCore,
226{
227 let pk_curve_point = CompressedEdwardsY::from_slice(public_key.as_bytes());
228 let pk_montgomery = pk_curve_point.decompress().unwrap().to_montgomery();
229 let x_public_key = x25519_dalek::PublicKey::from(pk_montgomery.to_bytes());
230
231 symmetric_key_from_x25519_public_key(prng, &x_public_key)
232}
233
234fn sec_key_as_scalar(sk: &SecretKey) -> RistrettoScalar {
235 let expanded: ExpandedSecretKey = sk.into();
236 let mut key_bytes = [0u8; 32];
238 key_bytes.copy_from_slice(&expanded.to_bytes()[0..32]); RistrettoScalar::from_bytes(&key_bytes).unwrap() }
241
242fn symmetric_key_from_x25519_secret_key(
244 sec_key: &x25519_dalek::StaticSecret,
245 ephemeral_public_key: &x25519_dalek::PublicKey,
246) -> [u8; 32] {
247 let shared_key = sec_key.diffie_hellman(ephemeral_public_key);
248 shared_secret_to_symmetric_key(&shared_key)
249}
250
251fn symmetric_key_from_ed25519_secret_key(
253 sec_key: &SecretKey,
254 ephemeral_public_key: &x25519_dalek::PublicKey,
255) -> [u8; 32] {
256 let scalar_sec_key = sec_key_as_scalar(sec_key);
257 let mut bytes = [0u8; 32];
258 bytes.copy_from_slice(scalar_sec_key.to_bytes().as_slice());
259 let x_secret = x25519_dalek::StaticSecret::from(bytes);
260 symmetric_key_from_x25519_secret_key(&x_secret, ephemeral_public_key)
261}
262
263fn symmetric_encrypt(key: &[u8; 32], plaintext: &[u8]) -> Ctext {
264 let kkey = GenericArray::from_slice(key);
265 let ctr = GenericArray::from_slice(&[0u8; 16]); let mut ctext_vec = plaintext.to_vec();
267 let mut cipher = Aes256Ctr::new(kkey, ctr);
268 cipher.apply_keystream(ctext_vec.as_mut_slice());
269 Ctext(ctext_vec)
270}
271
272fn symmetric_decrypt(key: &[u8; 32], ciphertext: &Ctext) -> Vec<u8> {
273 let kkey = GenericArray::from_slice(key);
274 let ctr = GenericArray::from_slice(&[0u8; 16]);
275 let mut plaintext_vec = ciphertext.0.clone();
276 let mut cipher = Aes256Ctr::new(kkey, ctr);
277 cipher.apply_keystream(plaintext_vec.as_mut_slice());
278 plaintext_vec
279}
280
281#[cfg(test)]
282mod test {
283 use super::*;
284 use ed25519_dalek::Keypair;
285
286 #[test]
287 fn key_derivation() {
288 let mut prng = test_rng();
289 let keypair = Keypair::generate(&mut prng);
290 let (from_pk_key, encoded_rand) =
291 symmetric_key_from_ed25519_public_key(&mut prng, &keypair.public);
292 let from_sk_key =
293 symmetric_key_from_ed25519_secret_key(&keypair.secret_key(), &encoded_rand);
294 assert_eq!(from_pk_key, from_sk_key);
295 }
296
297 #[test]
298 fn symmetric_encryption_fresh_key() {
299 let msg = b"this is a message";
300 let key: [u8; 32] = [0u8; 32];
301 let mut ciphertext = symmetric_encrypt(&key, msg);
302 let decrypted = symmetric_decrypt(&key, &ciphertext);
303 assert_eq!(msg, decrypted.as_slice());
304
305 ciphertext.0[0] = 0xFF - ciphertext.0[0];
306 let result = symmetric_decrypt(&key, &ciphertext);
307 assert_ne!(msg, result.as_slice());
308 }
309
310 #[test]
311 fn hybrid_cipher() {
312 let mut prng = test_rng();
313 let key_pair = Keypair::generate(&mut prng);
314 let msg = b"this is another message";
315
316 let cipherbox = hybrid_encrypt_ed25519(&mut prng, &key_pair.public, msg);
317 let plaintext = hybrid_decrypt_with_ed25519_secret_key(&cipherbox, &key_pair.secret_key());
318 assert_eq!(msg, plaintext.as_slice());
319 }
320}