use crate::errors::Result;
use rand::{CryptoRng, RngCore};
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, CHACHA20_POLY1305, MAX_TAG_LEN, NONCE_LEN};
use secrecy::{ExposeSecret, Secret};
use x25519_dalek::{SharedSecret, StaticSecret};
pub(crate) mod x25519;
#[cfg(test)]
mod unit_tests;
type PublicKey = x25519_dalek::PublicKey;
type PrivateKey = [u8; 32];
pub(crate) fn create_shared_secret(
public_key: &PublicKey,
private_key: &Secret<PrivateKey>,
) -> Secret<SharedSecret> {
let secret: Secret<StaticSecret> = Secret::new(private_key.expose_secret().to_owned().into());
Secret::new(secret.expose_secret().diffie_hellman(public_key))
}
pub(crate) fn encrypt_with_shared_secret<R: CryptoRng + RngCore>(
key: &[u8; 32],
msg: &[u8],
rng: &mut R,
) -> Result<Vec<u8>> {
let key = UnboundKey::new(&CHACHA20_POLY1305, key)?;
let key = LessSafeKey::new(key);
let mut in_out = msg.to_owned();
let mut nonce_bytes = [0u8; NONCE_LEN];
rng.try_fill_bytes(&mut nonce_bytes)?;
let nonce = Nonce::assume_unique_for_key(nonce_bytes);
key.seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)?;
let mut output = Vec::with_capacity(NONCE_LEN + in_out.len());
output.extend(nonce_bytes);
output.extend(&in_out);
Ok(output)
}
pub(crate) fn decrypt_with_shared_secret(
key: &[u8; 32],
cipher_text: &[u8],
) -> Result<Secret<Vec<u8>>> {
let key = UnboundKey::new(&CHACHA20_POLY1305, key)?;
let key = LessSafeKey::new(key);
let nonce = cipher_text.get(..NONCE_LEN).unwrap_or(&[]);
let mut in_out = cipher_text.get(NONCE_LEN..).unwrap_or(&[]).to_owned();
let nonce = Nonce::try_assume_unique_for_key(nonce)?;
key.open_in_place(nonce, Aad::empty(), &mut in_out)?;
let plain_len = cipher_text.len() - NONCE_LEN - MAX_TAG_LEN;
Ok(Secret::new(in_out[..plain_len].to_owned()))
}
#[cfg(test)]
mod test {
use super::*;
use crate::crypto::{shared_encryption::x25519::SharedEncryptionX25519KeyPair, RawKeyPair};
use rand::rngs::OsRng;
#[test]
fn deterministic_example_derive_from_shared() {
let mut rng = OsRng::default();
let sender_static_key = SharedEncryptionX25519KeyPair::generate().unwrap();
let receiver_static_key = SharedEncryptionX25519KeyPair::generate().unwrap();
let encrypt_me = b"I am so very secret";
let public_info = b"some public part of the transaction";
let secret = Secret::new(sender_static_key.static_secret.to_bytes());
let hashables: Vec<&[u8]> = vec![secret.expose_secret().as_ref(), public_info];
let sender_derived = SharedEncryptionX25519KeyPair::from_hashables(hashables);
let shared_from_sender_perspective = create_shared_secret(
&receiver_static_key.public().key,
&Secret::new(sender_derived.static_secret.to_bytes()),
);
let shared_from_receiver_perspective = create_shared_secret(
&sender_derived.public().key,
&Secret::new(receiver_static_key.static_secret.to_bytes()),
);
assert_eq!(
shared_from_sender_perspective.expose_secret().as_bytes(),
shared_from_receiver_perspective.expose_secret().as_bytes()
);
let hashables: Vec<&[u8]> = vec![shared_from_sender_perspective.expose_secret().as_bytes()];
let shared_derived = SharedEncryptionX25519KeyPair::from_hashables(hashables);
let encrypted = encrypt_with_shared_secret(
&shared_derived.static_secret.to_bytes(),
encrypt_me,
&mut rng,
)
.unwrap();
let decrypted_with_sender_shared =
decrypt_with_shared_secret(&shared_derived.static_secret.to_bytes(), &encrypted)
.unwrap();
assert_eq!(&decrypted_with_sender_shared.expose_secret(), &encrypt_me);
let hashables: Vec<&[u8]> =
vec![shared_from_receiver_perspective.expose_secret().as_bytes()];
let shared_derived = SharedEncryptionX25519KeyPair::from_hashables(hashables);
let decrypted_with_receiver_shared =
decrypt_with_shared_secret(&shared_derived.static_secret.to_bytes(), &encrypted)
.unwrap();
assert_eq!(&decrypted_with_receiver_shared.expose_secret(), &encrypt_me);
}
}