From cb95ab609a545a9877db6a26a440373d02bbdb01 Mon Sep 17 00:00:00 2001 From: Evan Feenstra Date: Fri, 1 Jul 2022 09:36:20 -0700 Subject: [PATCH] ecdh and chachapoly tests --- crypter/Cargo.toml | 9 ++++++ crypter/src/chacha.rs | 56 +++++++++++++++++++++++++++++++++++++ crypter/src/ecdh.rs | 47 +++++++++++++++++++++++++++++++ crypter/src/lib.rs | 65 ++++++++++++++++++++++--------------------- 4 files changed, 146 insertions(+), 31 deletions(-) create mode 100644 crypter/src/chacha.rs create mode 100644 crypter/src/ecdh.rs diff --git a/crypter/Cargo.toml b/crypter/Cargo.toml index 2d2b463..e21c893 100644 --- a/crypter/Cargo.toml +++ b/crypter/Cargo.toml @@ -4,11 +4,20 @@ version = "0.1.0" authors = ["Evan Feenstra "] edition = "2018" +[dependencies] +rand = "0.8" +anyhow = {version = "1", features = ["backtrace"]} +log = "0.4" +base64 = { version = "0.13.0" } +secp256k1 = { version = "0.22.0", features = ["std", "rand-std"] } + [dependencies.lightning] version = "0.0.108" default-features = false features = ["std", "grind_signatures"] [patch.crates-io] +getrandom = { version = "0.2", git = "https://github.com/esp-rs-compat/getrandom.git" } +secp256k1 = { git = "https://github.com/Evanfeenstra/rust-secp256k1", branch = "v0.22.0-new-rand" } lightning = { git = "https://github.com/Evanfeenstra/rust-lightning", branch = "v0.0.108-branch" } diff --git a/crypter/src/chacha.rs b/crypter/src/chacha.rs new file mode 100644 index 0000000..d0f0798 --- /dev/null +++ b/crypter/src/chacha.rs @@ -0,0 +1,56 @@ +use lightning::util::chacha20poly1305rfc::ChaCha20Poly1305RFC; +use anyhow::anyhow; + +pub const MSG_LEN: usize = 32; +pub const KEY_LEN: usize = 32; +pub const NONCE_END_LEN: usize = 8; +const TAG_LEN: usize = 16; +const CIPHER_LEN: usize = MSG_LEN + NONCE_END_LEN + TAG_LEN; + +pub fn encrypt(plaintext: [u8; MSG_LEN], key: [u8; KEY_LEN], nonce_end: [u8; NONCE_END_LEN]) -> anyhow::Result<[u8; CIPHER_LEN]> { + let mut nonce = [0; 4 + NONCE_END_LEN]; + nonce[4..].copy_from_slice(&nonce_end); + let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]); + let mut res = [0; MSG_LEN]; + let mut tag = [0; TAG_LEN]; + chacha.encrypt(&plaintext[..], &mut res[0..plaintext.len()], &mut tag); + let mut ret = [0; CIPHER_LEN]; + ret[..MSG_LEN].copy_from_slice(&res); + ret[MSG_LEN..MSG_LEN + NONCE_END_LEN].copy_from_slice(&nonce_end); + ret[MSG_LEN + NONCE_END_LEN..].copy_from_slice(&tag); + Ok(ret) +} + +pub fn decrypt(ciphertext: [u8; CIPHER_LEN], key: [u8; KEY_LEN]) -> anyhow::Result<[u8; MSG_LEN]> { + let mut nonce = [0; 4 + NONCE_END_LEN]; + nonce[4..].copy_from_slice(&ciphertext[MSG_LEN..MSG_LEN + NONCE_END_LEN]); + let mut tag = [0; TAG_LEN]; + tag.copy_from_slice(&ciphertext[MSG_LEN + NONCE_END_LEN..]); + let mut chacha2 = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]); + let mut dec = [0; MSG_LEN]; + let ok = chacha2.decrypt(&ciphertext[..MSG_LEN], &mut dec, &tag); + if ok { + Ok(dec) + } else { + Err(anyhow!("failed chacha authentication")) + } +} + +#[cfg(test)] +mod tests { + use crate::chacha::{decrypt, encrypt, MSG_LEN, KEY_LEN, NONCE_END_LEN}; + use rand::{rngs::OsRng, RngCore}; + + #[test] + fn test_chacha() -> anyhow::Result<()> { + let key = [9; KEY_LEN]; + let plaintext = [1; MSG_LEN]; + let mut nonce_end = [0; NONCE_END_LEN]; + OsRng.fill_bytes(&mut nonce_end); + let cipher = encrypt(plaintext, key, nonce_end)?; + let plain = decrypt(cipher, key)?; + assert_eq!(plaintext, plain); + Ok(()) + } + +} \ No newline at end of file diff --git a/crypter/src/ecdh.rs b/crypter/src/ecdh.rs new file mode 100644 index 0000000..8d998c1 --- /dev/null +++ b/crypter/src/ecdh.rs @@ -0,0 +1,47 @@ +use secp256k1::ecdh::SharedSecret; +use secp256k1::{SecretKey, PublicKey}; +use anyhow::Result; + +const PUBLIC_KEY_LEN: usize = 33; +const PRIVATE_KEY_LEN: usize = 32; +const SECRET_LEN: usize = 32; + +pub fn derive_shared_secret_from_slice(their_public_key: [u8; PUBLIC_KEY_LEN], my_private_key: [u8; PRIVATE_KEY_LEN]) -> Result<[u8; SECRET_LEN]> { + let public_key = PublicKey::from_slice(&their_public_key[..])?; + let private_key = SecretKey::from_slice(&my_private_key[..])?; + Ok(derive_shared_secret(&public_key, &private_key).secret_bytes()) +} + +pub fn derive_shared_secret(their_public_key: &PublicKey, my_private_key: &SecretKey) -> SharedSecret { + SharedSecret::new(their_public_key, my_private_key) +} + +#[cfg(test)] +mod tests { + use crate::ecdh::{derive_shared_secret, derive_shared_secret_from_slice}; + use rand::thread_rng; + use secp256k1::Secp256k1; + + #[test] + fn test_ecdh() -> anyhow::Result<()> { + let s = Secp256k1::new(); + let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); + let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); + let sec1 = derive_shared_secret(&pk2, &sk1); + let sec2 = derive_shared_secret(&pk1, &sk2); + assert_eq!(sec1, sec2); + Ok(()) + } + + #[test] + fn test_ecdh_from_slice() -> anyhow::Result<()> { + let s = Secp256k1::new(); + let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); + let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); + let sec1 = derive_shared_secret_from_slice(pk2.serialize(), sk1.secret_bytes())?; + let sec2 = derive_shared_secret_from_slice(pk1.serialize(), sk2.secret_bytes())?; + assert_eq!(sec1, sec2); + Ok(()) + } + +} \ No newline at end of file diff --git a/crypter/src/lib.rs b/crypter/src/lib.rs index 1de86e6..5e2ca49 100644 --- a/crypter/src/lib.rs +++ b/crypter/src/lib.rs @@ -1,39 +1,42 @@ -use lightning::util::chacha20poly1305rfc::ChaCha20Poly1305RFC; +pub mod chacha; +pub mod ecdh; -pub fn test_chacha20poly1305() { - - let key = [0; 32]; - // 32 bytes key - // 12 byte nonce - let n: u64 = 123456; - let mut nonce = [0; 12]; - println!("chacha1"); - nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); - println!("chacha2"); - let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]); - println!("chacha3"); - let mut tag = [0; 16]; - let plaintext = b"plaintext"; - let mut res = [0; 50]; - chacha.encrypt(plaintext, &mut res[0..plaintext.len()], &mut tag); - println!("chacha4 {:?}", res); - println!("tag {:?}", tag); - - let mut chacha2 = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]); - let mut dec = [0; 9]; - let ok = chacha2.decrypt(&res[0..9], &mut dec, &tag); - - println!("ok {}", ok); - println!("dec {:?}", dec); - println!("decrypted: {}", String::from_utf8_lossy(&dec[..])); -} #[cfg(test)] mod tests { - use crate::test_chacha20poly1305; + use crate::chacha::{decrypt, encrypt, MSG_LEN, NONCE_END_LEN}; + use crate::ecdh::derive_shared_secret_from_slice; + use rand::{rngs::OsRng, RngCore, thread_rng}; + use secp256k1::Secp256k1; #[test] - fn find_solution_1_btc() { - test_chacha20poly1305(); + fn test_crypter() -> anyhow::Result<()> { + // two keypairs + let s = Secp256k1::new(); + let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); + let (sk2, pk2) = s.generate_keypair(&mut thread_rng()); + + // derive shared secrets + let sec1 = derive_shared_secret_from_slice( + pk2.serialize(), sk1.secret_bytes() + )?; + let sec2 = derive_shared_secret_from_slice( + pk1.serialize(), sk2.secret_bytes() + )?; + assert_eq!(sec1, sec2); + + // encrypt plaintext with sec1 + let plaintext = [1; MSG_LEN]; + let mut nonce_end = [0; NONCE_END_LEN]; + OsRng.fill_bytes(&mut nonce_end); + let cipher = encrypt(plaintext, sec1, nonce_end)?; + + // decrypt with sec2 + let plain = decrypt(cipher, sec2)?; + assert_eq!(plaintext, plain); + + println!("PLAINTEXT MATCHES!"); + Ok(()) } + } \ No newline at end of file