tpfs_krypt 7.1.8

An interface for accessing secrets
Documentation
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());
    // Ideally we would use ring here as well - but ring will not let us use a private key
    // we already own - it will only use ones it generates on the fly.
    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>> {
    // Get the key into the correct form
    let key = UnboundKey::new(&CHACHA20_POLY1305, key)?;
    let key = LessSafeKey::new(key);

    // Ring uses the same input variable as output
    let mut in_out = msg.to_owned();

    // Generate the nonce
    let mut nonce_bytes = [0u8; NONCE_LEN];
    rng.try_fill_bytes(&mut nonce_bytes)?;
    let nonce = Nonce::assume_unique_for_key(nonce_bytes);

    // Encrypt data into in_out variable
    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>>> {
    // Get the key into the correct form
    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)?;

    // Truncate off the extra stuff
    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);
    }
}