Skip to main content
Sign in
Snippets Groups Projects
Commit c16a6415 authored by Éloïs's avatar Éloïs
Browse files

[feat] add module private message encryption

Closes #3
parent eeb7ad41
No related branches found
No related tags found
No related merge requests found
......@@ -288,6 +288,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "cryptoxide"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35f15e1a0699dd988fed910dd78fdc6407f44654cd12589c91fa44ea67d9159"
[[package]]
name = "csv"
version = "1.1.3"
......@@ -330,6 +336,7 @@ dependencies = [
"bs58",
"byteorder",
"criterion",
"cryptoxide",
"ring",
"scrypt",
"serde",
......
......
......@@ -18,6 +18,7 @@ arrayvec = { version = "0.5.1", features = ["array-sizes-33-128", "array-sizes-1
base64 = "0.11.0"
bs58 = "0.3.0"
byteorder = "1.3.2"
cryptoxide = { version = "0.1.3", optional = true }
ring = "0.16.9"
scrypt = { version = "0.2", default-features = false }
serde = { version = "1.0.*", features = ["derive"], optional = true }
......@@ -39,5 +40,6 @@ default = ["dewif", "rand", "ser"]
aes256 = ["aes"]
dewif = ["aes256", "arrayvec"]
private_message = ["cryptoxide", "rand"]
rand = []
ser = ["serde"]
......@@ -48,6 +48,8 @@ pub mod bases;
pub mod dewif;
pub mod hashs;
pub mod keys;
#[cfg(feature = "private_message")]
pub mod private_message;
#[cfg(feature = "rand")]
pub mod rand;
pub mod seeds;
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Manage private message encryption/decryption
//!
pub use ring::aead::Aad;
use crate::keys::ed25519::{Ed25519KeyPair, PublicKey};
use crate::rand::UnspecifiedRandError;
use crate::seeds::Seed32;
use cryptoxide::ed25519::{exchange, keypair};
use ring::aead::{LessSafeKey, Nonce, UnboundKey};
use ring::pbkdf2;
use std::io::Write;
use std::num::NonZeroU32;
use zeroize::Zeroize;
const ITERATIONS: u32 = 3;
const EPHEMERAL_PUBLIC_KEY_LEN: usize = 32;
/// Private message encryption algorithm
#[derive(Clone, Copy, Debug)]
pub enum Algorithm {
/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
Aes256Gcm,
/// ChaCha20-Poly1305 as described in [RFC 7539](https://tools.ietf.org/html/rfc7539).
Chacha20Poly1305,
}
impl Algorithm {
fn to_ring_algo(self) -> &'static ring::aead::Algorithm {
match self {
Self::Aes256Gcm => &ring::aead::AES_256_GCM,
Self::Chacha20Poly1305 => &ring::aead::CHACHA20_POLY1305,
}
}
}
/// Error at encryption/decryption of a private message
#[derive(Debug)]
pub enum PrivateMessageError {
/// I/O error
IoError(std::io::Error),
/// Unspecified errror
Unspecified,
}
impl From<std::io::Error> for PrivateMessageError {
fn from(e: std::io::Error) -> Self {
PrivateMessageError::IoError(e)
}
}
impl From<UnspecifiedRandError> for PrivateMessageError {
fn from(_: UnspecifiedRandError) -> Self {
PrivateMessageError::Unspecified
}
}
impl From<ring::error::Unspecified> for PrivateMessageError {
fn from(_: ring::error::Unspecified) -> Self {
PrivateMessageError::Unspecified
}
}
#[derive(Zeroize)]
#[zeroize(drop)]
struct SecretKey([u8; 64]);
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Zeroize)]
#[zeroize(drop)]
struct SharedSecret([u8; 48]);
impl Default for SharedSecret {
fn default() -> Self {
SharedSecret([0u8; 48])
}
}
impl AsRef<[u8]> for SharedSecret {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for SharedSecret {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
/// Encrypt private message
pub fn encrypt_private_message<A: AsRef<[u8]>, W: Write>(
additionally_authenticated_data: Aad<A>,
algorithm: Algorithm,
message: &mut [u8],
receiver_public_key: &PublicKey,
writer: &mut W,
) -> Result<(), PrivateMessageError> {
// Generate ephemeral ed25519 keypair
let (ephemeral_secret_key, ephemeral_public_key) = keypair(Seed32::random()?.as_ref());
// Compute DH exchange (ephemeral_secret_key, receiver_public_key)
// and derive symmetric_key and nonce from shared secret
let (symmetric_key, nonce) = generate_symmetric_key_and_nonce(
algorithm,
&ephemeral_public_key,
SecretKey(ephemeral_secret_key).as_ref(),
&receiver_public_key.as_ref(),
)?;
// write ephemeral_public_key
let _ = writer.write(&ephemeral_public_key)?;
// Encrypt and write message
encrypt_and_write::<A, W>(
symmetric_key,
nonce,
additionally_authenticated_data,
message,
writer,
)?;
Ok(())
}
/// Decrypt private message
/// Return message tag
pub fn decrypt_private_message<A: AsRef<[u8]>, W: Write>(
additionally_authenticated_data: Aad<A>,
algorithm: Algorithm,
encrypted_message: &mut [u8],
receiver_key_pair: &Ed25519KeyPair,
writer: &mut W,
) -> Result<(), PrivateMessageError> {
// Get ephemeral public key
let ephemeral_public_key = &encrypted_message[..EPHEMERAL_PUBLIC_KEY_LEN];
// Get receiver secret key
let (receiver_secret_key, _receiver_public_key) = keypair(receiver_key_pair.seed().as_ref());
// Compute DH exchange (receiver_secret_key, ephemeral_public_key)
// and derive symmetric_key and nonce from shared secret
let (symmetric_key, nonce) = generate_symmetric_key_and_nonce(
algorithm,
&ephemeral_public_key,
SecretKey(receiver_secret_key).as_ref(),
ephemeral_public_key,
)?;
// Decrypt and write decrypted message
decrypt_and_write::<A, W>(
symmetric_key,
nonce,
additionally_authenticated_data,
encrypted_message,
writer,
)?;
Ok(())
}
fn generate_symmetric_key_and_nonce(
algorithm: Algorithm,
ephemeral_public_key: &[u8],
exchange_secret_key: &[u8],
exchange_public_key: &[u8],
) -> Result<(UnboundKey, Nonce), PrivateMessageError> {
let shared_secret = derive(
&exchange(exchange_public_key, exchange_secret_key),
ephemeral_public_key,
);
let symmetric_key = UnboundKey::new(algorithm.to_ring_algo(), &shared_secret.as_ref()[..32])?;
let nonce = Nonce::try_assume_unique_for_key(&shared_secret.as_ref()[32..44])?;
Ok((symmetric_key, nonce))
}
fn derive(seed: &[u8], salt: &[u8]) -> SharedSecret {
let mut shared_secret = SharedSecret::default();
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA384,
NonZeroU32::new(ITERATIONS).expect("ITERATIONS must be > 0"),
salt,
seed,
shared_secret.as_mut(),
);
shared_secret
}
fn encrypt_and_write<A: AsRef<[u8]>, W: Write>(
key: UnboundKey,
nonce: Nonce,
aad: Aad<A>,
message: &mut [u8],
writer: &mut W,
) -> Result<(), PrivateMessageError> {
let key = LessSafeKey::new(key);
let tag = key.seal_in_place_separate_tag(nonce, aad, message)?;
let _ = writer.write(&message)?;
let _ = writer.write(tag.as_ref())?;
Ok(())
}
fn decrypt_and_write<A: AsRef<[u8]>, W: Write>(
key: UnboundKey,
nonce: Nonce,
aad: Aad<A>,
encrypted_message: &mut [u8],
writer: &mut W,
) -> Result<(), PrivateMessageError> {
let key = LessSafeKey::new(key);
//let aad_len = aad.as_ref().len();
let decrypted_message = key.open_in_place(
nonce,
aad,
&mut encrypted_message[EPHEMERAL_PUBLIC_KEY_LEN..],
)?;
//let plain_len = decrypted_message.len() - aad_len;
let _ = writer.write(&decrypted_message[..])?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keys::ed25519::KeyPairFromSeed32Generator;
use crate::keys::KeyPair;
const AAD: &[u8] = b"service name - currency name";
const MESSAGE: &[u8] =
b"Hello, this is a secret message, which can only be read by the recipient.";
#[test]
fn encrypt_same_message_must_be_different() -> Result<(), PrivateMessageError> {
let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
let message = MESSAGE;
let encrypted_message1 = test_encrypt(message, &receiver_key_pair.public_key())?;
let encrypted_message2 = test_encrypt(message, &receiver_key_pair.public_key())?;
assert_ne!(encrypted_message1, encrypted_message2);
assert_ne!(encrypted_message1[32..37], encrypted_message2[32..37]);
Ok(())
}
#[test]
fn encrypt_then_decrypt_with_invalid_aad() -> Result<(), PrivateMessageError> {
let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
let message = MESSAGE;
let mut encrypted_message = test_encrypt(message, &receiver_key_pair.public_key())?;
println!("encrypted message={:?}", encrypted_message);
let mut decrypted_message = Vec::new();
match decrypt_private_message(
Aad::from(b"invalid aad"),
Algorithm::Chacha20Poly1305,
&mut encrypted_message,
&receiver_key_pair,
&mut decrypted_message,
) {
Ok(()) => panic!("Expected error rivateMessageError::Unspecified, found: Ok(())."),
Err(PrivateMessageError::Unspecified) => Ok(()),
Err(e) => panic!(
"Expected error rivateMessageError::Unspecified, found: {:?}.",
e
),
}
}
#[test]
fn encrypt_then_decrypt_with_invalid_algorithm() -> Result<(), PrivateMessageError> {
let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
let message = MESSAGE;
let mut encrypted_message = test_encrypt(message, &receiver_key_pair.public_key())?;
println!("encrypted message={:?}", encrypted_message);
let mut decrypted_message = Vec::new();
match decrypt_private_message(
Aad::from(AAD),
Algorithm::Aes256Gcm,
&mut encrypted_message,
&receiver_key_pair,
&mut decrypted_message,
) {
Ok(()) => panic!("Expected error rivateMessageError::Unspecified, found: Ok(())."),
Err(PrivateMessageError::Unspecified) => Ok(()),
Err(e) => panic!(
"Expected error rivateMessageError::Unspecified, found: {:?}.",
e
),
}
}
#[test]
fn encrypt_and_decrypt_ok() -> Result<(), PrivateMessageError> {
let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
let message = MESSAGE;
let mut encrypted_message = test_encrypt(message, &receiver_key_pair.public_key())?;
println!("encrypted message={:?}", encrypted_message);
let mut decrypted_message = Vec::new();
decrypt_private_message(
Aad::from(AAD),
Algorithm::Chacha20Poly1305,
&mut encrypted_message,
&receiver_key_pair,
&mut decrypted_message,
)?;
println!("decrypted message={:?}", decrypted_message);
assert_eq!(decrypted_message, message);
Ok(())
}
fn test_encrypt(
message: &[u8],
receiver_public_key: &PublicKey,
) -> Result<Vec<u8>, PrivateMessageError> {
let mut encrypted_message = Vec::new();
encrypt_private_message(
Aad::from(AAD),
Algorithm::Chacha20Poly1305,
&mut message.to_owned(),
receiver_public_key,
&mut encrypted_message,
)?;
Ok(encrypted_message)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment