1mod authentication;
120
121pub use self::authentication::AuthenticationPolicy;
122
123use self::authentication::{
124 generate_authentication_proof, verify_authentication_proof, write_anthentication_datas,
125};
126use crate::keys::ed25519::{
127 Ed25519KeyPair, KeyPairFromSeed32Generator, PublicKey as Ed25519PublicKey, Signature,
128};
129use crate::keys::x25519::{diffie_hellman, X25519PublicKey, X25519SecretKey};
130use crate::keys::{KeyPair, PubKeyFromBytesError};
131use crate::rand::UnspecifiedRandError;
132use crate::seeds::Seed32;
133use chacha20poly1305::aead::{AeadInPlace as _, NewAead};
134use chacha20poly1305::{ChaCha12Poly1305, ChaCha20Poly1305, ChaCha8Poly1305};
135use std::num::NonZeroU32;
136use std::{convert::TryFrom, hint::unreachable_unchecked};
137use zeroize::Zeroize;
138
139type Key = chacha20poly1305::aead::Key<ChaCha20Poly1305>;
140type Nonce =
141 chacha20poly1305::aead::Nonce<chacha20poly1305::aead::generic_array::typenum::consts::U12>;
142type Tag = chacha20poly1305::aead::Tag<chacha20poly1305::aead::generic_array::typenum::consts::U16>;
143
144pub const METADATA_LEN: usize = CLEAR_FOOTER_LEN + AUTHENTICATION_DATAS_LEN;
146
147const AUTHENTICATION_DATAS_LEN: usize = 97;
148const CLEAR_FOOTER_LEN: usize = EPHEMERAL_PUBLIC_KEY_LEN + TAG_LEN;
149const EPHEMERAL_PUBLIC_KEY_LEN: usize = 32;
150const PBKDF2_ITERATIONS: u32 = 3;
151const SENDER_PUBLIC_KEY_LEN: usize = 32;
152const TAG_LEN: usize = 16;
153
154#[derive(Clone, Copy, Debug)]
156pub enum ChaChaRounds {
157 ChaCha8,
159 ChaCha12,
161 ChaCha20,
163}
164
165#[derive(Debug)]
167pub enum PrivateMessageError {
168 IoError(std::io::Error),
170 InvalidEphemeralPubKey(PubKeyFromBytesError),
172 InvalidSenderPubKey(PubKeyFromBytesError),
174 InvalidAuthenticationProof,
176 UnspecifiedAeadError,
178 UnspecifiedRandError,
180}
181
182impl From<std::io::Error> for PrivateMessageError {
183 fn from(e: std::io::Error) -> Self {
184 PrivateMessageError::IoError(e)
185 }
186}
187
188impl From<UnspecifiedRandError> for PrivateMessageError {
189 fn from(_: UnspecifiedRandError) -> Self {
190 PrivateMessageError::UnspecifiedRandError
191 }
192}
193
194#[derive(Zeroize)]
195#[zeroize(drop)]
196struct SharedSecret([u8; 44]);
197
198impl Default for SharedSecret {
199 fn default() -> Self {
200 SharedSecret([0u8; 44])
201 }
202}
203
204impl AsRef<[u8]> for SharedSecret {
205 fn as_ref(&self) -> &[u8] {
206 &self.0
207 }
208}
209
210impl AsMut<[u8]> for SharedSecret {
211 fn as_mut(&mut self) -> &mut [u8] {
212 &mut self.0
213 }
214}
215
216pub fn encrypt_private_message<M>(
218 additionally_authenticated_data: &[u8],
219 authentication_policy: AuthenticationPolicy,
220 chacha_rounds: ChaChaRounds,
221 message: &mut M,
222 receiver_public_key: &Ed25519PublicKey,
223 sender_keypair: &Ed25519KeyPair,
224) -> Result<(), PrivateMessageError>
225where
226 M: AsRef<[u8]> + AsMut<[u8]> + Extend<u8>,
227{
228 let ephemeral_keypair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
230
231 let shared_secret = generate_shared_secret(
234 ephemeral_keypair.public_key().datas.as_ref(),
235 ephemeral_keypair.seed(),
236 &receiver_public_key,
237 );
238
239 let encrypted_footer = write_anthentication_datas(
241 &sender_keypair.public_key(),
242 generate_authentication_proof(
243 authentication_policy,
244 sender_keypair,
245 receiver_public_key,
246 message.as_ref(),
247 ),
248 authentication_policy,
249 );
250 message.extend(encrypted_footer);
251
252 let tag = encrypt(
254 additionally_authenticated_data,
255 chacha_rounds,
256 message.as_mut(),
257 shared_secret,
258 )?;
259
260 let mut clear_footer = arrayvec::ArrayVec::<u8, 48>::new();
262 clear_footer
263 .try_extend_from_slice(tag.as_ref())
264 .unwrap_or_else(|_| unsafe { unreachable_unchecked() }); clear_footer
266 .try_extend_from_slice(ephemeral_keypair.public_key().datas.as_ref())
267 .unwrap_or_else(|_| unsafe { unreachable_unchecked() }); message.extend(clear_footer.into_iter());
269
270 Ok(())
271}
272
273pub struct DecryptedMessage<'m> {
275 pub message: &'m [u8],
277 pub sender_public_key: Ed25519PublicKey,
279 pub signature_opt: Option<Signature>,
281}
282
283pub fn decrypt_private_message<'m>(
289 additionally_authenticated_data: &[u8],
290 chacha_rounds: ChaChaRounds,
291 encrypted_message: &'m mut [u8],
292 receiver_key_pair: &Ed25519KeyPair,
293) -> Result<DecryptedMessage<'m>, PrivateMessageError> {
294 let len = encrypted_message.len();
296 let clear_footer_begin = len - EPHEMERAL_PUBLIC_KEY_LEN - TAG_LEN;
297 let tag_end = len - EPHEMERAL_PUBLIC_KEY_LEN;
298 let tag = Tag::from_slice(&encrypted_message[clear_footer_begin..tag_end]).to_owned();
299 let sender_ephemeral_public_key =
300 Ed25519PublicKey::try_from(&encrypted_message[(len - EPHEMERAL_PUBLIC_KEY_LEN)..])
301 .map_err(PrivateMessageError::InvalidEphemeralPubKey)?;
302
303 let shared_secret = generate_shared_secret(
306 &sender_ephemeral_public_key.datas.as_ref(),
307 &receiver_key_pair.seed(),
308 &sender_ephemeral_public_key,
309 );
310
311 decrypt(
313 additionally_authenticated_data,
314 chacha_rounds,
315 &mut encrypted_message[..(len - CLEAR_FOOTER_LEN)],
316 shared_secret,
317 &tag,
318 )?;
319
320 let authent_end = clear_footer_begin;
322 let authent_begin = authent_end - AUTHENTICATION_DATAS_LEN;
323 let (sender_public_key, sig_opt) = verify_authentication_proof(
324 receiver_key_pair,
325 &encrypted_message[..authent_begin],
326 &encrypted_message[authent_begin..authent_end],
327 )?;
328
329 Ok(DecryptedMessage {
330 message: &encrypted_message[..authent_begin],
331 sender_public_key,
332 signature_opt: sig_opt,
333 })
334}
335
336fn generate_shared_secret(
337 ephemeral_public_key: &[u8],
338 exchange_secret_key: &Seed32,
339 exchange_public_key: &Ed25519PublicKey,
340) -> SharedSecret {
341 diffie_hellman(
342 X25519SecretKey::from(exchange_secret_key),
343 X25519PublicKey::from(exchange_public_key),
344 |key_material| derive(key_material, ephemeral_public_key),
345 )
346}
347
348#[cfg(target_arch = "wasm32")]
349#[cfg(not(tarpaulin_include))]
350fn derive(seed: &[u8], salt: &[u8]) -> SharedSecret {
351 let mut shared_secret = SharedSecret::default();
352 let mut hmac = cryptoxide::hmac::Hmac::new(cryptoxide::sha2::Sha512::new(), seed);
353 cryptoxide::pbkdf2::pbkdf2(&mut hmac, salt, PBKDF2_ITERATIONS, shared_secret.as_mut());
354 shared_secret
355}
356#[cfg(not(target_arch = "wasm32"))]
357fn derive(seed: &[u8], salt: &[u8]) -> SharedSecret {
358 let mut shared_secret = SharedSecret::default();
359 ring::pbkdf2::derive(
360 ring::pbkdf2::PBKDF2_HMAC_SHA512,
361 unsafe { NonZeroU32::new_unchecked(PBKDF2_ITERATIONS) },
362 salt,
363 seed,
364 shared_secret.as_mut(),
365 );
366 shared_secret
367}
368
369fn encrypt(
370 associated_data: &[u8],
371 chacha_rounds: ChaChaRounds,
372 message: &mut [u8],
373 shared_secret: SharedSecret,
374) -> Result<Tag, PrivateMessageError> {
375 let symmetric_key = Key::from_slice(&shared_secret.as_ref()[..32]);
376 let nonce = Nonce::from_slice(&shared_secret.as_ref()[32..44]);
377 match chacha_rounds {
378 ChaChaRounds::ChaCha8 => ChaCha8Poly1305::new(symmetric_key)
379 .encrypt_in_place_detached(&nonce, associated_data, message)
380 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
381 ChaChaRounds::ChaCha12 => ChaCha12Poly1305::new(symmetric_key)
382 .encrypt_in_place_detached(&nonce, associated_data, message)
383 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
384 ChaChaRounds::ChaCha20 => ChaCha20Poly1305::new(symmetric_key)
385 .encrypt_in_place_detached(&nonce, associated_data, message)
386 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
387 }
388}
389
390fn decrypt(
391 associated_data: &[u8],
392 chacha_rounds: ChaChaRounds,
393 encrypted_message: &mut [u8],
394 shared_secret: SharedSecret,
395 tag: &Tag,
396) -> Result<(), PrivateMessageError> {
397 let symmetric_key = Key::from_slice(&shared_secret.as_ref()[..32]);
398 let nonce = Nonce::from_slice(&shared_secret.as_ref()[32..44]);
399
400 match chacha_rounds {
401 ChaChaRounds::ChaCha8 => ChaCha8Poly1305::new(symmetric_key)
402 .decrypt_in_place_detached(&nonce, associated_data, encrypted_message, tag)
403 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
404 ChaChaRounds::ChaCha12 => ChaCha12Poly1305::new(symmetric_key)
405 .decrypt_in_place_detached(&nonce, associated_data, encrypted_message, tag)
406 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
407 ChaChaRounds::ChaCha20 => ChaCha20Poly1305::new(symmetric_key)
408 .decrypt_in_place_detached(&nonce, associated_data, encrypted_message, tag)
409 .map_err(|_| PrivateMessageError::UnspecifiedAeadError),
410 }
411}
412
413#[cfg(test)]
414mod tests {
415
416 use super::*;
417 use crate::keys::ed25519::KeyPairFromSeed32Generator;
418 use crate::keys::KeyPair;
419 use unwrap::unwrap;
420
421 const AAD: &[u8] = b"service name - currency name";
422 const MESSAGE: &[u8] =
423 b"Hello, this is a secret message, which can only be read by the recipient.";
424
425 #[test]
426 fn encrypt_same_message_must_be_different() -> Result<(), PrivateMessageError> {
427 let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
428 let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
429
430 let message = MESSAGE;
431
432 let encrypted_message1 =
433 test_encrypt(message, &receiver_key_pair.public_key(), &sender_key_pair)?;
434
435 let encrypted_message2 =
436 test_encrypt(message, &receiver_key_pair.public_key(), &sender_key_pair)?;
437
438 assert_ne!(encrypted_message1, encrypted_message2);
439 assert_ne!(encrypted_message1[32..37], encrypted_message2[32..37]);
440
441 Ok(())
442 }
443
444 #[test]
445 fn encrypt_then_decrypt_with_invalid_aad() -> Result<(), PrivateMessageError> {
446 let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
447 let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
448
449 let message = MESSAGE;
450
451 let mut encrypted_message =
452 test_encrypt(message, &receiver_key_pair.public_key(), &sender_key_pair)?;
453
454 println!("encrypted message={:?}", encrypted_message);
455
456 match decrypt_private_message(
457 b"invalid aad",
458 ChaChaRounds::ChaCha20,
459 &mut encrypted_message,
460 &receiver_key_pair,
461 ) {
462 Ok(_) => {
463 panic!("Expected error PrivateMessageError::UnspecifiedAeadError, found: Ok(()).")
464 }
465 Err(PrivateMessageError::UnspecifiedAeadError) => Ok(()),
466 Err(e) => panic!(
467 "Expected error PrivateMessageError::UnspecifiedAeadError, found: {:?}.",
468 e
469 ),
470 }
471 }
472
473 #[test]
474 fn encrypt_then_decrypt_with_invalid_algorithm() -> Result<(), PrivateMessageError> {
475 let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
476 let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
477
478 let message = MESSAGE;
479
480 let mut encrypted_message =
481 test_encrypt(message, &receiver_key_pair.public_key(), &sender_key_pair)?;
482
483 println!("encrypted message={:?}", encrypted_message);
484
485 match decrypt_private_message(
486 AAD,
487 ChaChaRounds::ChaCha12,
488 &mut encrypted_message,
489 &receiver_key_pair,
490 ) {
491 Ok(_) => {
492 panic!("Expected error PrivateMessageError::UnspecifiedAeadError, found: Ok(()).")
493 }
494 Err(PrivateMessageError::UnspecifiedAeadError) => Ok(()),
495 Err(e) => panic!(
496 "Expected error PrivateMessageError::UnspecifiedAeadError, found: {:?}.",
497 e
498 ),
499 }
500 }
501
502 #[test]
503 fn encrypt_and_decrypt_ok() -> Result<(), PrivateMessageError> {
504 let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
505 let receiver_key_pair = KeyPairFromSeed32Generator::generate(unwrap!(Seed32::from_base58(
506 "7nY1fYmCXL1vF86ptneeg8r7M6C7G93M8MCfzBCaCtiJ"
507 )));
508
509 let message = MESSAGE;
510
511 let mut encrypted_message =
512 test_encrypt(message, &receiver_key_pair.public_key(), &sender_key_pair)?;
513
514 println!("encrypted message={:?}", encrypted_message);
515
516 let DecryptedMessage {
517 message: decrypted_message,
518 sender_public_key,
519 signature_opt,
520 } = decrypt_private_message(
521 AAD,
522 ChaChaRounds::ChaCha20,
523 &mut encrypted_message,
524 &receiver_key_pair,
525 )?;
526
527 println!("decrypted message={:?}", decrypted_message);
528
529 assert_eq!(decrypted_message, message);
530 assert_eq!(sender_public_key, sender_key_pair.public_key());
531 assert_eq!(signature_opt, None);
532
533 Ok(())
534 }
535
536 fn test_encrypt(
537 message: &[u8],
538 receiver_public_key: &Ed25519PublicKey,
539 sender_keypair: &Ed25519KeyPair,
540 ) -> Result<Vec<u8>, PrivateMessageError> {
541 let mut encrypted_message = Vec::new();
542 encrypted_message.extend(message);
543
544 encrypt_private_message(
545 AAD,
546 AuthenticationPolicy::PrivateAuthentication,
547 ChaChaRounds::ChaCha20,
548 &mut encrypted_message,
549 receiver_public_key,
550 sender_keypair,
551 )?;
552
553 Ok(encrypted_message)
554 }
555}