From aafb7077691b497c36db92ecc226fd6823487f20 Mon Sep 17 00:00:00 2001 From: jrconlin Date: Tue, 30 Apr 2019 13:09:52 -0700 Subject: [PATCH] feat: Add convenience functions for encrypt/decrypt Closes #25 --- Cargo.toml | 7 +++--- src/lib.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0342df1..88e4465 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ece" -version = "0.1.3" +version = "0.2.0" authors = ["Edouard Oger ", "JR Conlin "] license = "MPL-2.0" edition = "2018" @@ -13,14 +13,13 @@ byteorder = "1.3" failure = "0.1" failure_derive = "0.1" base64 = "0.10" +hex = "0.3" hkdf = { version = "0.7", optional = true } lazy_static = { version = "1.2", optional = true } openssl = { version = "0.10", optional = true } +rand = "0.6" sha2 = { version = "0.8", optional = true } -[dev-dependencies] -hex = "0.3" - [features] default = ["backend-openssl"] backend-openssl = ["openssl", "lazy_static", "hkdf", "sha2"] diff --git a/src/lib.rs b/src/lib.rs index 09e0686..f8cebbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,10 @@ mod crypto_backend; mod crypto_backends; mod error; +use hex; +use rand; +use rand::Rng; + pub use crate::{ aes128gcm::Aes128GcmEceWebPush, aesgcm::{AesGcmEceWebPush, AesGcmEncryptedBlock}, @@ -21,6 +25,7 @@ pub type Aes128GcmEceWebPushImpl = aes128gcm::Aes128GcmEceWebPush; pub use crate::crypto_backends::{CryptoImpl, LocalKeyPairImpl, RemoteKeyPairImpl}; +/// Generate a local ECE key pair and auth nonce. pub fn generate_keypair_and_auth_secret( ) -> Result<(LocalKeyPairImpl, [u8; ECE_WEBPUSH_AUTH_SECRET_LENGTH])> { let local_key_pair = LocalKeyPairImpl::generate_random()?; @@ -29,6 +34,44 @@ pub fn generate_keypair_and_auth_secret( Ok((local_key_pair, auth_secret)) } +/// Encrypt a block using default AES128GCM encoding. +/// +/// param remote_pub &str - The remote public key in hex encoded format +/// param remote_auth &str - The remote authorization token in hex encoded format +/// param salt &[u8] - The locally generated random salt +/// param data &[u8] - The data to encrypt +/// +pub fn encrypt(remote_pub: &str, remote_auth: &str, salt: &[u8], data: &[u8]) -> Result> { + let remote_decode = hex::decode(remote_pub).map_err(|_| error::ErrorKind::DecodeError)?; + let remote_key = crypto_backends::CryptoImpl::public_key_from_raw(remote_decode.as_slice())?; + let local_key = LocalKeyPairImpl::generate_random()?; + let auth_secret = hex::decode(remote_auth).map_err(|_| error::ErrorKind::DecodeError)?; + // TODO: randomize pad. + let mut rng = rand::thread_rng(); + let pad = rng.gen_range(1, 4096 - data.len()); + let params = WebPushParams::new(4096, pad, Vec::from(salt)); + Aes128GcmEceWebPushImpl::encrypt_with_keys( + &local_key, + &remote_key, + &auth_secret.as_slice(), + data, + params, + ) +} + +/// Decrypt a block using default AES128GCM encoding. +/// +/// param local_priv &str - The locally generated Private key as a raw, hex encoded string +/// param auth &str - The locally generated auth token as a hex encoded string (this value was shared with the encryptor) +/// param data &[u8] - The encrypted data block +/// +pub fn decrypt(local_priv: &str, auth: &str, data: &[u8]) -> Result> { + let priv_key = hex::decode(local_priv).unwrap(); + let priv_key = LocalKeyPairImpl::new(priv_key.as_slice()).unwrap(); + let auth_secret = hex::decode(auth).unwrap(); + Aes128GcmEceWebPushImpl::decrypt(&priv_key, &auth_secret.as_slice(), data) +} + #[cfg(test)] mod aes128gcm_tests { extern crate hex; @@ -70,11 +113,7 @@ mod aes128gcm_tests { } fn try_decrypt(priv_key: &str, auth_secret: &str, payload: &str) -> Result { - let priv_key = hex::decode(priv_key).unwrap(); - let priv_key = LocalKeyPairImpl::new(&priv_key)?; - let auth_secret = hex::decode(auth_secret).unwrap(); - let payload = hex::decode(payload).unwrap(); - let plaintext = Aes128GcmEceWebPushImpl::decrypt(&priv_key, &auth_secret, &payload)?; + let plaintext = decrypt(priv_key, auth_secret, &hex::decode(payload).unwrap())?; Ok(String::from_utf8(plaintext).unwrap()) } @@ -100,6 +139,25 @@ mod aes128gcm_tests { assert_eq!(decrypted, plaintext); } + #[test] + fn test_conv_fn() -> Result<()> { + let (local_key, auth) = generate_keypair_and_auth_secret()?; + let hex_auth = hex::encode(auth); + let plaintext = b"Mary had a little lamb, with some nice mint jelly"; + let mut salt = vec![0u8; 16]; + CryptoImpl::random(&mut salt)?; + let encoded = encrypt( + &hex::encode(local_key.pub_as_raw()?), + &hex_auth, + &salt, + plaintext, + ) + .unwrap(); + let decoded = decrypt(&hex::encode(local_key.to_raw()), &hex_auth, &encoded)?; + assert_eq!(decoded, plaintext.to_vec()); + Ok(()) + } + #[test] fn try_encrypt_ietf_rfc() { let ciphertext = try_encrypt(