From 90f123cfc7a39d110e8a60d130478bd63a10d007 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 19 Jun 2023 13:40:00 +1000 Subject: [PATCH 01/64] First pass: bump base64, clap and other deps. Next we need to substitute rust-crypto: https://rustsec.org/advisories/RUSTSEC-2016-0005.html Also coming from unmerged PR https://github.com/DaGenix/rust-crypto/pull/467 This crate change is required to be Crypt4GH spec-compliant (see section 5.1). --- Cargo.lock | 271 ++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 20 ++-- src/bin.rs | 2 +- src/cli.rs | 4 +- src/keys.rs | 13 ++- 5 files changed, 197 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb4a371..7bcba2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,28 +11,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "base64" -version = "0.13.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bincode" @@ -49,6 +32,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" + [[package]] name = "cc" version = "1.0.73" @@ -63,26 +52,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.14" +version = "4.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535434c063ced786eb04aaf529308092c5ab60889e8fe24275d15de07b01fa97" +checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" dependencies = [ - "atty", - "bitflags", + "bitflags 2.3.2", "clap_derive", "clap_lex", - "indexmap", - "lazy_static", + "is-terminal", + "once_cell", "strsim", "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.1.7" +version = "4.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" dependencies = [ "heck", "proc-macro-error", @@ -93,9 +80,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" dependencies = [ "os_str_bytes", ] @@ -138,17 +125,38 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -172,12 +180,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - [[package]] name = "heck" version = "0.4.0" @@ -186,30 +188,37 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "humantime" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "quick-error", + "hermit-abi", + "libc", + "windows-sys", ] [[package]] -name = "indexmap" -version = "1.8.1" +name = "is-terminal" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "autocfg", - "hashbrown", + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", ] [[package]] @@ -221,12 +230,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - [[package]] name = "lazy_static" version = "1.4.0" @@ -235,9 +238,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libsodium-sys" @@ -251,6 +254,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "log" version = "0.4.16" @@ -266,6 +275,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "os_str_bytes" version = "6.0.0" @@ -286,9 +301,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty_env_logger" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ "env_logger", "log", @@ -320,19 +335,13 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ - "unicode-xid", + "unicode-ident", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.18" @@ -438,13 +447,22 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rpassword" -version = "6.0.1" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" dependencies = [ "libc", - "serde", - "serde_json", "winapi", ] @@ -468,10 +486,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" [[package]] -name = "ryu" -version = "1.0.9" +name = "rustix" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] [[package]] name = "same-file" @@ -502,17 +528,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f972498cf015f7c0746cac89ebe1d6ef10c293b94175a243a2d9442c163d9944" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "signature" version = "1.5.0" @@ -557,12 +572,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thiserror" version = "1.0.31" @@ -593,6 +602,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -652,3 +667,69 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index 0c5e4a2..3537af1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,19 +20,19 @@ name = "crypt4gh" path = "src/bin.rs" [dependencies] -clap = { version = "3.1", features = ["derive", "env"] } +clap = { version = "4", features = ["derive", "env"] } regex = "1.5" -rpassword = "6.0" -sodiumoxide = "0.2.7" -base64 = "0.13.0" -lazy_static = "1.4.0" -rust-crypto = "0.2.36" -bincode = "1.3.3" -serde = { version = "1.0", features = ["derive"] } +rpassword = "7" +sodiumoxide = "0.2" +base64 = "0.21" +lazy_static = "1.4" +rust-crypto = "0.2" +bincode = "1" +serde = { version = "1", features = ["derive"] } log = "0.4" -pretty_env_logger = "0.4" +pretty_env_logger = "0.5" thiserror = "1.0" -libsodium-sys = "0.2.7" +libsodium-sys = "0.2" itertools = "0.10" [dev-dependencies] diff --git a/src/bin.rs b/src/bin.rs index 8974a3b..f5b1b2b 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -15,7 +15,7 @@ use std::io; use std::io::stdin; use std::path::{Path, PathBuf}; -use clap::StructOpt; +use clap::Parser; use cli::{Args, Command}; use crypt4gh::error::Crypt4GHError; use crypt4gh::keys::{get_private_key, get_public_key}; diff --git a/src/cli.rs b/src/cli.rs index 0c92c2a..0fd5782 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -11,7 +11,7 @@ pub enum Command { sk: Option, /// Recipient's Curve25519-based Public key - #[clap(long = "recipient_pk", multiple_values = true)] + #[clap(long = "recipient_pk", num_args = 1..)] recipient_pk: Vec, /// Byte-range either as or just (Start included, End excluded) @@ -49,7 +49,7 @@ pub enum Command { sk: Option, /// Recipient's Curve25519-based Public key - #[clap(long = "recipient_pk", multiple_values = true)] + #[clap(long = "recipient_pk", num_args = 1..)] recipient_pk: Vec, /// Keep only header packets that you can decrypt diff --git a/src/keys.rs b/src/keys.rs index 12f6346..1018a13 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -7,6 +7,9 @@ use std::io::{BufRead, BufReader, Cursor, Read, Write}; use std::path::Path; use std::sync::Once; +use base64::engine::general_purpose; +use base64::Engine; + use crypto::blockmodes::NoPadding; use crypto::buffer::{RefReadBuffer, RefWriteBuffer}; use crypto::scrypt::ScryptParams; @@ -77,7 +80,7 @@ fn load_from_pem(filepath: &Path) -> Result, Crypt4GHError> { ); // Decode with base64 - base64::decode(&lines[1..lines.len() - 1].join("")).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) + general_purpose::STANDARD.decode(&lines[1..lines.len() - 1].join("")).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) } fn decode_string_ssh(stream: &mut impl BufRead) -> Result, Crypt4GHError> { @@ -441,7 +444,7 @@ pub fn get_public_key(key_path: &Path) -> Result, Crypt4GHError> { // CRYPT4GH key else if lines_vec[0].contains("CRYPT4GH") { log::info!("Loading a Crypt4GH public key"); - base64::decode(&lines_vec[1]).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) + general_purpose::STANDARD.decode(&lines_vec[1]).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) } // SSH key else if lines_vec[0].len() >= 4 && lines_vec[0].get(0..4).unwrap() == "ssh-" { @@ -465,7 +468,7 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { return Err(Crypt4GHError::InvalidSSHKey); } - let pkey = base64::decode( + let pkey = general_purpose::STANDARD.decode( line[12..] .split(' ') .take(1) @@ -551,7 +554,7 @@ pub fn generate_keys( let (sk, pk) = skpk.split_at(32); log::debug!("Public Key: {:02x?}", pk.iter().format("")); pk_file.write_all(b"-----BEGIN CRYPT4GH PUBLIC KEY-----\n").unwrap(); - pk_file.write_all(base64::encode(pk).as_bytes()).unwrap(); + pk_file.write_all(general_purpose::STANDARD.encode(pk).as_bytes()).unwrap(); pk_file.write_all(b"\n-----END CRYPT4GH PUBLIC KEY-----\n").unwrap(); // Secret key file open @@ -566,7 +569,7 @@ pub fn generate_keys( sk.iter().format("") ); sk_file.write_all(b"-----BEGIN CRYPT4GH PRIVATE KEY-----\n").unwrap(); - sk_file.write_all(base64::encode(sk_encrypted).as_bytes()).unwrap(); + sk_file.write_all(general_purpose::STANDARD.encode(sk_encrypted).as_bytes()).unwrap(); sk_file.write_all(b"\n-----END CRYPT4GH PRIVATE KEY-----\n").unwrap(); // Secret key file permissions (read only) From ad146adfe7e5020871a49896a25d5e0e869471ad Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 19 Jun 2023 15:07:34 +1000 Subject: [PATCH 02/64] Fixed most of the decoupled RustCrypto crates, aes ctr (a.k.a counting) and other ciphers need to be determined on the new crate structure, also fix error management for invalid scrypt parameters and review dklen --- Cargo.toml | 9 ++++++++- src/error.rs | 5 ++++- src/keys.rs | 26 ++++++++++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3537af1..f5d6210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,14 @@ rpassword = "7" sodiumoxide = "0.2" base64 = "0.21" lazy_static = "1.4" -rust-crypto = "0.2" +chacha20poly1305 = "0.10" +block-padding = "0.3" +scrypt = "0.11" +cipher = "0.4" +bcrypt-pbkdf = "0.10" +aes = "0.8" +aes-gcm = "0.10" +block-buffer = "0.10" bincode = "1" serde = { version = "1", features = ["derive"] } log = "0.4" diff --git a/src/error.rs b/src/error.rs index 046cc73..ad4534c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,8 @@ use std::error::Error; use std::path::PathBuf; -use crypto::symmetriccipher::SymmetricCipherError; +use scrypt::errors::InvalidParams; + use thiserror::Error; #[derive(Debug, Error)] @@ -65,6 +66,8 @@ pub enum Crypt4GHError { BadSharedKey, #[error("Invalid Peer's Public Key")] InvalidPeerPubPkey, + #[error("Invalid paramenters passed to Scrypt")] + ScryptParamsError(InvalidParams), // Reading errors #[error("Unable to read {0} bytes from input (ERROR = {1:?})")] diff --git a/src/keys.rs b/src/keys.rs index 1018a13..c5c6230 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -3,17 +3,17 @@ use std::collections::HashMap; use std::fs::File; -use std::io::{BufRead, BufReader, Cursor, Read, Write}; +use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; use std::path::Path; use std::sync::Once; use base64::engine::general_purpose; use base64::Engine; -use crypto::blockmodes::NoPadding; -use crypto::buffer::{RefReadBuffer, RefWriteBuffer}; -use crypto::scrypt::ScryptParams; -use crypto::symmetriccipher::Decryptor; +use block_padding::NoPadding; +use scrypt::Params as ScryptParams; +use bcrypt_pbkdf; +use aes; use itertools::Itertools; use lazy_static::lazy_static; use sodiumoxide::crypto::aead::chacha20poly1305_ietf; @@ -120,19 +120,21 @@ fn derive_key( match alg { "scrypt" => { - let params = ScryptParams::new(14, 8, 1); - crypto::scrypt::scrypt( + // TODO: Review last param of ScryptParams (length of what, exactly?) carefully. + // Added "dklen" for now since it seemed fitting, but needs proper review. + let params = ScryptParams::new(14, 8, 1, dklen); + scrypt::scrypt( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); vec![0_u8; 0] }), - ¶ms, + ¶ms.unwrap(), //TODO: .map_err() this to GA4GHError &mut output, ); }, "bcrypt" => { - crypto::bcrypt_pbkdf::bcrypt_pbkdf( + bcrypt_pbkdf::bcrypt_pbkdf( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); @@ -317,14 +319,14 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< log::debug!("IV ({}): {:02x?}", iv.len(), iv.iter().format("")); let mut output = vec![0_u8; private_ciphertext.len()]; - let mut reader = RefReadBuffer::new(private_ciphertext); - let mut writer = RefWriteBuffer::new(&mut output); + let mut reader = BufReader::new(private_ciphertext); + let mut writer = BufWriter::new(&mut output); assert!((private_ciphertext.len() % block_size(ciphername)?) == 0); // Decipher match ciphername { - "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) + "aes128-ctr" => aes::Aes128(aes::Aes128, key, iv) .decrypt(&mut reader, &mut writer, true) .map_err(Crypt4GHError::DecryptKeyError)?, "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) From 0b575e1e7198376da00f4e6dc8539abc56c9ffec Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 19 Jun 2023 15:07:52 +1000 Subject: [PATCH 03/64] Cargo.lock --- Cargo.lock | 404 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 316 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7bcba2b..78a8213 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -17,6 +52,23 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2", +] + [[package]] name = "bincode" version = "1.3.3" @@ -38,6 +90,40 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.73" @@ -50,6 +136,41 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clap" version = "4.1.11" @@ -87,27 +208,74 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + [[package]] name = "crypt4gh" version = "0.4.1" dependencies = [ + "aes", + "aes-gcm", "base64", + "bcrypt-pbkdf", "bincode", + "block-buffer", + "block-padding", + "chacha20poly1305", + "cipher", "clap", "itertools", "lazy_static", "libsodium-sys", "log", "pretty_env_logger", - "rand 0.8.5", + "rand", "regex", "rpassword", - "rust-crypto", + "scrypt", "serde", "sodiumoxide", "thiserror", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + [[package]] name = "ed25519" version = "1.4.1" @@ -158,16 +326,14 @@ dependencies = [ ] [[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] [[package]] name = "getrandom" @@ -180,6 +346,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "heck" version = "0.4.0" @@ -192,12 +368,30 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -281,18 +475,68 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "pkg-config" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -351,29 +595,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -dependencies = [ - "libc", - "rand 0.4.6", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -382,7 +603,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.3", + "rand_core", ] [[package]] @@ -392,42 +613,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", + "rand_core", ] [[package]] name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "regex" version = "1.5.5" @@ -466,25 +663,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rust-crypto" -version = "0.2.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" -dependencies = [ - "gcc", - "libc", - "rand 0.3.23", - "rustc-serialize", - "time", -] - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" - [[package]] name = "rustix" version = "0.37.20" @@ -499,6 +677,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -508,6 +695,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "serde" version = "1.0.137" @@ -528,6 +727,17 @@ dependencies = [ "syn", ] +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signature" version = "1.5.0" @@ -552,6 +762,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.92" @@ -593,14 +809,10 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.1.43" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" @@ -614,6 +826,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "version_check" version = "0.9.4" @@ -733,3 +955,9 @@ name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" From 547b764d7b68c59230dd05850d3024cc37eab71a Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 20 Jun 2023 13:46:12 +1000 Subject: [PATCH 04/64] Continue removing sodium oxide and matching APIs towards RustCrypto crate ecosystem... wip --- Cargo.lock | 106 +------------------------------------------------- Cargo.toml | 5 +-- src/header.rs | 25 ++++++------ src/keys.rs | 7 +++- src/lib.rs | 16 +------- 5 files changed, 25 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78a8213..f8c7d3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,20 +23,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -221,8 +207,8 @@ dependencies = [ name = "crypt4gh" version = "0.4.1" dependencies = [ + "aead", "aes", - "aes-gcm", "base64", "bcrypt-pbkdf", "bincode", @@ -231,9 +217,9 @@ dependencies = [ "chacha20poly1305", "cipher", "clap", + "ctr", "itertools", "lazy_static", - "libsodium-sys", "log", "pretty_env_logger", "rand", @@ -241,7 +227,6 @@ dependencies = [ "rpassword", "scrypt", "serde", - "sodiumoxide", "thiserror", ] @@ -276,15 +261,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ed25519" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4" -dependencies = [ - "signature", -] - [[package]] name = "either" version = "1.6.1" @@ -346,16 +322,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "heck" version = "0.4.0" @@ -436,18 +402,6 @@ version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" -[[package]] -name = "libsodium-sys" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" -dependencies = [ - "cc", - "libc", - "pkg-config", - "walkdir", -] - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -508,12 +462,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - [[package]] name = "poly1305" version = "0.8.0" @@ -525,18 +473,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.16" @@ -686,15 +622,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scrypt" version = "0.11.0" @@ -738,24 +665,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signature" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" - -[[package]] -name = "sodiumoxide" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028" -dependencies = [ - "ed25519", - "libc", - "libsodium-sys", - "serde", -] - [[package]] name = "strsim" version = "0.10.0" @@ -842,17 +751,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index f5d6210..167e4d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ path = "src/bin.rs" clap = { version = "4", features = ["derive", "env"] } regex = "1.5" rpassword = "7" -sodiumoxide = "0.2" base64 = "0.21" lazy_static = "1.4" chacha20poly1305 = "0.10" @@ -32,14 +31,14 @@ scrypt = "0.11" cipher = "0.4" bcrypt-pbkdf = "0.10" aes = "0.8" -aes-gcm = "0.10" +ctr = "0.9" block-buffer = "0.10" +aead = { version = "0.5" } bincode = "1" serde = { version = "1", features = ["derive"] } log = "0.4" pretty_env_logger = "0.5" thiserror = "1.0" -libsodium-sys = "0.2" itertools = "0.10" [dev-dependencies] diff --git a/src/header.rs b/src/header.rs index f71c15a..bc22db3 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,10 +1,11 @@ use std::collections::HashSet; +use aead::{KeyInit, AeadInPlace}; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use sodiumoxide::crypto::aead::chacha20poly1305_ietf; -use sodiumoxide::crypto::kx::{x25519blake2b, PublicKey, SecretKey}; -use sodiumoxide::randombytes; +use chacha20poly1305::ChaCha20Poly1305; +// use sodiumoxide::crypto::kx::{x25519blake2b, PublicKey, SecretKey}; +// use sodiumoxide::randombytes; use super::SEGMENT_SIZE; use crate::error::Crypt4GHError; @@ -71,8 +72,8 @@ fn encrypt_x25519_chacha20_poly1305( seckey: &[u8], recipient_pubkey: &[u8], ) -> Result, Crypt4GHError> { - crate::init(); let pubkey = get_public_key_from_private_key(seckey)?; + let mut buffer = vec![]; // TODO: Check AEAD Buffer traits instead of this // Log log::debug!(" packed data({}): {:02x?}", data.len(), data.iter().format("")); @@ -98,13 +99,15 @@ fn encrypt_x25519_chacha20_poly1305( // Nonce & chacha20 key let nonce = - chacha20poly1305_ietf::Nonce::from_slice(&randombytes::randombytes(12)).ok_or(Crypt4GHError::NoRandomNonce)?; - let key = chacha20poly1305_ietf::Key::from_slice(shared_key.as_ref()).ok_or(Crypt4GHError::BadKey)?; + chacha20poly1305::Nonce::from_slice(&randombytes::randombytes(12)); + let key = chacha20poly1305::Key::from_slice(shared_key.as_ref()); + let cipher = ChaCha20Poly1305::new(key) + .encrypt_in_place_detached(nonce, data, &mut buffer); Ok(vec![ pubkey, - nonce.0.to_vec(), - chacha20poly1305_ietf::seal(data, None, &nonce, &key), + nonce.to_vec(), + buffer ] .concat()) } @@ -199,7 +202,7 @@ fn decrypt_x25519_chacha20_poly1305( return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = sodiumoxide::crypto::aead::chacha20poly1305_ietf::Nonce::from_slice(&encrypted_part[32..44]) + let nonce = ChaCha20Poly1305::Nonce::from_slice(&encrypted_part[32..44]) .ok_or(Crypt4GHError::NoNonce)?; let packet_data = &encrypted_part[44..]; @@ -221,8 +224,8 @@ fn decrypt_x25519_chacha20_poly1305( log::debug!("shared key: {:02x?}", shared_key.0.iter().format("")); // Chacha20_Poly1305 - let key = chacha20poly1305_ietf::Key::from_slice(&shared_key.0).ok_or(Crypt4GHError::BadSharedKey)?; - chacha20poly1305_ietf::open(packet_data, None, &nonce, &key).map_err(|_| Crypt4GHError::InvalidData) + let key = chacha20poly1305::Key::from_slice(&shared_key.0); + ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key).map_err(|_| Crypt4GHError::InvalidData) } fn partition_packets(packets: Vec>) -> Result { diff --git a/src/keys.rs b/src/keys.rs index c5c6230..6dbe2fb 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -17,7 +17,9 @@ use aes; use itertools::Itertools; use lazy_static::lazy_static; use sodiumoxide::crypto::aead::chacha20poly1305_ietf; -use sodiumoxide::randombytes::randombytes; +use aead::rand_core::{CryptoRng, RngCore}; +use aead +//use sodiumoxide::randombytes::randombytes; use crate::error::Crypt4GHError; @@ -326,7 +328,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< // Decipher match ciphername { - "aes128-ctr" => aes::Aes128(aes::Aes128, key, iv) + "aes128-ctr" => aes::Aes128Dec(aes::Aes128, key, iv) .decrypt(&mut reader, &mut writer, true) .map_err(Crypt4GHError::DecryptKeyError)?, "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) @@ -490,6 +492,7 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { let mut curve_pk = [0_u8; 32]; let ok = + unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; if ok { Ok(curve_pk) diff --git a/src/lib.rs b/src/lib.rs index ad16afb..8c3fcc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,10 +23,10 @@ use std::collections::HashSet; use std::io::{self, Read, Write}; -use std::sync::Once; use header::DecryptedHeaderPackets; -use sodiumoxide::crypto::aead::chacha20poly1305_ietf::{self, Key, Nonce}; +use chacha20poly1305::{ Key, Nonce }; +use chacha20poly1305 as chacha20_poly1305_ietf; use crate::error::Crypt4GHError; @@ -96,14 +96,6 @@ pub struct Keys { pub recipient_pubkey: Vec, } -pub(crate) static SODIUM_INIT: Once = Once::new(); - -pub(crate) fn init() { - SODIUM_INIT.call_once(|| { - sodiumoxide::init().expect("Unable to initialize libsodium"); - }); -} - /// Reads from the `read_buffer` and writes the encrypted data to `write_buffer`. /// /// Reads from the `read_buffer` and writes the encrypted data (for every `recipient_key`) to `write_buffer`. @@ -116,9 +108,6 @@ pub fn encrypt( range_start: usize, range_span: Option, ) -> Result<(), Crypt4GHError> { - crate::init(); - log::debug!("Start: {}, Span: {:?}", range_start, range_span); - if recipient_keys.is_empty() { return Err(Crypt4GHError::NoRecipients); } @@ -226,7 +215,6 @@ pub fn encrypt_header( ) -> Result, Crypt4GHError> { let encryption_method = 0; let session_key_or_new = session_key.unwrap_or_else(|| { - crate::init(); let mut session_key = [0_u8; 32]; sodiumoxide::randombytes::randombytes_into(&mut session_key); session_key From f355563d51dc4f89926b7b2c446a500860e200e1 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 20 Jun 2023 16:20:46 +1000 Subject: [PATCH 05/64] Continue exploring nacl-compat and curve25519-dalek crates and interfaces, among other refactorings/explorations --- Cargo.lock | 361 +++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 11 +- src/error.rs | 4 +- src/header.rs | 56 ++++---- src/keys.rs | 82 ++++-------- 5 files changed, 408 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8c7d3f..54e310a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.2" @@ -52,7 +58,7 @@ checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ "blowfish", "pbkdf2", - "sha2", + "sha2 0.10.7", ] [[package]] @@ -76,6 +82,24 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -104,6 +128,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "1.4.3" @@ -182,7 +212,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.92", ] [[package]] @@ -212,17 +242,21 @@ dependencies = [ "base64", "bcrypt-pbkdf", "bincode", - "block-buffer", + "block-buffer 0.10.4", "block-padding", "chacha20poly1305", "cipher", "clap", + "crypto_kx", "ctr", + "curve25519-dalek", + "ed25519-dalek", + "elliptic-curve", "itertools", "lazy_static", "log", "pretty_env_logger", - "rand", + "rand 0.8.5", "regex", "rpassword", "scrypt", @@ -230,6 +264,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -237,10 +283,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto_kx" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc51881180822b28e7a33fab08af724956ba96d8be2c03857b082b3425cdb7a" +dependencies = [ + "blake2", + "getrandom 0.2.6", + "rand_core 0.6.4", + "x25519-dalek", +] + [[package]] name = "ctr" version = "0.9.2" @@ -250,23 +308,84 @@ dependencies = [ "cipher", ] +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "crypto-common", "subtle", ] +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "ff", + "generic-array", + "group", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -301,6 +420,16 @@ dependencies = [ "libc", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -309,6 +438,18 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -318,8 +459,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -340,7 +494,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -390,6 +544,15 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -448,7 +611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -458,7 +621,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" dependencies = [ - "digest", + "digest 0.10.7", "hmac", ] @@ -498,7 +661,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.92", "version_check", ] @@ -524,13 +687,26 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -538,8 +714,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -549,7 +735,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -558,7 +753,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.6", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -631,7 +835,7 @@ dependencies = [ "password-hash", "pbkdf2", "salsa20", - "sha2", + "sha2 0.10.7", ] [[package]] @@ -651,7 +855,20 @@ checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.92", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -662,9 +879,15 @@ checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "strsim" version = "0.10.0" @@ -688,6 +911,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -714,7 +948,7 @@ checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.92", ] [[package]] @@ -751,12 +985,72 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + [[package]] name = "winapi" version = "0.3.9" @@ -854,8 +1148,33 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] diff --git a/Cargo.toml b/Cargo.toml index 167e4d8..fdbb3e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,14 @@ aes = "0.8" ctr = "0.9" block-buffer = "0.10" aead = { version = "0.5" } +crypto_kx = { version = "0.1" } +elliptic-curve = "0.13" + +# Experimental +curve25519-dalek = "3.2" +ed25519-dalek = "1.0" +rand = "0.8" + bincode = "1" serde = { version = "1", features = ["derive"] } log = "0.4" @@ -41,9 +49,6 @@ pretty_env_logger = "0.5" thiserror = "1.0" itertools = "0.10" -[dev-dependencies] -rand = "0.8" - [profile.release] lto = true overflow-checks = true diff --git a/src/error.rs b/src/error.rs index ad4534c..f243788 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,8 +24,8 @@ pub enum Crypt4GHError { NoKey, #[error("Unable to wrap key")] BadKey, - #[error("Unable to decrypt key (ERROR = {0:?})")] - DecryptKeyError(SymmetricCipherError), + // #[error("Unable to decrypt key (ERROR = {0:?})")] + // DecryptKeyError(SymmetricCipherError), #[error("Invalid key format")] InvalidKeyFormat, #[error("Invalid SSH key format")] diff --git a/src/header.rs b/src/header.rs index bc22db3..ab6ef08 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,16 +1,15 @@ use std::collections::HashSet; -use aead::{KeyInit, AeadInPlace}; +use aead::{KeyInit, AeadInPlace, AeadCore, OsRng}; +use ed25519_dalek::Keypair; use itertools::Itertools; use serde::{Deserialize, Serialize}; use chacha20poly1305::ChaCha20Poly1305; -// use sodiumoxide::crypto::kx::{x25519blake2b, PublicKey, SecretKey}; -// use sodiumoxide::randombytes; +use crypto_kx::{ SessionKey, ServerSessionKeys, ClientSessionKeys, KeyPair, SecretKey }; use super::SEGMENT_SIZE; use crate::error::Crypt4GHError; use crate::keys::get_public_key_from_private_key; -use crate::Keys; const MAGIC_NUMBER: &[u8; 8] = b"crypt4gh"; const VERSION: u32 = 1; @@ -74,6 +73,10 @@ fn encrypt_x25519_chacha20_poly1305( ) -> Result, Crypt4GHError> { let pubkey = get_public_key_from_private_key(seckey)?; let mut buffer = vec![]; // TODO: Check AEAD Buffer traits instead of this + let mut nonce_buf = vec![]; // TODO: Ditto as above + + let reconstructed_keypair = KeyPair::from(SecretKey::from(seckey)); + let keypair = KeyPair::session_keys_from(&reconstructed_keypair); // Log log::debug!(" packed data({}): {:02x?}", data.len(), data.iter().format("")); @@ -90,19 +93,19 @@ fn encrypt_x25519_chacha20_poly1305( ); // X25519 shared key - let server_pk = PublicKey::from_slice(pubkey.as_ref()).ok_or(Crypt4GHError::BadServerPublicKey)?; - let server_sk = SecretKey::from_slice(&seckey[0..32]).ok_or(Crypt4GHError::BadServerPrivateKey)?; - let client_pk = PublicKey::from_slice(recipient_pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; - let (_, shared_key) = x25519blake2b::server_session_keys(&server_pk, &server_sk, &client_pk) - .map_err(|_| Crypt4GHError::BadSharedKey)?; - log::debug!(" shared key: {:02x?}", shared_key.0.iter().format("")); + //let server_pk = PublicKey::from_slice(pubkey.as_ref()).ok_or(Crypt4GHError::BadServerPublicKey)?; + // let server_sk = SecretKey::from_slice(&seckey[0..32]).ok_or(Crypt4GHError::BadServerPrivateKey)?; + // let client_pk = PublicKey::from_slice(recipient_pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; + let shared_key = ServerSessionKeys::from(); // TODO: Which arg to take in? + //x25519blake2b::server_session_keys(&server_pk, &server_sk, &client_pk) + // .map_err(|_| Crypt4GHError::BadSharedKey)?; + log::debug!(" shared key: {:02x?}", shared_key.into()); // Nonce & chacha20 key - let nonce = - chacha20poly1305::Nonce::from_slice(&randombytes::randombytes(12)); - let key = chacha20poly1305::Key::from_slice(shared_key.as_ref()); - let cipher = ChaCha20Poly1305::new(key) - .encrypt_in_place_detached(nonce, data, &mut buffer); + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); + let key = ChaCha20Poly1305::generate_key(shared_key.into()); + let cipher = ChaCha20Poly1305::new(&key) + .encrypt_in_place_detached(&nonce, data, &mut buffer); Ok(vec![ pubkey, @@ -202,12 +205,12 @@ fn decrypt_x25519_chacha20_poly1305( return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = ChaCha20Poly1305::Nonce::from_slice(&encrypted_part[32..44]) - .ok_or(Crypt4GHError::NoNonce)?; + let nonce = ChaCha20Poly1305::generate_nonce(encrypted_part[32..44]); + //.ok_or(Crypt4GHError::NoNonce)?; let packet_data = &encrypted_part[44..]; log::debug!(" peer pubkey: {:02x?}", peer_pubkey.iter().format("")); - log::debug!(" nonce: {:02x?}", nonce.0.iter().format("")); + log::debug!(" nonce: {:02x?}", nonce.iter().format("")); log::debug!( " encrypted data ({}): {:02x?}", packet_data.len(), @@ -216,16 +219,17 @@ fn decrypt_x25519_chacha20_poly1305( // X25519 shared key let pubkey = get_public_key_from_private_key(privkey)?; - let client_pk = PublicKey::from_slice(&pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; - let client_sk = SecretKey::from_slice(&privkey[0..32]).ok_or(Crypt4GHError::BadClientPrivateKey)?; - let server_pk = PublicKey::from_slice(peer_pubkey).ok_or(Crypt4GHError::BadServerPublicKey)?; - let (shared_key, _) = x25519blake2b::client_session_keys(&client_pk, &client_sk, &server_pk) - .map_err(|_| Crypt4GHError::BadSharedKey)?; - log::debug!("shared key: {:02x?}", shared_key.0.iter().format("")); + //let client_pk = PublicKey::from_slice(&pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; + // let client_sk = SecretKey::from_slice(&privkey[0..32]).ok_or(Crypt4GHError::BadClientPrivateKey)?; + // let server_pk = PublicKey::from_slice(peer_pubkey).ok_or(Crypt4GHError::BadServerPublicKey)?; + let shared_key = ClientSessionKeys::from(&pubkey); + //x25519blake2b::client_session_keys(&client_pk, &client_sk, &server_pk) + // .map_err(|_| Crypt4GHError::BadSharedKey)?; + log::debug!("shared key: {:02x?}", shared_key.into()); // Chacha20_Poly1305 - let key = chacha20poly1305::Key::from_slice(&shared_key.0); - ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key).map_err(|_| Crypt4GHError::InvalidData) + let key = chacha20poly1305::Key::from_slice(&shared_key); + ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key);//.map_err(|_| Crypt4GHError::InvalidData) } fn partition_packets(packets: Vec>) -> Result { diff --git a/src/keys.rs b/src/keys.rs index 6dbe2fb..b8392ab 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -5,21 +5,25 @@ use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; use std::path::Path; -use std::sync::Once; +use aead::{OsRng, KeyInit}; use base64::engine::general_purpose; use base64::Engine; use block_padding::NoPadding; + +use curve25519_dalek::constants::X25519_BASEPOINT; +use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::montgomery::MontgomeryPoint; +use ed25519_dalek::PublicKey; + use scrypt::Params as ScryptParams; use bcrypt_pbkdf; use aes; use itertools::Itertools; use lazy_static::lazy_static; -use sodiumoxide::crypto::aead::chacha20poly1305_ietf; -use aead::rand_core::{CryptoRng, RngCore}; -use aead -//use sodiumoxide::randombytes::randombytes; +use chacha20poly1305; use crate::error::Crypt4GHError; @@ -209,11 +213,11 @@ fn parse_c4gh_private_key( log::debug!("Shared Key: {:02x?}", shared_key.iter().format("")); log::debug!("Nonce: {:02x?}", &private_data[0..12].iter().format("")); - let nonce = chacha20poly1305_ietf::Nonce::from_slice(&private_data[0..12]).ok_or(Crypt4GHError::NoNonce)?; - let key = chacha20poly1305_ietf::Key::from_slice(&shared_key).ok_or(Crypt4GHError::BadKey)?; + let nonce = chacha20poly1305::Nonce::from_slice(&private_data[0..12]);//.ok_or(Crypt4GHError::NoNonce)?; + let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; let encrypted_data = &private_data[12..]; - Ok(chacha20poly1305_ietf::seal(encrypted_data, None, &nonce, &key)) + Ok(chacha20poly1305::ChaCha20Poly1305::new()(encrypted_data, None, &nonce, &key)) } fn parse_ssh_private_key( @@ -490,36 +494,11 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { } fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { - let mut curve_pk = [0_u8; 32]; - let ok = - - unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; - if ok { - Ok(curve_pk) - } - else { - Err(Crypt4GHError::ConversionFailed) - } + todo!() } fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { - let mut curve_sk = [0_u8; 32]; - let ok = - unsafe { libsodium_sys::crypto_sign_ed25519_sk_to_curve25519(curve_sk.as_mut_ptr(), ed25519_sk.as_ptr()) == 0 }; - if ok { - Ok(curve_sk) - } - else { - Err(Crypt4GHError::ConversionFailed) - } -} - -pub(crate) static SODIUM_INIT: Once = Once::new(); - -pub(crate) fn init() { - SODIUM_INIT.call_once(|| { - sodiumoxide::init().expect("Unable to initialize libsodium"); - }); + todo!() } /// Generates a random privary key. @@ -528,10 +507,9 @@ pub(crate) fn init() { /// The resulting private key has a length of 64. The first 32 bytes belong to the secret key, /// the last 32 bytes belong to the public key. pub fn generate_private_key() -> Vec { - init(); - let seckey = randombytes(32); - let pubkey = get_public_key_from_private_key(&seckey).unwrap(); - vec![seckey, pubkey].concat() + let seckey = chacha20poly1305::ChaCha20Poly1305::generate_key(OsRng); + let pubkey = get_public_key_from_private_key(&seckey.into()); + vec![seckey, pubkey] } /// Generates a pair of `Crypt4GH` keys. @@ -591,7 +569,6 @@ fn encode_string_c4gh(s: Option<&[u8]>) -> Vec { } fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> Result, Crypt4GHError> { - init(); Ok(if passphrase.is_empty() { log::warn!("The private key is not encrypted"); vec![ @@ -609,23 +586,23 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> else { let kdfname = "scrypt"; let (salt_size, rounds) = get_kdf(kdfname)?; - let salt = randombytes(salt_size); + let salt = getrandom(salt_size); let derived_key = derive_key(kdfname, passphrase, Some(salt.clone()), Some(rounds), 32)?; - let nonce_bytes = randombytes(12); - let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); - let key = chacha20poly1305_ietf::Key::from_slice(&derived_key).unwrap(); - let encrypted_key = chacha20poly1305_ietf::seal(skpk, None, &nonce, &key); + let nonce_bytes = getrandom(12); + let nonce = chacha20poly1305::Nonce::from_slice(&nonce_bytes); + let key = chacha20poly1305::Key::from_slice(&derived_key); + let encrypted_key = chacha20poly1305::seal(skpk, None, &nonce, &key); - log::debug!("Derived Key: {:02x?}", derived_key.iter().format("")); - log::debug!("Salt: {:02x?}", salt.iter().format("")); - log::debug!("Nonce: {:02x?}", nonce.0.to_vec().iter().format("")); + log::debug!("Derived Key: {:02x?}", derived_key); + log::debug!("Salt: {:02x?}", salt); + log::debug!("Nonce: {:02x?}", nonce); vec![ C4GH_MAGIC_WORD.to_vec(), encode_string_c4gh(Some(kdfname.as_bytes())), encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt].concat())), encode_string_c4gh(Some(b"chacha20_poly1305")), - encode_string_c4gh(Some(&vec![nonce.0.to_vec(), encrypted_key].concat())), + encode_string_c4gh(Some(&vec![nonce, encrypted_key].concat())), match comment { Some(c) => encode_string_c4gh(Some(c.as_bytes())), None => [].to_vec(), @@ -646,8 +623,5 @@ fn get_kdf(kdfname: &str) -> Result<(usize, u32), Crypt4GHError> { /// Computes the curve25519 `scalarmult_base` to the first 32 bytes of `sk`. /// `sk` must be at least 32 bytes. pub fn get_public_key_from_private_key(sk: &[u8]) -> Result, Crypt4GHError> { - let scalar = - sodiumoxide::crypto::scalarmult::Scalar::from_slice(&sk[0..32]).ok_or(Crypt4GHError::ReadPublicKeyError)?; - let pubkey = sodiumoxide::crypto::scalarmult::scalarmult_base(&scalar).0; - Ok(pubkey.to_vec()) -} + todo!() +} \ No newline at end of file From 9fb32e98c0adf38cc1ff786377b4582d00087215 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sat, 24 Jun 2023 23:02:26 +1000 Subject: [PATCH 06/64] Continue refactor, break code a bit more :-S --- Cargo.lock | 105 +++++++++++++++++++++++++++++++++----------------- Cargo.toml | 9 ++--- src/error.rs | 4 +- src/header.rs | 61 ++++++----------------------- src/lib.rs | 5 +-- 5 files changed, 90 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54e310a..8ebb5c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,17 +12,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -238,18 +227,16 @@ name = "crypt4gh" version = "0.4.1" dependencies = [ "aead", - "aes", "base64", "bcrypt-pbkdf", "bincode", "block-buffer 0.10.4", "block-padding", "chacha20poly1305", - "cipher", "clap", + "crypto_box", "crypto_kx", - "ctr", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519-dalek", "elliptic-curve", "itertools", @@ -257,6 +244,7 @@ dependencies = [ "log", "pretty_env_logger", "rand 0.8.5", + "rand_core 0.6.4", "regex", "rpassword", "scrypt", @@ -287,25 +275,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto_box" +version = "0.9.0-rc.0" +source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" +dependencies = [ + "aead", + "crypto_secretbox", + "curve25519-dalek 4.0.0-rc.2", + "salsa20", + "zeroize", +] + [[package]] name = "crypto_kx" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc51881180822b28e7a33fab08af724956ba96d8be2c03857b082b3425cdb7a" +version = "0.2.0-pre.0" +source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" dependencies = [ "blake2", + "curve25519-dalek 4.0.0-rc.2", "getrandom 0.2.6", "rand_core 0.6.4", - "x25519-dalek", ] [[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +name = "crypto_secretbox" +version = "0.1.1" +source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" dependencies = [ + "aead", "cipher", + "generic-array", + "poly1305", + "salsa20", + "subtle", + "zeroize", ] [[package]] @@ -321,6 +325,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "subtle", + "zeroize", +] + [[package]] name = "digest" version = "0.9.0" @@ -356,7 +374,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519", "rand 0.7.3", "serde", @@ -430,6 +448,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "generic-array" version = "0.14.7" @@ -565,6 +589,12 @@ version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -604,6 +634,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + [[package]] name = "password-hash" version = "0.5.0" @@ -625,6 +665,12 @@ dependencies = [ "hmac", ] +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + [[package]] name = "poly1305" version = "0.8.0" @@ -1148,17 +1194,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index fdbb3e0..b1cbf1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,25 +28,24 @@ lazy_static = "1.4" chacha20poly1305 = "0.10" block-padding = "0.3" scrypt = "0.11" -cipher = "0.4" bcrypt-pbkdf = "0.10" -aes = "0.8" -ctr = "0.9" block-buffer = "0.10" aead = { version = "0.5" } -crypto_kx = { version = "0.1" } elliptic-curve = "0.13" +rand_core = "0.6" # Experimental curve25519-dalek = "3.2" ed25519-dalek = "1.0" rand = "0.8" +crypto_kx = { version = "0.2.0-pre.0", git = "https://github.com/RustCrypto/nacl-compat" } +crypto_box = { version = "0.9.0-rc.0", git = "https://github.com/RustCrypto/nacl-compat" } bincode = "1" serde = { version = "1", features = ["derive"] } log = "0.4" pretty_env_logger = "0.5" -thiserror = "1.0" +thiserror = "1" itertools = "0.10" [profile.release] diff --git a/src/error.rs b/src/error.rs index f243788..3a59a38 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,6 @@ use std::error::Error; use std::path::PathBuf; -use scrypt::errors::InvalidParams; - use thiserror::Error; #[derive(Debug, Error)] @@ -28,6 +26,8 @@ pub enum Crypt4GHError { // DecryptKeyError(SymmetricCipherError), #[error("Invalid key format")] InvalidKeyFormat, + #[error("Invalid key length")] + InvalidKeyLength(#[from] crypto_kx::errors::InvalidLength), #[error("Invalid SSH key format")] InvalidSSHKey, #[error("Unable to wrap nonce")] diff --git a/src/header.rs b/src/header.rs index ab6ef08..a4c912e 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,11 +1,14 @@ use std::collections::HashSet; - -use aead::{KeyInit, AeadInPlace, AeadCore, OsRng}; -use ed25519_dalek::Keypair; +use aead::AeadCore; use itertools::Itertools; +use rand_core::OsRng; use serde::{Deserialize, Serialize}; + +use chacha20poly1305; use chacha20poly1305::ChaCha20Poly1305; -use crypto_kx::{ SessionKey, ServerSessionKeys, ClientSessionKeys, KeyPair, SecretKey }; +use crate::Keys; + +use crypto_kx::ClientSessionKeys; use super::SEGMENT_SIZE; use crate::error::Crypt4GHError; @@ -71,48 +74,7 @@ fn encrypt_x25519_chacha20_poly1305( seckey: &[u8], recipient_pubkey: &[u8], ) -> Result, Crypt4GHError> { - let pubkey = get_public_key_from_private_key(seckey)?; - let mut buffer = vec![]; // TODO: Check AEAD Buffer traits instead of this - let mut nonce_buf = vec![]; // TODO: Ditto as above - - let reconstructed_keypair = KeyPair::from(SecretKey::from(seckey)); - let keypair = KeyPair::session_keys_from(&reconstructed_keypair); - - // Log - log::debug!(" packed data({}): {:02x?}", data.len(), data.iter().format("")); - log::debug!(" my public key({}): {:02x?}", pubkey.len(), pubkey.iter().format("")); - log::debug!( - " my private key({}): {:02x?}", - seckey[0..32].len(), - &seckey[0..32].iter().format("") - ); - log::debug!( - " recipient public key({}): {:02x?}", - recipient_pubkey.len(), - recipient_pubkey.iter().format("") - ); - - // X25519 shared key - //let server_pk = PublicKey::from_slice(pubkey.as_ref()).ok_or(Crypt4GHError::BadServerPublicKey)?; - // let server_sk = SecretKey::from_slice(&seckey[0..32]).ok_or(Crypt4GHError::BadServerPrivateKey)?; - // let client_pk = PublicKey::from_slice(recipient_pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; - let shared_key = ServerSessionKeys::from(); // TODO: Which arg to take in? - //x25519blake2b::server_session_keys(&server_pk, &server_sk, &client_pk) - // .map_err(|_| Crypt4GHError::BadSharedKey)?; - log::debug!(" shared key: {:02x?}", shared_key.into()); - - // Nonce & chacha20 key - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); - let key = ChaCha20Poly1305::generate_key(shared_key.into()); - let cipher = ChaCha20Poly1305::new(&key) - .encrypt_in_place_detached(&nonce, data, &mut buffer); - - Ok(vec![ - pubkey, - nonce.to_vec(), - buffer - ] - .concat()) + todo!() } /// Computes the encrypted part, using all keys @@ -205,7 +167,7 @@ fn decrypt_x25519_chacha20_poly1305( return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = ChaCha20Poly1305::generate_nonce(encrypted_part[32..44]); + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); //.ok_or(Crypt4GHError::NoNonce)?; let packet_data = &encrypted_part[44..]; @@ -222,14 +184,15 @@ fn decrypt_x25519_chacha20_poly1305( //let client_pk = PublicKey::from_slice(&pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; // let client_sk = SecretKey::from_slice(&privkey[0..32]).ok_or(Crypt4GHError::BadClientPrivateKey)?; // let server_pk = PublicKey::from_slice(peer_pubkey).ok_or(Crypt4GHError::BadServerPublicKey)?; - let shared_key = ClientSessionKeys::from(&pubkey); + //x25519blake2b + let shared_key = ClientSessionKeys::new(pubkey); //x25519blake2b::client_session_keys(&client_pk, &client_sk, &server_pk) // .map_err(|_| Crypt4GHError::BadSharedKey)?; log::debug!("shared key: {:02x?}", shared_key.into()); // Chacha20_Poly1305 let key = chacha20poly1305::Key::from_slice(&shared_key); - ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key);//.map_err(|_| Crypt4GHError::InvalidData) + ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key)//.map_err(|_| Crypt4GHError::InvalidData) } fn partition_packets(packets: Vec>) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 8c3fcc4..7e65a5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,8 +25,7 @@ use std::collections::HashSet; use std::io::{self, Read, Write}; use header::DecryptedHeaderPackets; -use chacha20poly1305::{ Key, Nonce }; -use chacha20poly1305 as chacha20_poly1305_ietf; +use chacha20poly1305::{ self, Key, Nonce }; use crate::error::Crypt4GHError; @@ -530,7 +529,7 @@ fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result Date: Tue, 4 Jul 2023 09:59:55 +1000 Subject: [PATCH 07/64] Replace chacha20 sodiumoxide functions for those tested in github.com/brainstorm/crypt4gh-rustcrypto --- src/header.rs | 104 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/src/header.rs b/src/header.rs index a4c912e..f1a373c 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,18 +1,17 @@ use std::collections::HashSet; -use aead::AeadCore; + +use aead::consts::{U12, U32}; +use aead::generic_array::GenericArray; use itertools::Itertools; -use rand_core::OsRng; use serde::{Deserialize, Serialize}; -use chacha20poly1305; -use chacha20poly1305::ChaCha20Poly1305; +use chacha20poly1305::{self, aead, ChaCha20Poly1305}; use crate::Keys; -use crypto_kx::ClientSessionKeys; +use crypto_kx::{SecretKey, PublicKey, Keypair}; use super::SEGMENT_SIZE; use crate::error::Crypt4GHError; -use crate::keys::get_public_key_from_private_key; const MAGIC_NUMBER: &[u8; 8] = b"crypt4gh"; const VERSION: u32 = 1; @@ -74,7 +73,46 @@ fn encrypt_x25519_chacha20_poly1305( seckey: &[u8], recipient_pubkey: &[u8], ) -> Result, Crypt4GHError> { - todo!() + + let server_sk = SecretKey::try_from(&seckey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; + let client_pk = PublicKey::try_from(recipient_pubkey).map_err(|_| Crypt4GHError::BadServerPublicKey)?; + + let pubkey = server_sk.public_key(); + + + log::debug!(" RustCrypto encrypt() packed data({}): {:02x?}", data.len(), data.iter()); + log::debug!(" RustCrypto encrypt() public key({}): {:02x?}", pubkey.as_ref().len(), pubkey.as_ref().iter()); + log::debug!( + " RustCrypto encrypt() private key({}): {:02x?}", + seckey[0..32].len(), + &seckey[0..32].iter() + ); + log::debug!( + " RustCrypto encrypt() recipient public key({}): {:02x?}", + recipient_pubkey.len(), + recipient_pubkey.iter() + ); + + + let nonce = GenericArray::::from_slice(crate::NONCE); + + let keypair = Keypair::from(server_sk); + let server_session_keys = keypair.session_keys_from(&client_pk); + let shared_key = GenericArray::::from_slice(&server_session_keys.rx.as_ref().as_slice()); + + log::debug!(" RustCrypto encrypt() shared key: {:02x?}", shared_key); + + let cipher = ChaCha20Poly1305::new(shared_key); + + let ciphertext = cipher.encrypt(nonce, data) + .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; + + Ok(vec![ + [0,0,0,0].as_ref(), + pubkey.as_ref(), + nonce.as_slice(), + ciphertext.as_slice() + ].concat()) } /// Computes the encrypted part, using all keys @@ -155,44 +193,42 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) } fn decrypt_x25519_chacha20_poly1305( - encrypted_part: &[u8], - privkey: &[u8], - sender_pubkey: &Option>, + encrypted_part: &[u8], + privkey: &[u8], + sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { - log::debug!(" my secret key: {:02x?}", &privkey[0..32].iter().format("")); - - let peer_pubkey = &encrypted_part[0..32]; + let peer_pubkey = &encrypted_part[4..PublicKey::BYTES+4]; + log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); + log::debug!(" RustCrypto decrypt() sender_pubkey({}): {:02x?}", sender_pubkey.clone().unwrap().as_slice().len(), sender_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); - //.ok_or(Crypt4GHError::NoNonce)?; - let packet_data = &encrypted_part[44..]; + let nonce = GenericArray::::from_slice(crate::NONCE); + let packet_data = &encrypted_part[44+4..]; + + let client_sk = SecretKey::try_from(&privkey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; + let server_pk = PublicKey::try_from(peer_pubkey).map_err(|_| Crypt4GHError::BadServerPublicKey)?; + + let keypair = Keypair::from(client_sk); + let client_session_keys = keypair.session_keys_to(&server_pk); + let shared_key = GenericArray::::from_slice(&client_session_keys.tx.as_ref().as_slice()); - log::debug!(" peer pubkey: {:02x?}", peer_pubkey.iter().format("")); - log::debug!(" nonce: {:02x?}", nonce.iter().format("")); + let cipher = ChaCha20Poly1305::new(shared_key); + + log::debug!(" RustCrypto peer pubkey: {:02x?}", peer_pubkey.iter()); + log::debug!(" RustCrypto nonce: {:02x?}", nonce.iter()); log::debug!( - " encrypted data ({}): {:02x?}", + " RustCrypto encrypted data ({}): {:02x?}", packet_data.len(), - packet_data.iter().format("") + packet_data.iter() ); - // X25519 shared key - let pubkey = get_public_key_from_private_key(privkey)?; - //let client_pk = PublicKey::from_slice(&pubkey).ok_or(Crypt4GHError::BadClientPublicKey)?; - // let client_sk = SecretKey::from_slice(&privkey[0..32]).ok_or(Crypt4GHError::BadClientPrivateKey)?; - // let server_pk = PublicKey::from_slice(peer_pubkey).ok_or(Crypt4GHError::BadServerPublicKey)?; - //x25519blake2b - let shared_key = ClientSessionKeys::new(pubkey); - //x25519blake2b::client_session_keys(&client_pk, &client_sk, &server_pk) - // .map_err(|_| Crypt4GHError::BadSharedKey)?; - log::debug!("shared key: {:02x?}", shared_key.into()); - - // Chacha20_Poly1305 - let key = chacha20poly1305::Key::from_slice(&shared_key); - ChaCha20Poly1305::decrypt_in_place(nonce, None, &nonce, &key)//.map_err(|_| Crypt4GHError::InvalidData) + let plaintext = cipher.decrypt(nonce, packet_data) + .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; + + Ok(plaintext) } fn partition_packets(packets: Vec>) -> Result { From cc59211484bb88dbb234ac09f1106fc737809fc8 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 4 Jul 2023 10:00:31 +1000 Subject: [PATCH 08/64] Cleanup intermediate (hazmat) RustCrypto crates that are not needed anymore --- Cargo.lock | 389 ++--------------------------------------------------- Cargo.toml | 15 +-- 2 files changed, 11 insertions(+), 393 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ebb5c5..e309cc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,35 +21,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bcrypt-pbkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" -dependencies = [ - "blowfish", - "pbkdf2", - "sha2 0.10.7", -] - [[package]] name = "bincode" version = "1.3.3" @@ -77,16 +54,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", + "digest", ] [[package]] @@ -98,37 +66,12 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", -] - [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "cc" version = "1.0.73" @@ -226,44 +169,21 @@ dependencies = [ name = "crypt4gh" version = "0.4.1" dependencies = [ - "aead", "base64", - "bcrypt-pbkdf", "bincode", - "block-buffer 0.10.4", - "block-padding", "chacha20poly1305", "clap", - "crypto_box", "crypto_kx", - "curve25519-dalek 3.2.0", - "ed25519-dalek", - "elliptic-curve", "itertools", "lazy_static", "log", "pretty_env_logger", - "rand 0.8.5", - "rand_core 0.6.4", "regex", "rpassword", - "scrypt", "serde", "thiserror", ] -[[package]] -name = "crypto-bigint" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -271,58 +191,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] -[[package]] -name = "crypto_box" -version = "0.9.0-rc.0" -source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" -dependencies = [ - "aead", - "crypto_secretbox", - "curve25519-dalek 4.0.0-rc.2", - "salsa20", - "zeroize", -] - [[package]] name = "crypto_kx" version = "0.2.0-pre.0" source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" dependencies = [ "blake2", - "curve25519-dalek 4.0.0-rc.2", - "getrandom 0.2.6", - "rand_core 0.6.4", -] - -[[package]] -name = "crypto_secretbox" -version = "0.1.1" -source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" -dependencies = [ - "aead", - "cipher", - "generic-array", - "poly1305", - "salsa20", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", + "curve25519-dalek", + "getrandom", + "rand_core", ] [[package]] @@ -339,71 +220,23 @@ dependencies = [ "zeroize", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "crypto-common", "subtle", ] -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "elliptic-curve" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" -dependencies = [ - "base16ct", - "crypto-bigint", - "ff", - "generic-array", - "group", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -438,16 +271,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "fiat-crypto" version = "0.1.20" @@ -462,18 +285,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -485,21 +296,10 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "heck" version = "0.4.0" @@ -512,15 +312,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "humantime" version = "2.1.0" @@ -561,9 +352,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -644,27 +435,6 @@ dependencies = [ "libm", ] -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" -dependencies = [ - "digest 0.10.7", - "hmac", -] - [[package]] name = "platforms" version = "3.0.2" @@ -682,12 +452,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "pretty_env_logger" version = "0.5.0" @@ -740,75 +504,13 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.6", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -863,27 +565,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "password-hash", - "pbkdf2", - "salsa20", - "sha2 0.10.7", -] - [[package]] name = "serde" version = "1.0.137" @@ -904,36 +585,6 @@ dependencies = [ "syn 1.0.92", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "strsim" version = "0.10.0" @@ -1031,12 +682,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -1199,17 +844,3 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] diff --git a/Cargo.toml b/Cargo.toml index b1cbf1b..8e58dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,27 +26,14 @@ rpassword = "7" base64 = "0.21" lazy_static = "1.4" chacha20poly1305 = "0.10" -block-padding = "0.3" -scrypt = "0.11" -bcrypt-pbkdf = "0.10" -block-buffer = "0.10" -aead = { version = "0.5" } -elliptic-curve = "0.13" -rand_core = "0.6" - -# Experimental -curve25519-dalek = "3.2" -ed25519-dalek = "1.0" -rand = "0.8" crypto_kx = { version = "0.2.0-pre.0", git = "https://github.com/RustCrypto/nacl-compat" } -crypto_box = { version = "0.9.0-rc.0", git = "https://github.com/RustCrypto/nacl-compat" } bincode = "1" serde = { version = "1", features = ["derive"] } log = "0.4" pretty_env_logger = "0.5" thiserror = "1" -itertools = "0.10" +itertools = "0.11" [profile.release] lto = true From 3c4ed10645f292a174e4ecc719fec1dda5c26515 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 4 Jul 2023 11:26:04 +1000 Subject: [PATCH 09/64] Fix header decrypt/encrypt nonce, start fixing keys.rs --- Cargo.lock | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/error.rs | 8 ++--- src/header.rs | 23 ++++++------ src/keys.rs | 24 +++++-------- 5 files changed, 125 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e309cc5..3c4f652 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,23 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2", +] + [[package]] name = "bincode" version = "1.3.3" @@ -66,12 +83,28 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.73" @@ -170,6 +203,7 @@ name = "crypt4gh" version = "0.4.1" dependencies = [ "base64", + "bcrypt-pbkdf", "bincode", "chacha20poly1305", "clap", @@ -180,6 +214,7 @@ dependencies = [ "pretty_env_logger", "regex", "rpassword", + "scrypt", "serde", "thiserror", ] @@ -312,6 +347,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "humantime" version = "2.1.0" @@ -435,6 +479,27 @@ dependencies = [ "libm", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "platforms" version = "3.0.2" @@ -565,6 +630,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "serde" version = "1.0.137" @@ -585,6 +671,17 @@ dependencies = [ "syn 1.0.92", ] +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 8e58dd7..a7f1310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ base64 = "0.21" lazy_static = "1.4" chacha20poly1305 = "0.10" crypto_kx = { version = "0.2.0-pre.0", git = "https://github.com/RustCrypto/nacl-compat" } +scrypt = { version = "0.11" } +bcrypt-pbkdf = { version = "0.10" } bincode = "1" serde = { version = "1", features = ["derive"] } diff --git a/src/error.rs b/src/error.rs index 3a59a38..e4ed762 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,8 +26,8 @@ pub enum Crypt4GHError { // DecryptKeyError(SymmetricCipherError), #[error("Invalid key format")] InvalidKeyFormat, - #[error("Invalid key length")] - InvalidKeyLength(#[from] crypto_kx::errors::InvalidLength), + // #[error("Invalid key length")] + // InvalidKeyLength(#[from] crypto_kx::errors::InvalidLength), #[error("Invalid SSH key format")] InvalidSSHKey, #[error("Unable to wrap nonce")] @@ -66,8 +66,8 @@ pub enum Crypt4GHError { BadSharedKey, #[error("Invalid Peer's Public Key")] InvalidPeerPubPkey, - #[error("Invalid paramenters passed to Scrypt")] - ScryptParamsError(InvalidParams), + // #[error("Invalid paramenters passed to Scrypt")] + // ScryptParamsError(InvalidParams), // Reading errors #[error("Unable to read {0} bytes from input (ERROR = {1:?})")] diff --git a/src/header.rs b/src/header.rs index f1a373c..5a697bc 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,14 +1,17 @@ use std::collections::HashSet; -use aead::consts::{U12, U32}; +use aead::consts::U32; use aead::generic_array::GenericArray; -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use chacha20poly1305::{self, aead, ChaCha20Poly1305}; -use crate::Keys; +use chacha20poly1305::aead::Aead; +use chacha20poly1305::aead::OsRng; +use chacha20poly1305::{self, aead, ChaCha20Poly1305, KeyInit, AeadCore}; + +//use itertools::Itertools; +use serde::{Deserialize, Serialize}; use crypto_kx::{SecretKey, PublicKey, Keypair}; +use crate::Keys; use super::SEGMENT_SIZE; use crate::error::Crypt4GHError; @@ -93,8 +96,8 @@ fn encrypt_x25519_chacha20_poly1305( recipient_pubkey.iter() ); - - let nonce = GenericArray::::from_slice(crate::NONCE); + // TODO: Make sure this doesn't exceed 2^32 executions, otherwise implement a counter and/or other countermeasures against repeats + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let keypair = Keypair::from(server_sk); let server_session_keys = keypair.session_keys_from(&client_pk); @@ -104,7 +107,7 @@ fn encrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); - let ciphertext = cipher.encrypt(nonce, data) + let ciphertext = cipher.encrypt(&nonce, data) .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; Ok(vec![ @@ -205,7 +208,7 @@ fn decrypt_x25519_chacha20_poly1305( return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = GenericArray::::from_slice(crate::NONCE); + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let packet_data = &encrypted_part[44+4..]; let client_sk = SecretKey::try_from(&privkey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; @@ -225,7 +228,7 @@ fn decrypt_x25519_chacha20_poly1305( packet_data.iter() ); - let plaintext = cipher.decrypt(nonce, packet_data) + let plaintext = cipher.decrypt(&nonce, packet_data) .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; Ok(plaintext) diff --git a/src/keys.rs b/src/keys.rs index b8392ab..aed4496 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -6,24 +6,15 @@ use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; use std::path::Path; -use aead::{OsRng, KeyInit}; use base64::engine::general_purpose; use base64::Engine; -use block_padding::NoPadding; - -use curve25519_dalek::constants::X25519_BASEPOINT; -use curve25519_dalek::scalar::Scalar; -use curve25519_dalek::edwards::CompressedEdwardsY; -use curve25519_dalek::montgomery::MontgomeryPoint; -use ed25519_dalek::PublicKey; - -use scrypt::Params as ScryptParams; -use bcrypt_pbkdf; -use aes; use itertools::Itertools; use lazy_static::lazy_static; -use chacha20poly1305; + +use chacha20poly1305::aead::Aead; +use chacha20poly1305::aead::OsRng; +use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit}; use crate::error::Crypt4GHError; @@ -128,7 +119,7 @@ fn derive_key( "scrypt" => { // TODO: Review last param of ScryptParams (length of what, exactly?) carefully. // Added "dklen" for now since it seemed fitting, but needs proper review. - let params = ScryptParams::new(14, 8, 1, dklen); + let params = scrypt::Params::new(14, 8, 1, dklen); scrypt::scrypt( passphrase.as_bytes(), &salt.unwrap_or_else(|| { @@ -217,7 +208,10 @@ fn parse_c4gh_private_key( let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; let encrypted_data = &private_data[12..]; - Ok(chacha20poly1305::ChaCha20Poly1305::new()(encrypted_data, None, &nonce, &key)) + let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) + .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; + + Ok(privkey_plain) } fn parse_ssh_private_key( From 7c4cd03d20fe042866647808c042114285f7882c Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 5 Jul 2023 10:06:24 +1000 Subject: [PATCH 10/64] Experimenting with similar aes-ctr constructs for libsodium->rustcrypto transition --- src/keys.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index aed4496..e7c3b18 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -12,9 +12,12 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; +use aes::cipher::{self, KeyIvInit, StreamCipher, StreamCipherSeek}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; -use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit}; +use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore}; + +use ctr; use crate::error::Crypt4GHError; @@ -326,7 +329,16 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< // Decipher match ciphername { - "aes128-ctr" => aes::Aes128Dec(aes::Aes128, key, iv) + "aes128-ctr" => { + let ctr_iv = ctr::cipher::Iv::from_slice(&iv); + let ctr_key = ctr::cipher::Key::from_slice(&key); + + type Aes128Ctr = ctr::Ctr128LE; + + let mut cipher = Aes128Ctr::new(&ctr_key.into(), &ctr_iv.into()); + cipher.apply_keystream(reader) + }, + "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) .decrypt(&mut reader, &mut writer, true) .map_err(Crypt4GHError::DecryptKeyError)?, "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) @@ -347,7 +359,6 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), }; - Ok(output) } From c9952264e799a9656cfef35ffe42ea79e29f8532 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 5 Jul 2023 12:20:14 +1000 Subject: [PATCH 11/64] Taking a stab at ChaCha20 PRNGs to replace libsodium's getrandom() --- Cargo.lock | 51 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +++ src/error.rs | 2 ++ src/keys.rs | 72 ++++++++++++++++++++++++++++------------------------ src/lib.rs | 6 ++++- 5 files changed, 101 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c4f652..e3f3ae7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -202,16 +213,20 @@ dependencies = [ name = "crypt4gh" version = "0.4.1" dependencies = [ + "aes", "base64", "bcrypt-pbkdf", "bincode", "chacha20poly1305", "clap", "crypto_kx", + "ctr", "itertools", "lazy_static", "log", "pretty_env_logger", + "rand", + "rand_chacha", "regex", "rpassword", "scrypt", @@ -241,6 +256,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "4.0.0-rc.2" @@ -517,6 +541,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "pretty_env_logger" version = "0.5.0" @@ -569,6 +599,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + [[package]] name = "rand_core" version = "0.6.4" diff --git a/Cargo.toml b/Cargo.toml index a7f1310..7495eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ chacha20poly1305 = "0.10" crypto_kx = { version = "0.2.0-pre.0", git = "https://github.com/RustCrypto/nacl-compat" } scrypt = { version = "0.11" } bcrypt-pbkdf = { version = "0.10" } +aes = { version = "0.8" } +ctr = { version = "0.9" } bincode = "1" serde = { version = "1", features = ["derive"] } @@ -36,6 +38,8 @@ log = "0.4" pretty_env_logger = "0.5" thiserror = "1" itertools = "0.11" +rand = "0.8" +rand_chacha = "0.3.1" [profile.release] lto = true diff --git a/src/error.rs b/src/error.rs index e4ed762..0249304 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,6 +18,8 @@ pub enum Crypt4GHError { NoRandomNonce, #[error("Unable to extract nonce")] NoNonce, + #[error("Unable to create random salt")] + NoRandomSalt, #[error("Unable to create session key")] NoKey, #[error("Unable to wrap key")] diff --git a/src/keys.rs b/src/keys.rs index e7c3b18..2f944ad 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,6 +1,10 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] +use aes::cipher::generic_array::GenericArray; +use rand::{SeedableRng, Rng}; +use rand_chacha; + use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; @@ -12,10 +16,10 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; -use aes::cipher::{self, KeyIvInit, StreamCipher, StreamCipherSeek}; +use aes::cipher::{KeyIvInit}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; -use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore}; +use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore, consts::U12}; use ctr; @@ -330,33 +334,29 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< // Decipher match ciphername { "aes128-ctr" => { - let ctr_iv = ctr::cipher::Iv::from_slice(&iv); - let ctr_key = ctr::cipher::Key::from_slice(&key); - type Aes128Ctr = ctr::Ctr128LE; - let mut cipher = Aes128Ctr::new(&ctr_key.into(), &ctr_iv.into()); - cipher.apply_keystream(reader) + Aes128Ctr::new(key.into(), &iv.into()); }, - "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "aes256-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize256, key, iv) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "aes128-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize128, key, iv, NoPadding) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "aes192-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize192, key, iv, NoPadding) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "aes256-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize256, key, iv, NoPadding) - .decrypt(&mut reader, &mut writer, true) - .map_err(Crypt4GHError::DecryptKeyError)?, - "3des-cbc" => unimplemented!(), + // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "aes256-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize256, key, iv) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "aes128-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize128, key, iv, NoPadding) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "aes192-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize192, key, iv, NoPadding) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "aes256-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize256, key, iv, NoPadding) + // .decrypt(&mut reader, &mut writer, true) + // .map_err(Crypt4GHError::DecryptKeyError)?, + // "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), }; Ok(output) @@ -591,12 +591,18 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> else { let kdfname = "scrypt"; let (salt_size, rounds) = get_kdf(kdfname)?; - let salt = getrandom(salt_size); - let derived_key = derive_key(kdfname, passphrase, Some(salt.clone()), Some(rounds), 32)?; - let nonce_bytes = getrandom(12); - let nonce = chacha20poly1305::Nonce::from_slice(&nonce_bytes); + let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).gen::<[u8;10]>(); // TODO: This is wrong X"D + + let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let key = chacha20poly1305::Key::from_slice(&derived_key); - let encrypted_key = chacha20poly1305::seal(skpk, None, &nonce, &key); + let salt_ga = GenericArray::from_slice(salt.as_slice()); + + let encrypted_key = ChaCha20Poly1305::new(&key) + .encrypt(salt_ga, skpk) + .map_err(|_| Crypt4GHError::BadKey)?; + + let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); log::debug!("Derived Key: {:02x?}", derived_key); log::debug!("Salt: {:02x?}", salt); @@ -605,9 +611,9 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> vec![ C4GH_MAGIC_WORD.to_vec(), encode_string_c4gh(Some(kdfname.as_bytes())), - encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt].concat())), + encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), encode_string_c4gh(Some(b"chacha20_poly1305")), - encode_string_c4gh(Some(&vec![nonce, encrypted_key].concat())), + encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), match comment { Some(c) => encode_string_c4gh(Some(c.as_bytes())), None => [].to_vec(), diff --git a/src/lib.rs b/src/lib.rs index 7e65a5c..57a1517 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,9 @@ clippy::redundant_else )] +use rand::SeedableRng; +use rand_chacha; + use std::collections::HashSet; use std::io::{self, Read, Write}; @@ -129,7 +132,8 @@ pub fn encrypt( log::info!("Creating Crypt4GH header"); let mut session_key = [0_u8; 32]; - sodiumoxide::randombytes::randombytes_into(&mut session_key); + let rnd = rand_chacha::ChaCha20Rng::from_entropy(); + rnd.set_stream(session_key.into()); let header_bytes = encrypt_header(recipient_keys, &Some(session_key))?; log::debug!("header length: {}", header_bytes.len()); From 17538eeb1507f8e7501031ba4e8f4b2b24f65a8f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 24 Jul 2023 14:50:17 +1000 Subject: [PATCH 12/64] Most lib.rs primitives translated, need to tackle the decrypt block and validate --- src/lib.rs | 76 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 57a1517..b30d7e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,9 @@ use std::collections::HashSet; use std::io::{self, Read, Write}; use header::DecryptedHeaderPackets; -use chacha20poly1305::{ self, Key, Nonce }; + +use chacha20poly1305::aead::Aead; +use chacha20poly1305::{ self, ChaCha20Poly1305, Key, KeyInit, Nonce }; use crate::error::Crypt4GHError; @@ -133,7 +135,9 @@ pub fn encrypt( log::info!("Creating Crypt4GH header"); let mut session_key = [0_u8; 32]; let rnd = rand_chacha::ChaCha20Rng::from_entropy(); - rnd.set_stream(session_key.into()); + let rnd_num = rnd.get_stream(); + // random bytes into session_key + session_key = rnd.get_seed(); // TODO: Is this correct usage? let header_bytes = encrypt_header(recipient_keys, &Some(session_key))?; log::debug!("header length: {}", header_bytes.len()); @@ -153,18 +157,19 @@ pub fn encrypt( } else if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12)) - .ok_or(Crypt4GHError::NoRandomNonce)?; - let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, nonce, &key); + let nonce = Nonce::from_slice(&rnd.get_seed()); + //.map_err(|_| Crypt4GHError::NoRandomNonce)?; + let key = Key::from_slice(&session_key); + //.ok_or(Crypt4GHError::NoKey)?; + let encrypted_data = encrypt_segment(data, *nonce, &key); write_buffer.write_all(&encrypted_data)?; break; } else { - let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12)) - .ok_or(Crypt4GHError::NoRandomNonce)?; - let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(&segment, nonce, &key); + let nonce = Nonce::from_slice(&rnd.get_seed()); + //.ok_or(Crypt4GHError::NoRandomNonce)?; + let key = Key::from_slice(&session_key);//.ok_or(Crypt4GHError::NoKey)?; + let encrypted_data = encrypt_segment(&segment, *nonce, &key); write_buffer.write_all(&encrypted_data)?; } }, @@ -175,10 +180,11 @@ pub fn encrypt( // Stop if segment_len >= remaining_length { let (data, _) = segment.split_at(remaining_length); - let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12)) - .ok_or(Crypt4GHError::NoRandomNonce)?; - let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, nonce, &key); + let nonce = Nonce::from_slice(&rnd.get_seed()); + //.ok_or(Crypt4GHError::NoRandomNonce)?; + let key = Key::from_slice(&session_key); + //.ok_or(Crypt4GHError::NoKey)?; + let encrypted_data = encrypt_segment(data, *nonce, &key); write_buffer.write_all(&encrypted_data)?; break; } @@ -186,18 +192,20 @@ pub fn encrypt( // Not a full segment if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12)) - .ok_or(Crypt4GHError::NoRandomNonce)?; - let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, nonce, &key); + let nonce = Nonce::from_slice(&rnd.get_seed()); + //.ok_or(Crypt4GHError::NoRandomNonce)?; + let key = Key::from_slice(&session_key); + //.ok_or(Crypt4GHError::NoKey)?; + let encrypted_data = encrypt_segment(data, *nonce, &key); write_buffer.write_all(&encrypted_data)?; break; } - let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12)) - .ok_or(Crypt4GHError::NoRandomNonce)?; - let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(&segment, nonce, &key); + let nonce = Nonce::from_slice(&rnd.get_seed()); + //.ok_or(Crypt4GHError::NoRandomNonce)?; + let key = Key::from_slice(&session_key); + //.ok_or(Crypt4GHError::NoKey)?; + let encrypted_data = encrypt_segment(&segment, *nonce, &key); write_buffer.write_all(&encrypted_data)?; remaining_length -= segment_len; @@ -219,7 +227,9 @@ pub fn encrypt_header( let encryption_method = 0; let session_key_or_new = session_key.unwrap_or_else(|| { let mut session_key = [0_u8; 32]; - sodiumoxide::randombytes::randombytes_into(&mut session_key); + let rnd = rand_chacha::ChaCha20Rng::from_entropy(); + + session_key = rnd.get_seed(); // TODO: Double check this too session_key }); let header_content = header::make_packet_data_enc(encryption_method, &session_key_or_new); @@ -232,7 +242,9 @@ pub fn encrypt_header( /// /// Returns [ nonce + `encrypted_data` ]. pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Vec { - vec![nonce.0.to_vec(), chacha20poly1305_ietf::seal(data, None, &nonce, key)].concat() + let cipher = ChaCha20Poly1305::new(key); + let ciphertext = cipher.encrypt(&nonce, data); + vec![nonce.to_vec(), ].concat() } /// Reads from the `read_buffer` and writes the decrypted data to `write_buffer`. @@ -529,12 +541,16 @@ pub fn body_decrypt( fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { let (nonce_slice, data) = ciphersegment.split_at(12); - let nonce = Nonce::from_slice(nonce_slice).ok_or(Crypt4GHError::UnableToWrapNonce)?; - - session_keys - .iter() - .find_map(|key| Key::from_slice(key).and_then(|key| chacha20poly1305::open(data, None, &nonce, &key).ok())) - .ok_or(Crypt4GHError::UnableToDecryptBlock) + let nonce = Nonce::from_slice(nonce_slice);//.ok_or(Crypt4GHError::UnableToWrapNonce)?; + let key_slice = Key::from_slice(session_keys); + + let cipher = ChaCha20Poly1305::new(session_keys); + //chacha20poly1305::open(data, None, &nonce, &key).ok()) + // session_keys + // .iter() + // .map(|key| ) + //.ok_or(Crypt4GHError::UnableToDecryptBlock) + todo!() } /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. From b0f8d92ef4b03badeed513df1c405086cd65edda Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 24 Jul 2023 15:07:43 +1000 Subject: [PATCH 13/64] Fix generate_private_key --- src/keys.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 2f944ad..e761f44 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -336,7 +336,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; - Aes128Ctr::new(key.into(), &iv.into()); + Aes128Ctr::new(key.into(), &iv); }, // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) // .decrypt(&mut reader, &mut writer, true) @@ -511,10 +511,11 @@ fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt /// It generates 32 random bytes and calculates the public key using the curve25519 algorithm. /// The resulting private key has a length of 64. The first 32 bytes belong to the secret key, /// the last 32 bytes belong to the public key. -pub fn generate_private_key() -> Vec { - let seckey = chacha20poly1305::ChaCha20Poly1305::generate_key(OsRng); - let pubkey = get_public_key_from_private_key(&seckey.into()); - vec![seckey, pubkey] +pub fn generate_private_key() -> Result, Crypt4GHError> { + let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); + let pubkey = get_public_key_from_private_key(&seckey)?; + assert_eq!(seckey.len(), pubkey.len()); + Ok(vec![seckey, pubkey].concat()) } /// Generates a pair of `Crypt4GH` keys. @@ -529,7 +530,7 @@ pub fn generate_keys( passphrase_callback: impl Fn() -> Result, comment: Option, ) -> Result<(), Crypt4GHError> { - let skpk = generate_private_key(); + let skpk = generate_private_key()?; log::debug!("Private Key: {:02x?}", skpk.iter().format("")); // Public key permissions (read & write) From b44bab0bf9c3841c728c82e2cc91b0b38db34611 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 24 Jul 2023 15:08:13 +1000 Subject: [PATCH 14/64] Fix issue https://github.com/EGA-archive/crypt4gh-rust/issues/2 --- src/error.rs | 4 ++++ src/header.rs | 16 +++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0249304..6f1a59f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -100,6 +100,10 @@ pub enum Crypt4GHError { ReadCheckNumber2Error, #[error("Unable to read magic word from private key (ERROR = {0:?})")] ReadMagicWord(Box), + #[error("Not a CRYPT4GH formatted file")] + MagicStringError, + #[error("Unsupported CRYPT4GH version (version = {0:?})")] + InvalidCrypt4GHVersion(u32), #[error("Empty public key at {0:?}")] EmptyPublicKey(PathBuf), #[error("Secret key not found: {0}")] diff --git a/src/header.rs b/src/header.rs index 5a697bc..3720a87 100644 --- a/src/header.rs +++ b/src/header.rs @@ -331,15 +331,13 @@ pub fn deconstruct_header_info( let header_info = bincode::deserialize::(header_info_file).map_err(|e| Crypt4GHError::ReadHeaderError(e))?; - assert!( - &header_info.magic_number == MAGIC_NUMBER, - "Not a CRYPT4GH formatted file" - ); - assert!( - header_info.version == VERSION, - "Unsupported CRYPT4GH version (version = {})", - header_info.version - ); + if &header_info.magic_number != MAGIC_NUMBER { + return Err(Crypt4GHError::MagicStringError); + } + + if header_info.version != VERSION { + return Err(Crypt4GHError::InvalidCrypt4GHVersion(header_info.version)); + } Ok(header_info) } From 87c42e8fc43268054175bb38c41e03ce285ca8a6 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 24 Jul 2023 15:25:54 +1000 Subject: [PATCH 15/64] Testing out apply_keystream buffer to buffer method on decipher() match, how to not clone BufWriter(mut) on the second argument, @mmalenic? --- src/keys.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index e761f44..b423842 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,7 +1,7 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] -use aes::cipher::generic_array::GenericArray; +use aes::cipher::{StreamCipher, generic_array::GenericArray}; use rand::{SeedableRng, Rng}; use rand_chacha; @@ -335,8 +335,10 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< match ciphername { "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; + let iv_ga = GenericArray::from_slice(iv); - Aes128Ctr::new(key.into(), &iv); + let cipher = Aes128Ctr::new(key.into(), iv_ga); + cipher.apply_keystream_b2b(reader.buffer(), writer) }, // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) // .decrypt(&mut reader, &mut writer, true) From bf788faaae4e29e32e1fac9495595ccfbf1192c9 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 24 Jul 2023 17:00:59 +1000 Subject: [PATCH 16/64] Converted most of the asserts to proper Crypt4GH errors, a few more to go, WIP to solve https://github.com/EGA-archive/crypt4gh-rust/issues/2 --- src/error.rs | 11 +++++++---- src/header.rs | 6 +++++- src/keys.rs | 28 ++++++++++++++-------------- src/lib.rs | 25 ++++++++++++++++++------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/error.rs b/src/error.rs index 6f1a59f..fe1d456 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use std::error::Error; -use std::path::PathBuf; +use std::path::{ Path, PathBuf }; use thiserror::Error; @@ -12,7 +12,6 @@ pub enum Crypt4GHError { InvalidRangeSpan(Option), #[error("The edit list is empty")] EmptyEditList, - // Sodiumoxide errors #[error("Unable to create random nonce")] NoRandomNonce, @@ -28,8 +27,10 @@ pub enum Crypt4GHError { // DecryptKeyError(SymmetricCipherError), #[error("Invalid key format")] InvalidKeyFormat, - // #[error("Invalid key length")] - // InvalidKeyLength(#[from] crypto_kx::errors::InvalidLength), + #[error("Invalid PEM file length. The file ({0:?}) is not 3 lines long")] + InvalidPEMFormatLength(&'static Path), + #[error("Invalid PEM file header or footer: -----BEGIN or -----END")] + InvalidPEMHeaderOrFooter, #[error("Invalid SSH key format")] InvalidSSHKey, #[error("Unable to wrap nonce")] @@ -74,6 +75,8 @@ pub enum Crypt4GHError { // Reading errors #[error("Unable to read {0} bytes from input (ERROR = {1:?})")] NotEnoughInput(usize, Box), + #[error("You shouldn't skip 0 bytes")] + SkipZeroBytes, #[error("Unable to read header info (ERROR = {0:?})")] ReadHeaderError(Box), #[error("Unable to read header packet length (ERROR = {0:?})")] diff --git a/src/header.rs b/src/header.rs index 3720a87..afd9448 100644 --- a/src/header.rs +++ b/src/header.rs @@ -384,6 +384,11 @@ pub fn rearrange<'a>( range_span: Option, sender_pubkey: &Option>, ) -> Result<(Vec>, impl Iterator + 'a), Crypt4GHError> { + if range_span <= Some(0) { + //assert!(span > 0, "Span should be greater than 0"); + return Err(Crypt4GHError::InvalidRangeSpan(range_span)); + } + log::info!("Rearranging the header"); log::debug!(" Start coordinate: {}", range_start); @@ -393,7 +398,6 @@ pub fn rearrange<'a>( }, |span| { log::debug!(" End coordinate: {}", range_start + span); - assert!(span > 0, "Span should be greater than 0"); }, ); log::debug!(" Segment size: {}", SEGMENT_SIZE); diff --git a/src/keys.rs b/src/keys.rs index b423842..96bd8fd 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -71,17 +71,14 @@ fn load_from_pem(filepath: &Path) -> Result, Crypt4GHError> { let lines = read_lines(filepath).map_err(|e| Crypt4GHError::ReadLinesError(filepath.into(), e.into()))?; // Check format - assert!(lines.len() >= 3, "The file ({:?}) is not 3 lines long", filepath); - assert!( - lines.first().unwrap().starts_with("-----BEGIN "), - "The file ({:?}) does not start with -----BEGIN", - filepath - ); - assert!( - lines.last().unwrap().starts_with("-----END "), - "The file ({:?}) does not end with -----END", - filepath - ); + if lines.len() < 3 { + return Err(Crypt4GHError::InvalidPEMFormatLength(filepath)); + } + + if lines.first().unwrap().starts_with("-----BEGIN ") || + lines.last().unwrap().starts_with("-----END ") { + return Err(Crypt4GHError::InvalidPEMHeaderOrFooter); + } // Decode with base64 general_purpose::STANDARD.decode(&lines[1..lines.len() - 1].join("")).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) @@ -257,8 +254,11 @@ fn parse_ssh_private_key( .map_err(|_| Crypt4GHError::ReadRoundsError)?; rounds = Some(u32::from_be_bytes(buf)); - // Assert - assert!(kdfoptions_cursor.read_exact(&mut [0_u8]).is_err()); + // Make sure kdfoptions are initialized to other values than 0 + if kdfoptions_cursor.read_exact(&mut [0_u8]).is_ok() { + return Err(Crypt4GHError::BadKey); + } + //assert!(kdfoptions_cursor.read_exact(&mut [0_u8]).is_err()); // Log log::debug!("Salt: {:02x?}", salt.iter().format("")); @@ -338,7 +338,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let iv_ga = GenericArray::from_slice(iv); let cipher = Aes128Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer) + cipher.apply_keystream_b2b(reader.buffer(), &mut writer.buffer()) }, // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) // .decrypt(&mut reader, &mut writer, true) diff --git a/src/lib.rs b/src/lib.rs index b30d7e9..8eb1403 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -382,9 +382,13 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!(""); } - fn skip(&mut self, size: usize) { - assert!(size > 0, "You shouldn't skip 0 bytes"); - log::debug!("Skipping {} bytes | Buffer size: {}", size, self.buf.len()); + fn skip(&mut self, size: usize) -> Result<(), Crypt4GHError> { + if size <= 0 { + //return Err(Crypt4GHError::NotEnoughInput(size, self.buf.len())); + return Err(Crypt4GHError::SkipZeroBytes) + } + // assert!(size > 0, "You shouldn't skip 0 bytes"); + // log::debug!("Skipping {} bytes | Buffer size: {}", size, self.buf.len()); let mut remaining_size = size; @@ -411,11 +415,18 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { // Apply self.decrypt(); + + Ok(()) } - fn read(&mut self, size: usize) -> usize { - assert!(size > 0, "You shouldn't read 0 bytes"); - log::debug!("Reading {} bytes | Buffer size: {}", size, self.buf.len()); + fn read(&mut self, size: usize) -> Result { + if size <= 0 { + //return Err(Crypt4GHError::NotEnoughInput(size, self.buf.len())); + return Err(Crypt4GHError::SkipZeroBytes) + } + + // assert!(size > 0, "You shouldn't read 0 bytes"); + // log::debug!("Reading {} bytes | Buffer size: {}", size, self.buf.len()); let mut remaining_size = size; @@ -444,7 +455,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!("Finished reading"); log::debug!(""); - size + Ok(size) } } From 536e9117d1dc703d033364d6033a4b2a78faba32 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 26 Jul 2023 13:49:14 +1000 Subject: [PATCH 17/64] Only output should be mut? No need to use Rc I hope /cc @mmalenic --- src/keys.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 96bd8fd..f1554ac 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -326,8 +326,8 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< log::debug!("IV ({}): {:02x?}", iv.len(), iv.iter().format("")); let mut output = vec![0_u8; private_ciphertext.len()]; - let mut reader = BufReader::new(private_ciphertext); - let mut writer = BufWriter::new(&mut output); + let reader = BufReader::new(private_ciphertext); + let writer = BufWriter::new(&mut output); assert!((private_ciphertext.len() % block_size(ciphername)?) == 0); @@ -337,7 +337,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< type Aes128Ctr = ctr::Ctr128LE; let iv_ga = GenericArray::from_slice(iv); - let cipher = Aes128Ctr::new(key.into(), iv_ga); + let mut cipher = Aes128Ctr::new(key.into(), iv_ga); cipher.apply_keystream_b2b(reader.buffer(), &mut writer.buffer()) }, // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) From 05a67dd620b55d692c1de8d7f61c39574660f60e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 26 Jul 2023 16:01:33 +1000 Subject: [PATCH 18/64] There seem to be some missing gaps on AES CTR and CBC types that we might need to cover... --- Cargo.lock | 20 ++++++++++++++++++++ Cargo.toml | 1 + src/keys.rs | 39 ++++++++++++++++++++------------------- src/lib.rs | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3f3ae7..b491974 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "blowfish" version = "0.9.1" @@ -116,6 +125,15 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.0.73" @@ -217,6 +235,7 @@ dependencies = [ "base64", "bcrypt-pbkdf", "bincode", + "cbc", "chacha20poly1305", "clap", "crypto_kx", @@ -392,6 +411,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ + "block-padding", "generic-array", ] diff --git a/Cargo.toml b/Cargo.toml index 7495eac..4e649fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ scrypt = { version = "0.11" } bcrypt-pbkdf = { version = "0.10" } aes = { version = "0.8" } ctr = { version = "0.9" } +cbc = { version = "0.1" } bincode = "1" serde = { version = "1", features = ["derive"] } diff --git a/src/keys.rs b/src/keys.rs index f1554ac..ca34362 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -22,6 +22,7 @@ use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore, consts::U12}; use ctr; +use cbc; use crate::error::Crypt4GHError; @@ -340,25 +341,25 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let mut cipher = Aes128Ctr::new(key.into(), iv_ga); cipher.apply_keystream_b2b(reader.buffer(), &mut writer.buffer()) }, - // "aes128-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize128, key, iv) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "aes192-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize192, key, iv) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "aes256-ctr" => crypto::aes::ctr(crypto::aes::KeySize::KeySize256, key, iv) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "aes128-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize128, key, iv, NoPadding) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "aes192-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize192, key, iv, NoPadding) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "aes256-cbc" => crypto::aes::cbc_decryptor(crypto::aes::KeySize::KeySize256, key, iv, NoPadding) - // .decrypt(&mut reader, &mut writer, true) - // .map_err(Crypt4GHError::DecryptKeyError)?, - // "3des-cbc" => unimplemented!(), + "aes192-ctr" => { + //type Aes192Ctr = ctr::Ctr192; // Ctr192 not implemented in RustCrypto ctr crate! + unimplemented!() + }, + "aes256-ctr" => { + //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! + unimplemented!() + }, + "aes128-cbc" => { + //type Aes128Cbc = cbc::cipher::BlockCipher:: // No analogous type? + unimplemented!() + }, + "aes192-cbc" => { + unimplemented!() // Ditto above + }, + "aes256-cbc" => { + unimplemented!() // Ditto above + }, + "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), }; Ok(output) diff --git a/src/lib.rs b/src/lib.rs index 8eb1403..a35654e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -495,7 +495,7 @@ pub fn body_decrypt_parts( if !skip { // If we finished with a skip, read until the end loop { - let n = decrypted.read(SEGMENT_SIZE); + let n = decrypted.read(SEGMENT_SIZE)?; if n == 0 { break; } From be17e1118724da8101767f4349a6af1dc3f46c3b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 26 Jul 2023 16:14:49 +1000 Subject: [PATCH 19/64] Must revisit seed and encrypt_segment() later, focusing on mut issues with the writer and output now --- src/keys.rs | 8 ++++---- src/lib.rs | 32 +++++++++++++++++--------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index ca34362..7feceab 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -16,13 +16,13 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; -use aes::cipher::{KeyIvInit}; +use aes::cipher::KeyIvInit; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore, consts::U12}; use ctr; -use cbc; +//use cbc; use crate::error::Crypt4GHError; @@ -67,7 +67,7 @@ where .collect()) } -fn load_from_pem(filepath: &Path) -> Result, Crypt4GHError> { +fn load_from_pem(filepath: &'static Path) -> Result, Crypt4GHError> { // Read lines let lines = read_lines(filepath).map_err(|e| Crypt4GHError::ReadLinesError(filepath.into(), e.into()))?; @@ -339,7 +339,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let iv_ga = GenericArray::from_slice(iv); let mut cipher = Aes128Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), &mut writer.buffer()) + cipher.apply_keystream_b2b(reader.buffer(), writer.buffer()) }, "aes192-ctr" => { //type Aes192Ctr = ctr::Ctr192; // Ctr192 not implemented in RustCrypto ctr crate! diff --git a/src/lib.rs b/src/lib.rs index a35654e..44a12a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,6 +147,7 @@ pub fn encrypt( log::info!("Streaming content"); let mut segment = [0_u8; SEGMENT_SIZE]; + let seed = &rnd.get_seed(); // The whole file match range_span { @@ -157,19 +158,19 @@ pub fn encrypt( } else if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&rnd.get_seed()); + let nonce = Nonce::from_slice(seed); //.map_err(|_| Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, *nonce, &key); + let encrypted_data = encrypt_segment(data, *nonce, &key)?; write_buffer.write_all(&encrypted_data)?; break; } else { - let nonce = Nonce::from_slice(&rnd.get_seed()); + let nonce = Nonce::from_slice(seed); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key);//.ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(&segment, *nonce, &key); + let encrypted_data = encrypt_segment(&segment, *nonce, &key)?; write_buffer.write_all(&encrypted_data)?; } }, @@ -180,11 +181,11 @@ pub fn encrypt( // Stop if segment_len >= remaining_length { let (data, _) = segment.split_at(remaining_length); - let nonce = Nonce::from_slice(&rnd.get_seed()); + let nonce = Nonce::from_slice(seed); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, *nonce, &key); + let encrypted_data = encrypt_segment(data, *nonce, &key)?; write_buffer.write_all(&encrypted_data)?; break; } @@ -192,20 +193,20 @@ pub fn encrypt( // Not a full segment if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&rnd.get_seed()); + let nonce = Nonce::from_slice(seed); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(data, *nonce, &key); + let encrypted_data = encrypt_segment(data, *nonce, &key)?; write_buffer.write_all(&encrypted_data)?; break; } - let nonce = Nonce::from_slice(&rnd.get_seed()); + let nonce = Nonce::from_slice(seed); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; - let encrypted_data = encrypt_segment(&segment, *nonce, &key); + let encrypted_data = encrypt_segment(&segment, *nonce, &key)?; write_buffer.write_all(&encrypted_data)?; remaining_length -= segment_len; @@ -241,10 +242,10 @@ pub fn encrypt_header( /// Encrypts a segment. /// /// Returns [ nonce + `encrypted_data` ]. -pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Vec { +pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Result, Crypt4GHError> { let cipher = ChaCha20Poly1305::new(key); - let ciphertext = cipher.encrypt(&nonce, data); - vec![nonce.to_vec(), ].concat() + let _ciphertext = cipher.encrypt(&nonce, data); + Ok(vec![nonce.to_vec(), ].concat()) } /// Reads from the `read_buffer` and writes the decrypted data to `write_buffer`. @@ -553,9 +554,10 @@ pub fn body_decrypt( fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { let (nonce_slice, data) = ciphersegment.split_at(12); let nonce = Nonce::from_slice(nonce_slice);//.ok_or(Crypt4GHError::UnableToWrapNonce)?; - let key_slice = Key::from_slice(session_keys); + + //let key_slice = Key::from_slice(); - let cipher = ChaCha20Poly1305::new(session_keys); + //let cipher = ChaCha20Poly1305::new(session_keys); //chacha20poly1305::open(data, None, &nonce, &key).ok()) // session_keys // .iter() From 8d655c5b3f3a66b4bb09b9a111827e9d120e6404 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 7 Aug 2023 08:57:13 +1000 Subject: [PATCH 20/64] Narrow down the fixing to other functions... --- src/keys.rs | 190 +++++++++++++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 90 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 7feceab..2beff6c 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; use std::path::Path; +use std::sync::Arc; use base64::engine::general_purpose; use base64::Engine; @@ -16,13 +17,13 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; -use aes::cipher::KeyIvInit; +use aes::cipher::{KeyIvInit, ArrayLength}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; -use chacha20poly1305::{self, ChaCha20Poly1305, KeyInit, AeadCore, consts::U12}; +use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; use ctr; -//use cbc; +use cbc; use crate::error::Crypt4GHError; @@ -213,10 +214,11 @@ fn parse_c4gh_private_key( let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; let encrypted_data = &private_data[12..]; - let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) - .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; + todo!(); + // let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) + // .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; - Ok(privkey_plain) + // Ok(privkey_plain) } fn parse_ssh_private_key( @@ -326,9 +328,9 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< log::debug!("Decryption Key ({}): {:02x?}", key.len(), key); log::debug!("IV ({}): {:02x?}", iv.len(), iv.iter().format("")); - let mut output = vec![0_u8; private_ciphertext.len()]; + let output = vec![0_u8; private_ciphertext.len()]; let reader = BufReader::new(private_ciphertext); - let writer = BufWriter::new(&mut output); + let mut writer = BufWriter::new(output); assert!((private_ciphertext.len() % block_size(ciphername)?) == 0); @@ -339,30 +341,35 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let iv_ga = GenericArray::from_slice(iv); let mut cipher = Aes128Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer.buffer()) + //let output = Arc::new(writer.buffer()); + + //let buf = writer.get_mut(); + + cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()) }, "aes192-ctr" => { - //type Aes192Ctr = ctr::Ctr192; // Ctr192 not implemented in RustCrypto ctr crate! - unimplemented!() + //type Aes192Ctr = ctr::CtrFlavor<192>; + todo!() }, "aes256-ctr" => { //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! - unimplemented!() + todo!() }, "aes128-cbc" => { - //type Aes128Cbc = cbc::cipher::BlockCipher:: // No analogous type? - unimplemented!() + todo!() }, "aes192-cbc" => { - unimplemented!() // Ditto above + todo!() // Ditto above }, "aes256-cbc" => { - unimplemented!() // Ditto above + todo!() // Ditto above }, "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), }; - Ok(output) + + let a = writer.into_inner().unwrap(); + Ok(a) } fn block_size(ciphername: &str) -> Result { @@ -421,28 +428,29 @@ pub fn get_private_key( key_path: &Path, callback: impl Fn() -> Result, ) -> Result, Crypt4GHError> { - let data = load_from_pem(key_path)?; - - if data.starts_with(C4GH_MAGIC_WORD) { - log::info!("Loading a Crypt4GH private key"); - let mut stream = BufReader::new(data.as_slice()); - stream - .read_exact(&mut [0_u8; C4GH_MAGIC_WORD.len()]) - .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - parse_c4gh_private_key(stream, callback) - } - else if data.starts_with(SSH_MAGIC_WORD) { - log::info!("Loading an OpenSSH private key"); - let mut stream = BufReader::new(data.as_slice()); - stream - .read_exact(&mut [0_u8; SSH_MAGIC_WORD.len()]) - .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - let (seckey, pubkey) = parse_ssh_private_key(stream, callback)?; - Ok(vec![seckey, pubkey].concat()) - } - else { - Err(Crypt4GHError::InvalidKeyFormat) - } + todo!(); + // let data = load_from_pem(key_path)?; + + // if data.starts_with(C4GH_MAGIC_WORD) { + // log::info!("Loading a Crypt4GH private key"); + // let mut stream = BufReader::new(data.as_slice()); + // stream + // .read_exact(&mut [0_u8; C4GH_MAGIC_WORD.len()]) + // .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; + // parse_c4gh_private_key(stream, callback) + // } + // else if data.starts_with(SSH_MAGIC_WORD) { + // log::info!("Loading an OpenSSH private key"); + // let mut stream = BufReader::new(data.as_slice()); + // stream + // .read_exact(&mut [0_u8; SSH_MAGIC_WORD.len()]) + // .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; + // let (seckey, pubkey) = parse_ssh_private_key(stream, callback)?; + // Ok(vec![seckey, pubkey].concat()) + // } + // else { + // Err(Crypt4GHError::InvalidKeyFormat) + // } } /// Reads and decodes the public key stored in `key_path`. @@ -515,10 +523,11 @@ fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt /// The resulting private key has a length of 64. The first 32 bytes belong to the secret key, /// the last 32 bytes belong to the public key. pub fn generate_private_key() -> Result, Crypt4GHError> { - let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); - let pubkey = get_public_key_from_private_key(&seckey)?; - assert_eq!(seckey.len(), pubkey.len()); - Ok(vec![seckey, pubkey].concat()) + todo!(); + // let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); + // let pubkey = get_public_key_from_private_key(&seckey)?; + // assert_eq!(seckey.len(), pubkey.len()); + // Ok(vec![seckey, pubkey].concat()) } /// Generates a pair of `Crypt4GH` keys. @@ -578,53 +587,54 @@ fn encode_string_c4gh(s: Option<&[u8]>) -> Vec { } fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> Result, Crypt4GHError> { - Ok(if passphrase.is_empty() { - log::warn!("The private key is not encrypted"); - vec![ - C4GH_MAGIC_WORD.to_vec(), - encode_string_c4gh(None), // KDF = None - encode_string_c4gh(None), // Cipher = None - encode_string_c4gh(Some(skpk)), - match comment { - Some(c) => encode_string_c4gh(Some(c.as_bytes())), - None => [].to_vec(), - }, - ] - .concat() - } - else { - let kdfname = "scrypt"; - let (salt_size, rounds) = get_kdf(kdfname)?; - let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).gen::<[u8;10]>(); // TODO: This is wrong X"D - - let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); - let key = chacha20poly1305::Key::from_slice(&derived_key); - let salt_ga = GenericArray::from_slice(salt.as_slice()); - - let encrypted_key = ChaCha20Poly1305::new(&key) - .encrypt(salt_ga, skpk) - .map_err(|_| Crypt4GHError::BadKey)?; - - let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); - - log::debug!("Derived Key: {:02x?}", derived_key); - log::debug!("Salt: {:02x?}", salt); - log::debug!("Nonce: {:02x?}", nonce); - - vec![ - C4GH_MAGIC_WORD.to_vec(), - encode_string_c4gh(Some(kdfname.as_bytes())), - encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), - encode_string_c4gh(Some(b"chacha20_poly1305")), - encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), - match comment { - Some(c) => encode_string_c4gh(Some(c.as_bytes())), - None => [].to_vec(), - }, - ] - .concat() - }) + todo!(); + // Ok(if passphrase.is_empty() { + // log::warn!("The private key is not encrypted"); + // vec![ + // C4GH_MAGIC_WORD.to_vec(), + // encode_string_c4gh(None), // KDF = None + // encode_string_c4gh(None), // Cipher = None + // encode_string_c4gh(Some(skpk)), + // match comment { + // Some(c) => encode_string_c4gh(Some(c.as_bytes())), + // None => [].to_vec(), + // }, + // ] + // .concat() + // } + // else { + // let kdfname = "scrypt"; + // let (salt_size, rounds) = get_kdf(kdfname)?; + // let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).gen::<[u8;10]>(); // TODO: This is wrong X"D + + // let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; + // let nonce = ChaCha20Poly1305::generate_nonce(OsRng); + // let key = chacha20poly1305::Key::from_slice(&derived_key); + // let salt_ga = GenericArray::from_slice(salt.as_slice()); + + // let encrypted_key = ChaCha20Poly1305::new(&key) + // .encrypt(salt_ga, skpk) + // .map_err(|_| Crypt4GHError::BadKey)?; + + // let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); + + // log::debug!("Derived Key: {:02x?}", derived_key); + // log::debug!("Salt: {:02x?}", salt); + // log::debug!("Nonce: {:02x?}", nonce); + + // vec![ + // C4GH_MAGIC_WORD.to_vec(), + // encode_string_c4gh(Some(kdfname.as_bytes())), + // encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), + // encode_string_c4gh(Some(b"chacha20_poly1305")), + // encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), + // match comment { + // Some(c) => encode_string_c4gh(Some(c.as_bytes())), + // None => [].to_vec(), + // }, + // ] + // .concat() + // }) } fn get_kdf(kdfname: &str) -> Result<(usize, u32), Crypt4GHError> { From 91dfea31d85ea1abe8afdc77f880ebc6f9169ee1 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Aug 2023 10:57:18 +1000 Subject: [PATCH 21/64] Migrate away from Path towards PathBuf, see https://github.com/rust-lang/rust/issues/23286 --- src/bin.rs | 17 ++++----- src/error.rs | 2 +- src/keys.rs | 80 ++++++++++++++++++++---------------------- tests/edit_list_gen.rs | 2 +- 4 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index f5b1b2b..0d295f1 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -74,7 +74,7 @@ fn retrieve_private_key(sk: Option, generate: bool) -> Result, let seckey_path = sk; if generate && seckey_path.is_none() { - let skey = keys::generate_private_key(); + let skey = keys::generate_private_key()?; log::info!("Generating Private Key: {:02x?}", skey.iter().format("")); Ok(skey) } @@ -95,7 +95,7 @@ fn retrieve_private_key(sk: Option, generate: bool) -> Result, }), }; - get_private_key(&path, callback) + get_private_key(path.to_owned(), callback) } } @@ -111,7 +111,7 @@ fn build_recipients(recipient_pk: &[PathBuf], sk: &[u8]) -> Result Ok(Keys { method: 0, privkey: sk.to_vec(), - recipient_pubkey: get_public_key(Path::new(pk))?, + recipient_pubkey: get_public_key(PathBuf::from(pk))?, }) }) .collect() @@ -138,7 +138,7 @@ fn run_encrypt(sk: Option, recipient_pk: &[PathBuf], range: Option, sender_pk: Option, range: Option) -> Result<(), Crypt4GHError> { let sender_pubkey = match sender_pk { - Some(path) => Some(keys::get_public_key(&path)?), + Some(path) => Some(keys::get_public_key(path)?), None => None, }; @@ -193,13 +193,13 @@ fn run_reencrypt(sk: Option, recipient_pk: &[PathBuf], trim: bool) -> R crypt4gh::reencrypt(&keys, &recipient_keys, &mut io::stdin(), &mut io::stdout(), trim) } -fn run_keygen(sk: &Path, pk: &Path, comment: Option, nocrypt: bool, force: bool) -> Result<(), Crypt4GHError> { +fn run_keygen(sk: PathBuf, pk: PathBuf, comment: Option, nocrypt: bool, force: bool) -> Result<(), Crypt4GHError> { // Prepare key files let seckey = sk; - let pubkey = &pk; + let pubkey = pk; - for key in &[seckey, pubkey] { + for key in &[seckey, pubkey.to_owned()] { // If key exists and it is a file if key.is_file() { // Force overwrite? @@ -221,6 +221,7 @@ fn run_keygen(sk: &Path, pk: &Path, comment: Option, nocrypt: bool, forc // Comment let comment = comment; let do_crypt = !nocrypt; + let passphrase_callback = move || { if do_crypt { prompt_password(format!("Passphrase for {}: ", seckey.display())) @@ -263,7 +264,7 @@ fn run() -> Result<(), Crypt4GHError> { comment, nocrypt, force, - } => run_keygen(&sk, &pk, comment, nocrypt, force)?, + } => run_keygen(sk, pk, comment, nocrypt, force)?, } Ok(()) diff --git a/src/error.rs b/src/error.rs index fe1d456..130350c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,7 +28,7 @@ pub enum Crypt4GHError { #[error("Invalid key format")] InvalidKeyFormat, #[error("Invalid PEM file length. The file ({0:?}) is not 3 lines long")] - InvalidPEMFormatLength(&'static Path), + InvalidPEMFormatLength(PathBuf), #[error("Invalid PEM file header or footer: -----BEGIN or -----END")] InvalidPEMHeaderOrFooter, #[error("Invalid SSH key format")] diff --git a/src/keys.rs b/src/keys.rs index 2beff6c..77247eb 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,14 +2,11 @@ #![warn(rustdoc::missing_doc_code_examples)] use aes::cipher::{StreamCipher, generic_array::GenericArray}; -use rand::{SeedableRng, Rng}; -use rand_chacha; use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Read, Write, BufWriter}; -use std::path::Path; -use std::sync::Arc; +use std::path::PathBuf; use base64::engine::general_purpose; use base64::Engine; @@ -57,9 +54,8 @@ lazy_static! { .collect(); } -fn read_lines

(filename: P) -> Result, Crypt4GHError> +fn read_lines(filename: &PathBuf) -> Result, Crypt4GHError> where - P: AsRef, { let file = File::open(filename)?; Ok(BufReader::new(file) @@ -68,13 +64,14 @@ where .collect()) } -fn load_from_pem(filepath: &'static Path) -> Result, Crypt4GHError> { +fn load_from_pem(filepath: &PathBuf) -> Result, Crypt4GHError> { // Read lines - let lines = read_lines(filepath).map_err(|e| Crypt4GHError::ReadLinesError(filepath.into(), e.into()))?; + let lines = read_lines(&filepath) + .map_err(|e| Crypt4GHError::ReadLinesError(filepath.to_owned(), Box::new(e)))?; // Check format if lines.len() < 3 { - return Err(Crypt4GHError::InvalidPEMFormatLength(filepath)); + return Err(Crypt4GHError::InvalidPEMFormatLength(filepath.into())); } if lines.first().unwrap().starts_with("-----BEGIN ") || @@ -83,7 +80,7 @@ fn load_from_pem(filepath: &'static Path) -> Result, Crypt4GHError> { } // Decode with base64 - general_purpose::STANDARD.decode(&lines[1..lines.len() - 1].join("")).map_err(|e| Crypt4GHError::BadBase64Error(e.into())) + general_purpose::STANDARD.decode(&lines[1..lines.len() - 1].join("")).map_err(move |e| Crypt4GHError::BadBase64Error(e.into())) } fn decode_string_ssh(stream: &mut impl BufRead) -> Result, Crypt4GHError> { @@ -124,9 +121,10 @@ fn derive_key( match alg { "scrypt" => { // TODO: Review last param of ScryptParams (length of what, exactly?) carefully. + // TODO: Why is the output not used? // Added "dklen" for now since it seemed fitting, but needs proper review. let params = scrypt::Params::new(14, 8, 1, dklen); - scrypt::scrypt( + let _ = scrypt::scrypt( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); @@ -137,7 +135,7 @@ fn derive_key( ); }, "bcrypt" => { - bcrypt_pbkdf::bcrypt_pbkdf( + let _ = bcrypt_pbkdf::bcrypt_pbkdf( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); @@ -335,7 +333,8 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< assert!((private_ciphertext.len() % block_size(ciphername)?) == 0); // Decipher - match ciphername { + // TODO: Why is this match not used? Was it used upstream? + let _ = match ciphername { "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; let iv_ga = GenericArray::from_slice(iv); @@ -425,41 +424,40 @@ fn get_skpk_from_decrypted_private_blob(blob: &[u8]) -> Result<([u8; 32], [u8; 3 /// or if the key is not one of the two supported formats. Returns the decode key. /// If the key is encrypted, the `callback` should return the passphrase of the key. pub fn get_private_key( - key_path: &Path, + key_path: PathBuf, callback: impl Fn() -> Result, ) -> Result, Crypt4GHError> { - todo!(); - // let data = load_from_pem(key_path)?; - - // if data.starts_with(C4GH_MAGIC_WORD) { - // log::info!("Loading a Crypt4GH private key"); - // let mut stream = BufReader::new(data.as_slice()); - // stream - // .read_exact(&mut [0_u8; C4GH_MAGIC_WORD.len()]) - // .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - // parse_c4gh_private_key(stream, callback) - // } - // else if data.starts_with(SSH_MAGIC_WORD) { - // log::info!("Loading an OpenSSH private key"); - // let mut stream = BufReader::new(data.as_slice()); - // stream - // .read_exact(&mut [0_u8; SSH_MAGIC_WORD.len()]) - // .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - // let (seckey, pubkey) = parse_ssh_private_key(stream, callback)?; - // Ok(vec![seckey, pubkey].concat()) - // } - // else { - // Err(Crypt4GHError::InvalidKeyFormat) - // } + let data = load_from_pem(&key_path)?; + + if data.starts_with(C4GH_MAGIC_WORD) { + log::info!("Loading a Crypt4GH private key"); + let mut stream = BufReader::new(data.as_slice()); + stream + .read_exact(&mut [0_u8; C4GH_MAGIC_WORD.len()]) + .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; + parse_c4gh_private_key(stream, callback) + } + else if data.starts_with(SSH_MAGIC_WORD) { + log::info!("Loading an OpenSSH private key"); + let mut stream = BufReader::new(data.as_slice()); + stream + .read_exact(&mut [0_u8; SSH_MAGIC_WORD.len()]) + .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; + let (seckey, pubkey) = parse_ssh_private_key(stream, callback)?; + Ok(vec![seckey, pubkey].concat()) + } + else { + Err(Crypt4GHError::InvalidKeyFormat) + } } /// Reads and decodes the public key stored in `key_path`. /// /// It supports `Crypt4GH` and OpenSSH public keys. Fails if it can not read the file /// or if the key is not one of the two supported formats. Returns the decoded key. -pub fn get_public_key(key_path: &Path) -> Result, Crypt4GHError> { +pub fn get_public_key(key_path: PathBuf) -> Result, Crypt4GHError> { // Read lines from public key file - match read_lines(key_path) { + match read_lines(&key_path) { Ok(lines_vec) => { // Empty key if lines_vec.is_empty() { @@ -537,8 +535,8 @@ pub fn generate_private_key() -> Result, Crypt4GHError> { /// The passphrase callback should return a string that will be used to encode the keys. You can add /// an optional comment at the end of the keys. pub fn generate_keys( - seckey: &Path, - pubkey: &Path, + seckey: PathBuf, + pubkey: PathBuf, passphrase_callback: impl Fn() -> Result, comment: Option, ) -> Result<(), Crypt4GHError> { diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index b56910a..3cf711a 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -5,7 +5,7 @@ use std::io::Write; use std::path::Path; use rand::Rng; -use sodiumoxide::crypto::aead::chacha20poly1305_ietf; +//use sodiumoxide::crypto::aead::chacha20poly1305_ietf; pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, passphrase: &str) { let mut rng = rand::thread_rng(); From 4aaa23b36742e2b70b4eb4069a3816a2a40b70fd Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Aug 2023 11:39:46 +1000 Subject: [PATCH 22/64] Remove unnecessary callback and fix PathBuf ownership issues in this context --- src/bin.rs | 15 ++++++++------- src/keys.rs | 5 ++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 0d295f1..f30e6dd 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -199,7 +199,7 @@ fn run_keygen(sk: PathBuf, pk: PathBuf, comment: Option, nocrypt: bool, let seckey = sk; let pubkey = pk; - for key in &[seckey, pubkey.to_owned()] { + for key in &[seckey.to_owned(), pubkey.to_owned()] { // If key exists and it is a file if key.is_file() { // Force overwrite? @@ -221,18 +221,19 @@ fn run_keygen(sk: PathBuf, pk: PathBuf, comment: Option, nocrypt: bool, // Comment let comment = comment; let do_crypt = !nocrypt; - - let passphrase_callback = move || { + let seckey_display = PathBuf::from(&seckey); + + let passphrase = { if do_crypt { - prompt_password(format!("Passphrase for {}: ", seckey.display())) + prompt_password(format!("Passphrase for {}: ", seckey_display.display())) .map_err(|e| Crypt4GHError::NoPassphrase(e.into())) - } - else { + + } else { Ok(String::new()) } }; - crypt4gh::keys::generate_keys(seckey, pubkey, passphrase_callback, comment) + crypt4gh::keys::generate_keys(seckey, pubkey, passphrase, comment) } fn run() -> Result<(), Crypt4GHError> { diff --git a/src/keys.rs b/src/keys.rs index 77247eb..1b2db19 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -537,7 +537,7 @@ pub fn generate_private_key() -> Result, Crypt4GHError> { pub fn generate_keys( seckey: PathBuf, pubkey: PathBuf, - passphrase_callback: impl Fn() -> Result, + passphrase: Result, comment: Option, ) -> Result<(), Crypt4GHError> { let skpk = generate_private_key()?; @@ -560,8 +560,7 @@ pub fn generate_keys( let mut sk_file = File::create(seckey).unwrap(); // Write secret key - let passphrase = passphrase_callback().unwrap(); - let sk_encrypted = encode_private_key(sk, &passphrase, comment)?; + let sk_encrypted = encode_private_key(sk, &passphrase?, comment)?; log::debug!( "Encoded Private Key ({}): {:02x?}", sk_encrypted.len(), From 86e67bb6331f6c15efbecfaf197cab1224fd24b5 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Aug 2023 11:41:36 +1000 Subject: [PATCH 23/64] Fix generate random private key... --- src/keys.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 1b2db19..6332c6c 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -14,7 +14,7 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; -use aes::cipher::{KeyIvInit, ArrayLength}; +use aes::cipher::{KeyInit, KeyIvInit, ArrayLength}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; @@ -521,11 +521,10 @@ fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt /// The resulting private key has a length of 64. The first 32 bytes belong to the secret key, /// the last 32 bytes belong to the public key. pub fn generate_private_key() -> Result, Crypt4GHError> { - todo!(); - // let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); - // let pubkey = get_public_key_from_private_key(&seckey)?; - // assert_eq!(seckey.len(), pubkey.len()); - // Ok(vec![seckey, pubkey].concat()) + let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); + let pubkey = get_public_key_from_private_key(&seckey)?; + assert_eq!(seckey.len(), pubkey.len()); + Ok(vec![seckey, pubkey].concat()) } /// Generates a pair of `Crypt4GH` keys. From 034bcf1672064884ed7ab484d2834cd0dafa48be Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Aug 2023 11:42:01 +1000 Subject: [PATCH 24/64] Fix generate random private key and encode private key --- src/keys.rs | 98 +++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 6332c6c..355657b 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -14,6 +14,8 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; +use rand::SeedableRng; + use aes::cipher::{KeyInit, KeyIvInit, ArrayLength}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; @@ -583,54 +585,54 @@ fn encode_string_c4gh(s: Option<&[u8]>) -> Vec { } fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> Result, Crypt4GHError> { - todo!(); - // Ok(if passphrase.is_empty() { - // log::warn!("The private key is not encrypted"); - // vec![ - // C4GH_MAGIC_WORD.to_vec(), - // encode_string_c4gh(None), // KDF = None - // encode_string_c4gh(None), // Cipher = None - // encode_string_c4gh(Some(skpk)), - // match comment { - // Some(c) => encode_string_c4gh(Some(c.as_bytes())), - // None => [].to_vec(), - // }, - // ] - // .concat() - // } - // else { - // let kdfname = "scrypt"; - // let (salt_size, rounds) = get_kdf(kdfname)?; - // let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).gen::<[u8;10]>(); // TODO: This is wrong X"D - - // let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; - // let nonce = ChaCha20Poly1305::generate_nonce(OsRng); - // let key = chacha20poly1305::Key::from_slice(&derived_key); - // let salt_ga = GenericArray::from_slice(salt.as_slice()); - - // let encrypted_key = ChaCha20Poly1305::new(&key) - // .encrypt(salt_ga, skpk) - // .map_err(|_| Crypt4GHError::BadKey)?; - - // let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); - - // log::debug!("Derived Key: {:02x?}", derived_key); - // log::debug!("Salt: {:02x?}", salt); - // log::debug!("Nonce: {:02x?}", nonce); - - // vec![ - // C4GH_MAGIC_WORD.to_vec(), - // encode_string_c4gh(Some(kdfname.as_bytes())), - // encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), - // encode_string_c4gh(Some(b"chacha20_poly1305")), - // encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), - // match comment { - // Some(c) => encode_string_c4gh(Some(c.as_bytes())), - // None => [].to_vec(), - // }, - // ] - // .concat() - // }) + //todo!(); + Ok(if passphrase.is_empty() { + log::warn!("The private key is not encrypted"); + vec![ + C4GH_MAGIC_WORD.to_vec(), + encode_string_c4gh(None), // KDF = None + encode_string_c4gh(None), // Cipher = None + encode_string_c4gh(Some(skpk)), + match comment { + Some(c) => encode_string_c4gh(Some(c.as_bytes())), + None => [].to_vec(), + }, + ] + .concat() + } + else { + let kdfname = "scrypt"; + let (salt_size, rounds) = get_kdf(kdfname)?; + let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).get_seed();;//.gen::<[u8;10]>(); // TODO: This is wrong X"D + + let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); + let key = chacha20poly1305::Key::from_slice(&derived_key); + let salt_ga = GenericArray::from_slice(salt.as_slice()); + + let encrypted_key = ChaCha20Poly1305::new(&key) + .encrypt(salt_ga, skpk) + .map_err(|_| Crypt4GHError::BadKey)?; + + let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); + + log::debug!("Derived Key: {:02x?}", derived_key); + log::debug!("Salt: {:02x?}", salt); + log::debug!("Nonce: {:02x?}", nonce); + + vec![ + C4GH_MAGIC_WORD.to_vec(), + encode_string_c4gh(Some(kdfname.as_bytes())), + encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), + encode_string_c4gh(Some(b"chacha20_poly1305")), + encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), + match comment { + Some(c) => encode_string_c4gh(Some(c.as_bytes())), + None => [].to_vec(), + }, + ] + .concat() + }) } fn get_kdf(kdfname: &str) -> Result<(usize, u32), Crypt4GHError> { From b5dec466afae27d27a020af3ee06b97ed834c356 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Aug 2023 15:06:13 +1000 Subject: [PATCH 25/64] Two more functions implemented w/ RustCrypto API, only two convert_ed25519_* pending to port before a full review/refactor... --- src/keys.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 355657b..155e5e8 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -16,6 +16,8 @@ use lazy_static::lazy_static; use rand::SeedableRng; +use crypto_kx::{Keypair, SecretKey}; + use aes::cipher::{KeyInit, KeyIvInit, ArrayLength}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; @@ -214,11 +216,10 @@ fn parse_c4gh_private_key( let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; let encrypted_data = &private_data[12..]; - todo!(); - // let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) - // .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; + let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) + .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; - // Ok(privkey_plain) + Ok(privkey_plain) } fn parse_ssh_private_key( @@ -510,10 +511,28 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { } fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { + // let mut curve_pk = [0_u8; 32]; + // let ok = + // unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; + // if ok { + // Ok(curve_pk) + // } + // else { + // Err(Crypt4GHError::ConversionFailed) + // } todo!() } fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { + // let mut curve_sk = [0_u8; 32]; + // let ok = + // unsafe { libsodium_sys::crypto_sign_ed25519_sk_to_curve25519(curve_sk.as_mut_ptr(), ed25519_sk.as_ptr()) == 0 }; + // if ok { + // Ok(curve_sk) + // } + // else { + // Err(Crypt4GHError::ConversionFailed) + // } todo!() } @@ -585,7 +604,6 @@ fn encode_string_c4gh(s: Option<&[u8]>) -> Vec { } fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> Result, Crypt4GHError> { - //todo!(); Ok(if passphrase.is_empty() { log::warn!("The private key is not encrypted"); vec![ @@ -646,5 +664,8 @@ fn get_kdf(kdfname: &str) -> Result<(usize, u32), Crypt4GHError> { /// Computes the curve25519 `scalarmult_base` to the first 32 bytes of `sk`. /// `sk` must be at least 32 bytes. pub fn get_public_key_from_private_key(sk: &[u8]) -> Result, Crypt4GHError> { - todo!() + let secret_key = SecretKey::try_from(sk).map_err(|_| Crypt4GHError::BadServerPrivateKey)?; + let keypair = Keypair::from(secret_key); + let public_key = keypair.public(); + Ok(public_key.as_ref().to_vec()) } \ No newline at end of file From ab5b087ec1d7ce1de8fbac162365928f4118af29 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 15 Aug 2023 12:35:53 +1000 Subject: [PATCH 26/64] Catch up with new crypto_kx version (which doesn't fail to compile) and add curve2251-dalek crate for convert_* and decrypt_block functions --- Cargo.lock | 128 +++++++++++++++-------------------------------------- Cargo.toml | 4 +- 2 files changed, 38 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b491974..9b4d011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,12 +113,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - [[package]] name = "byteorder" version = "1.4.3" @@ -240,6 +234,7 @@ dependencies = [ "clap", "crypto_kx", "ctr", + "curve25519-dalek", "itertools", "lazy_static", "log", @@ -266,12 +261,12 @@ dependencies = [ [[package]] name = "crypto_kx" -version = "0.2.0-pre.0" -source = "git+https://github.com/RustCrypto/nacl-compat#a5d7003e3dfffa4cf0f4e5cd320b511eae3f0737" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704722d1d929489c8528bb1882805700f1ba20f54325704973e786352320b1ed" dependencies = [ "blake2", "curve25519-dalek", - "getrandom", "rand_core", ] @@ -286,18 +281,31 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", "fiat-crypto", - "packed_simd_2", "platforms", + "rustc_version", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "digest" version = "0.10.7" @@ -372,10 +380,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -447,15 +453,6 @@ dependencies = [ "either", ] -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -468,12 +465,6 @@ version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -513,16 +504,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm", -] - [[package]] name = "password-hash" version = "0.5.0" @@ -687,6 +668,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.20" @@ -722,6 +712,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "serde" version = "1.0.137" @@ -856,60 +852,6 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.18", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 4e649fc..0b9add1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ rpassword = "7" base64 = "0.21" lazy_static = "1.4" chacha20poly1305 = "0.10" -crypto_kx = { version = "0.2.0-pre.0", git = "https://github.com/RustCrypto/nacl-compat" } +crypto_kx = { version = "0.2.1" } scrypt = { version = "0.11" } bcrypt-pbkdf = { version = "0.10" } aes = { version = "0.8" } @@ -42,6 +42,8 @@ itertools = "0.11" rand = "0.8" rand_chacha = "0.3.1" +curve25519-dalek = "4.0.0" + [profile.release] lto = true overflow-checks = true From 182b9dbbc40a3b5172bcc1f58dd22a597db09745 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 15 Aug 2023 12:37:01 +1000 Subject: [PATCH 27/64] Convert functions are very similar, they'd need retyping of arguments to avoid accidental calls with wrong args. decrypt_block needs a bit more attention with Vec to GA conversions... --- src/error.rs | 2 +- src/keys.rs | 111 +++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 26 +++++++----- 3 files changed, 93 insertions(+), 46 deletions(-) diff --git a/src/error.rs b/src/error.rs index 130350c..7c9b467 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use std::error::Error; -use std::path::{ Path, PathBuf }; +use std::path::PathBuf; use thiserror::Error; diff --git a/src/keys.rs b/src/keys.rs index 155e5e8..b9857bf 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,6 +1,7 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] +use aes::cipher::typenum::Length; use aes::cipher::{StreamCipher, generic_array::GenericArray}; use std::collections::HashMap; @@ -24,7 +25,12 @@ use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; use ctr; -use cbc; + +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::montgomery::MontgomeryPoint; +use curve25519_dalek::traits::Identity; +use curve25519_dalek::traits::IsIdentity; +//use ed25519_dalek::PublicKey as DalekPublicKey; use crate::error::Crypt4GHError; @@ -343,29 +349,26 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let iv_ga = GenericArray::from_slice(iv); let mut cipher = Aes128Ctr::new(key.into(), iv_ga); - //let output = Arc::new(writer.buffer()); - - //let buf = writer.get_mut(); - cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()) }, - "aes192-ctr" => { - //type Aes192Ctr = ctr::CtrFlavor<192>; - todo!() - }, - "aes256-ctr" => { - //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! - todo!() - }, - "aes128-cbc" => { - todo!() - }, - "aes192-cbc" => { - todo!() // Ditto above - }, - "aes256-cbc" => { - todo!() // Ditto above - }, + // "aes192-ctr" => { + // let ctr_array = GenericArray::::default(); + // type Aes192Ctr = dyn ctr::flavors::CtrFlavor; + // todo!() + // }, + // "aes256-ctr" => { + // //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! + // todo!() + // }, + // "aes128-cbc" => { + // todo!() + // }, + // "aes192-cbc" => { + // todo!() // Ditto above + // }, + // "aes256-cbc" => { + // todo!() // Ditto above + // }, "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), }; @@ -511,19 +514,59 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { } fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { - // let mut curve_pk = [0_u8; 32]; - // let ok = - // unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; - // if ok { - // Ok(curve_pk) - // } - // else { - // Err(Crypt4GHError::ConversionFailed) - // } - todo!() + if ed25519_pk.len() != 32 { + return Err(Crypt4GHError::ConversionFailed); + } + + let mut curve_pk = [0_u8; 32]; + + // let mut montgomery_point = MontgomeryPoint(CompressedEdwardsY::identity()); + let mut montgomery_point = MontgomeryPoint(curve_pk); // TODO: Fix this nonsense :point_up: + montgomery_point.0.copy_from_slice(ed25519_pk); + + // Ensure the given point is not the identity point + if montgomery_point.is_identity() { + return Err(Crypt4GHError::ConversionFailed); + } + + curve_pk.copy_from_slice(&montgomery_point.to_bytes()); + + Ok(curve_pk) + + // old impl, libsodium function involved: + // https://doc.libsodium.org/advanced/ed25519-curve25519 + + // let mut curve_pk = [0_u8; 32]; + // let ok = + // unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; + // if ok { + // Ok(curve_pk) + // } + // else { + // Err(Crypt4GHError::ConversionFailed) + // } } +// TODO: Exact copy from the convert_ function above, perhaps the input types need to be a bit more specific than just typeless slices. fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { + if ed25519_sk.len() != 32 { + return Err(Crypt4GHError::ConversionFailed); + } + + let mut curve_sk = [0_u8; 32]; + + // let mut montgomery_point = MontgomeryPoint(CompressedEdwardsY::identity()); + let mut montgomery_point = MontgomeryPoint(curve_sk); // TODO: Fix this nonsense :point_up: + montgomery_point.0.copy_from_slice(ed25519_sk); + + // Ensure the given point is not the identity point + if montgomery_point.is_identity() { + return Err(Crypt4GHError::ConversionFailed); + } + + curve_sk.copy_from_slice(&montgomery_point.to_bytes()); + + Ok(curve_sk) // let mut curve_sk = [0_u8; 32]; // let ok = // unsafe { libsodium_sys::crypto_sign_ed25519_sk_to_curve25519(curve_sk.as_mut_ptr(), ed25519_sk.as_ptr()) == 0 }; @@ -533,7 +576,7 @@ fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt // else { // Err(Crypt4GHError::ConversionFailed) // } - todo!() + //todo!() } /// Generates a random privary key. @@ -621,7 +664,7 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> else { let kdfname = "scrypt"; let (salt_size, rounds) = get_kdf(kdfname)?; - let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).get_seed();;//.gen::<[u8;10]>(); // TODO: This is wrong X"D + let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).get_seed();//.gen::<[u8;10]>(); // TODO: This is wrong X"D let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; let nonce = ChaCha20Poly1305::generate_nonce(OsRng); diff --git a/src/lib.rs b/src/lib.rs index 44a12a5..63a17d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ use header::DecryptedHeaderPackets; use chacha20poly1305::aead::Aead; use chacha20poly1305::{ self, ChaCha20Poly1305, Key, KeyInit, Nonce }; +use aes::cipher::generic_array::GenericArray; use crate::error::Crypt4GHError; @@ -551,19 +552,22 @@ pub fn body_decrypt( Ok(()) } +/// Reads and returns the first successfully decrypted block, trying all the session keys against one ciphersegment. fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { let (nonce_slice, data) = ciphersegment.split_at(12); - let nonce = Nonce::from_slice(nonce_slice);//.ok_or(Crypt4GHError::UnableToWrapNonce)?; - - //let key_slice = Key::from_slice(); - - //let cipher = ChaCha20Poly1305::new(session_keys); - //chacha20poly1305::open(data, None, &nonce, &key).ok()) - // session_keys - // .iter() - // .map(|key| ) - //.ok_or(Crypt4GHError::UnableToDecryptBlock) - todo!() + let nonce_bytes: [u8; 24] = nonce_slice + .try_into() + .map_err(|_| Crypt4GHError::UnableToWrapNonce)?; + + for key in session_keys { + if let Ok(key) = chacha20poly1305::ChaCha20Poly1305::new(key) { + if let Ok(decrypted) = key.decrypt(&nonce_bytes, data) { + return Ok(decrypted); + } + } + } + + Err(Crypt4GHError::UnableToDecryptBlock) } /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. From ed28ab6a91a8767ef606f7d460be7a2a69e69191 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 16 Aug 2023 14:49:37 +1000 Subject: [PATCH 28/64] Fixed decrypt_block functions, starting functional review of the rest of TODO's. Co-authored-by: Marko Malenic --- src/lib.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 63a17d2..12447c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ use header::DecryptedHeaderPackets; use chacha20poly1305::aead::Aead; use chacha20poly1305::{ self, ChaCha20Poly1305, Key, KeyInit, Nonce }; use aes::cipher::generic_array::GenericArray; +use chacha20poly1305::consts::U32; use crate::error::Crypt4GHError; @@ -228,10 +229,9 @@ pub fn encrypt_header( ) -> Result, Crypt4GHError> { let encryption_method = 0; let session_key_or_new = session_key.unwrap_or_else(|| { - let mut session_key = [0_u8; 32]; let rnd = rand_chacha::ChaCha20Rng::from_entropy(); - session_key = rnd.get_seed(); // TODO: Double check this too + let session_key = rnd.get_seed(); // TODO: Double check this too session_key }); let header_content = header::make_packet_data_enc(encryption_method, &session_key_or_new); @@ -489,7 +489,7 @@ pub fn body_decrypt_parts( } } else { - decrypted.read(edit_length as usize); + decrypted.read(edit_length as usize)?; } skip = !skip; } @@ -555,21 +555,35 @@ pub fn body_decrypt( /// Reads and returns the first successfully decrypted block, trying all the session keys against one ciphersegment. fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { let (nonce_slice, data) = ciphersegment.split_at(12); - let nonce_bytes: [u8; 24] = nonce_slice + let nonce_bytes: [u8; 12] = nonce_slice .try_into() .map_err(|_| Crypt4GHError::UnableToWrapNonce)?; + for key in session_keys { - if let Ok(key) = chacha20poly1305::ChaCha20Poly1305::new(key) { - if let Ok(decrypted) = key.decrypt(&nonce_bytes, data) { - return Ok(decrypted); - } + let key_t = Key::from_slice(&key); + let key_1 = chacha20poly1305::ChaCha20Poly1305::new(&key_t); + + // GenericArray:: + let nonce = Nonce::from(nonce_bytes); + if let Ok(decrypted) = key_1.decrypt(&nonce, data) { + return Ok(decrypted); } } Err(Crypt4GHError::UnableToDecryptBlock) } +// fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { +// let (nonce_slice, data) = ciphersegment.split_at(12); +// let nonce = Nonce::from_slice(nonce_slice).ok_or(Crypt4GHError::UnableToWrapNonce)?; + +// session_keys +// .iter() +// .find_map(|key| Key::from_slice(key).and_then(|key| chacha20poly1305_ietf::open(data, None, &nonce, &key).ok())) +// .ok_or(Crypt4GHError::UnableToDecryptBlock) +// } + /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. /// /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. From c2fcd0e766ad0c9a569ac996f5836f89a6fe9cad Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 16 Aug 2023 15:16:29 +1000 Subject: [PATCH 29/64] Compiles! Need to fix tests... --- src/keys.rs | 43 ++++++++++++++++++++++++++++++++++++------- src/lib.rs | 28 ++++++++++++++++------------ 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index b9857bf..00caaf6 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,7 +1,6 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] -use aes::cipher::typenum::Length; use aes::cipher::{StreamCipher, generic_array::GenericArray}; use std::collections::HashMap; @@ -15,22 +14,20 @@ use base64::Engine; use itertools::Itertools; use lazy_static::lazy_static; -use rand::SeedableRng; +use rand_chacha; +use rand::{SeedableRng, RngCore}; use crypto_kx::{Keypair, SecretKey}; -use aes::cipher::{KeyInit, KeyIvInit, ArrayLength}; +use aes::cipher::{KeyInit, KeyIvInit}; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; use ctr; -use curve25519_dalek::edwards::CompressedEdwardsY; use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::traits::Identity; use curve25519_dalek::traits::IsIdentity; -//use ed25519_dalek::PublicKey as DalekPublicKey; use crate::error::Crypt4GHError; @@ -662,9 +659,41 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> .concat() } else { + +// let kdfname = "scrypt"; +// let (salt_size, rounds) = get_kdf(kdfname)?; +// let salt = randombytes(salt_size); +// let derived_key = derive_key(kdfname, passphrase, Some(salt.clone()), Some(rounds), 32)?; +// let nonce_bytes = randombytes(12); +// let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); +// let key = chacha20poly1305_ietf::Key::from_slice(&derived_key).unwrap(); +// let encrypted_key = chacha20poly1305_ietf::seal(skpk, None, &nonce, &key); + +// log::debug!("Derived Key: {:02x?}", derived_key.iter().format("")); +// log::debug!("Salt: {:02x?}", salt.iter().format("")); +// log::debug!("Nonce: {:02x?}", nonce.0.to_vec().iter().format("")); + +// vec![ +// C4GH_MAGIC_WORD.to_vec(), +// encode_string_c4gh(Some(kdfname.as_bytes())), +// encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt].concat())), +// encode_string_c4gh(Some(b"chacha20_poly1305")), +// encode_string_c4gh(Some(&vec![nonce.0.to_vec(), encrypted_key].concat())), +// match comment { +// Some(c) => encode_string_c4gh(Some(c.as_bytes())), +// None => [].to_vec(), +// }, +// ] +// .concat() +// }) +// } let kdfname = "scrypt"; let (salt_size, rounds) = get_kdf(kdfname)?; - let salt = rand_chacha::ChaCha20Rng::seed_from_u64(u64::from(rounds)).get_seed();//.gen::<[u8;10]>(); // TODO: This is wrong X"D + + let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); + let mut salt = vec![0; salt_size]; + + rnd.try_fill_bytes(&mut salt).map_err(|_| Crypt4GHError::NoRandomNonce)?; let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; let nonce = ChaCha20Poly1305::generate_nonce(OsRng); diff --git a/src/lib.rs b/src/lib.rs index 12447c8..96b6761 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ clippy::redundant_else )] -use rand::SeedableRng; +use rand::{SeedableRng, RngCore}; use rand_chacha; use std::collections::HashSet; @@ -31,8 +31,6 @@ use header::DecryptedHeaderPackets; use chacha20poly1305::aead::Aead; use chacha20poly1305::{ self, ChaCha20Poly1305, Key, KeyInit, Nonce }; -use aes::cipher::generic_array::GenericArray; -use chacha20poly1305::consts::U32; use crate::error::Crypt4GHError; @@ -135,11 +133,13 @@ pub fn encrypt( log::debug!(" Span: {:?}", range_span); log::info!("Creating Crypt4GH header"); + let mut session_key = [0_u8; 32]; - let rnd = rand_chacha::ChaCha20Rng::from_entropy(); - let rnd_num = rnd.get_stream(); + let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); + // random bytes into session_key - session_key = rnd.get_seed(); // TODO: Is this correct usage? + rnd.try_fill_bytes(&mut session_key).map_err(|_| Crypt4GHError::NoRandomNonce)?; + let header_bytes = encrypt_header(recipient_keys, &Some(session_key))?; log::debug!("header length: {}", header_bytes.len()); @@ -228,12 +228,16 @@ pub fn encrypt_header( session_key: &Option<[u8; 32]>, ) -> Result, Crypt4GHError> { let encryption_method = 0; - let session_key_or_new = session_key.unwrap_or_else(|| { - let rnd = rand_chacha::ChaCha20Rng::from_entropy(); + + let session_key_or_new = session_key.map_or_else(|| { + let mut session_key = [0_u8; 32]; + let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); + + rnd.try_fill_bytes(&mut session_key).map_err(|_| Crypt4GHError::NoRandomNonce)?; // TODO: Custom error for this + + Ok::<_, Crypt4GHError>(session_key) + }, |value| { Ok(value)} )?; - let session_key = rnd.get_seed(); // TODO: Double check this too - session_key - }); let header_content = header::make_packet_data_enc(encryption_method, &session_key_or_new); let header_packets = header::encrypt(&header_content, recipient_keys)?; let header_bytes = header::serialize(header_packets); @@ -485,7 +489,7 @@ pub fn body_decrypt_parts( for edit_length in edit_list { if skip { if edit_length != 0 { - decrypted.skip(edit_length as usize); + decrypted.skip(edit_length as usize)?; } } else { From c5bcf878f1e3ec8f758ab2ec763f061d558e52a1 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 16 Aug 2023 16:20:26 +1000 Subject: [PATCH 30/64] Integration tests fixed to the extent they run :) Next will be fixing the password_prompt callback logic and probably introduce more functional testing (instead of fully integration testing as it is now) --- src/bin.rs | 10 +++++----- src/keys.rs | 16 ++++++++-------- tests/edit_list_gen.rs | 31 +++++++++++++++++-------------- tests/test_keygen.rs | 10 +++++----- tests/test_keygen_length.rs | 10 +++++----- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index f30e6dd..0312f97 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -84,18 +84,18 @@ fn retrieve_private_key(sk: Option, generate: bool) -> Result, return Err(Crypt4GHError::ReadSecretKeyFileError(path)); } - let callback: Box Result> = match std::env::var(PASSPHRASE) { + let passphrase: Result = match std::env::var(PASSPHRASE) { Ok(_) => { log::warn!("Warning: Using a passphrase in an environment variable is insecure"); - Box::new(|| std::env::var(PASSPHRASE).map_err(|e| Crypt4GHError::NoPassphrase(e.into()))) + std::env::var(PASSPHRASE).map_err(|e| Crypt4GHError::NoPassphrase(e.into())) // TODO: Does this make sure it's set/unset? }, - Err(_) => Box::new(|| { + Err(_) => { prompt_password(format!("Passphrase for {:?}: ", path)) .map_err(|e| Crypt4GHError::NoPassphrase(e.into())) - }), + }, }; - get_private_key(path.to_owned(), callback) + get_private_key(path.to_owned(), passphrase) } } diff --git a/src/keys.rs b/src/keys.rs index 00caaf6..8c78ef6 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -164,7 +164,7 @@ fn derive_key( fn parse_c4gh_private_key( mut stream: impl BufRead, - callback: impl Fn() -> Result, + callback: Result, ) -> Result, Crypt4GHError> { let kdfname = String::from_utf8(decode_string_c4gh(&mut stream)?) .map_err(|e| Crypt4GHError::UnsupportedKdf(e.to_string()))?; @@ -209,7 +209,7 @@ fn parse_c4gh_private_key( return Err(Crypt4GHError::BadCiphername(ciphername)); } - let passphrase = callback()?; + let passphrase = callback?; let shared_key = derive_key(&kdfname, &passphrase, salt, rounds, 32)?; log::debug!("Shared Key: {:02x?}", shared_key.iter().format("")); @@ -227,7 +227,7 @@ fn parse_c4gh_private_key( fn parse_ssh_private_key( mut stream: impl BufRead, - callback: impl Fn() -> Result, + callback: Result, ) -> Result<([u8; 32], [u8; 32]), Crypt4GHError> { let ciphername = String::from_utf8(decode_string_ssh(&mut stream)?).map_err(|e| Crypt4GHError::BadCiphername(e.to_string()))?; @@ -304,7 +304,7 @@ fn parse_ssh_private_key( // Encrypted assert!(salt.is_some() && rounds.is_some()); - let passphrase = callback().map_err(|e| Crypt4GHError::NoPassphrase(e.into()))?; + let passphrase = callback.map_err(|e| Crypt4GHError::NoPassphrase(e.into()))?; // TODO: Just check for passphrase being null since callback is gone? let dklen = get_derived_key_length(&ciphername)?; log::debug!("Derived Key len: {}", dklen); @@ -425,10 +425,10 @@ fn get_skpk_from_decrypted_private_blob(blob: &[u8]) -> Result<([u8; 32], [u8; 3 /// /// It supports `Crypt4GH` and OpenSSH private keys. Fails if it can not read the file /// or if the key is not one of the two supported formats. Returns the decode key. -/// If the key is encrypted, the `callback` should return the passphrase of the key. +/// If the key is encrypted, passphrase should return the passphrase of the key. pub fn get_private_key( key_path: PathBuf, - callback: impl Fn() -> Result, + passphrase: Result, ) -> Result, Crypt4GHError> { let data = load_from_pem(&key_path)?; @@ -438,7 +438,7 @@ pub fn get_private_key( stream .read_exact(&mut [0_u8; C4GH_MAGIC_WORD.len()]) .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - parse_c4gh_private_key(stream, callback) + parse_c4gh_private_key(stream, passphrase) } else if data.starts_with(SSH_MAGIC_WORD) { log::info!("Loading an OpenSSH private key"); @@ -446,7 +446,7 @@ pub fn get_private_key( stream .read_exact(&mut [0_u8; SSH_MAGIC_WORD.len()]) .map_err(|e| Crypt4GHError::ReadMagicWord(e.into()))?; - let (seckey, pubkey) = parse_ssh_private_key(stream, callback)?; + let (seckey, pubkey) = parse_ssh_private_key(stream, passphrase)?; Ok(vec![seckey, pubkey].concat()) } else { diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 3cf711a..c9b8e08 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -2,12 +2,12 @@ use std::fs::File; use std::io::Write; -use std::path::Path; +use std::path::{Path, PathBuf}; +use crypt4gh::error::Crypt4GHError; use rand::Rng; -//use sodiumoxide::crypto::aead::chacha20poly1305_ietf; -pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, passphrase: &str) { +pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, passphrase: &str) -> Result<(), Crypt4GHError> { let mut rng = rand::thread_rng(); let parts = input.lines().collect::>(); @@ -32,9 +32,9 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p eprintln!("Recipient PK: {:?}", recipient_pk); assert!(Path::new(recipient_pk).exists(), "Edit list gen key not found"); - let callback = || Ok(passphrase.to_string()); - let seckey = crypt4gh::keys::get_private_key(Path::new(sk), callback).unwrap(); - let recipient_pubkey = crypt4gh::keys::get_public_key(Path::new(recipient_pk)).unwrap(); + let callback = Ok(passphrase.to_string()); + let seckey = crypt4gh::keys::get_private_key(PathBuf::from(sk), callback)?; + let recipient_pubkey = crypt4gh::keys::get_public_key(PathBuf::from(recipient_pk))?; eprintln!("Sec: {:?}", seckey); eprintln!("Pub: {:?}", recipient_pubkey); @@ -68,13 +68,16 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p log::debug!("header length: {}", header_bytes.len()); + outfile; // TODO: Implement the code below w/ Rustcrypto here before returning output // Output the message - sodiumoxide::init().expect("Unable to initialize libsodium"); - for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { - let nonce_bytes = sodiumoxide::randombytes::randombytes(12); - let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); - let key = chacha20poly1305_ietf::Key::from_slice(&session_key).unwrap(); - let encrypted_segment = crypt4gh::encrypt_segment(segment, nonce, &key); - outfile.write_all(&encrypted_segment).unwrap(); - } + // sodiumoxide::init().expect("Unable to initialize libsodium"); + // for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { + // let nonce_bytes = sodiumoxide::randombytes::randombytes(12); + // let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); + // let key = chacha20poly1305_ietf::Key::from_slice(&session_key).unwrap(); + // let encrypted_segment = crypt4gh::encrypt_segment(segment, nonce, &key); + // outfile.write_all(&encrypted_segment).unwrap(); + // } + + Ok(()) } diff --git a/tests/test_keygen.rs b/tests/test_keygen.rs index 6ac310e..8673831 100644 --- a/tests/test_keygen.rs +++ b/tests/test_keygen.rs @@ -1,6 +1,6 @@ mod test_common; -use std::path::Path; +use std::path::PathBuf; pub use test_common::*; @@ -11,16 +11,16 @@ fn test_keygen() { let bob_pk_path = temp_file("bob.pub"); let bob_sk_path = temp_file("bob.sec"); - let callback = || Ok(BOB_PASSPHRASE.to_string()); + let callback = Ok(BOB_PASSPHRASE.to_string()); - crypt4gh::keys::generate_keys(Path::new(&bob_sk_path), Path::new(&bob_pk_path), callback, None) + crypt4gh::keys::generate_keys(PathBuf::from(&bob_sk_path), PathBuf::from(&bob_pk_path), callback, None) .expect("Unable to generate Bob's keys"); let alice_pk_path = temp_file("alice.pub"); let alice_sk_path = temp_file("alice.sec"); - let callback2 = || Ok(ALICE_PASSPHRASE.to_string()); + let callback2 = Ok(ALICE_PASSPHRASE.to_string()); - crypt4gh::keys::generate_keys(Path::new(&alice_sk_path), Path::new(&alice_pk_path), callback2, None) + crypt4gh::keys::generate_keys(PathBuf::from(&alice_sk_path), PathBuf::from(&alice_pk_path), callback2, None) .expect("Unable to generate Alice's keys"); // Encrypt diff --git a/tests/test_keygen_length.rs b/tests/test_keygen_length.rs index d1cac52..f586a98 100644 --- a/tests/test_keygen_length.rs +++ b/tests/test_keygen_length.rs @@ -1,6 +1,6 @@ mod test_common; -use std::path::Path; +use std::path::PathBuf; pub use test_common::*; @@ -11,9 +11,9 @@ fn test_keygen_length_encrypted() { let bob_pk_path = temp_file("bob.pub"); let bob_sk_path = temp_file("bob.sec"); - let callback = || Ok(BOB_PASSPHRASE.to_string()); + let callback = Ok(BOB_PASSPHRASE.to_string()); - crypt4gh::keys::generate_keys(Path::new(&bob_sk_path), Path::new(&bob_pk_path), callback, None) + crypt4gh::keys::generate_keys(PathBuf::from(&bob_sk_path), PathBuf::from(&bob_pk_path), callback, None) .expect("Unable to generate Bob's keys"); count_characters(&temp_file("bob.pub"), 36 + 45 + 34); @@ -30,9 +30,9 @@ fn test_keygen_length_not_encrypted() { let alice_pk_path = temp_file("alice.pub"); let alice_sk_path = temp_file("alice.sec"); - let callback = || Ok("".to_string()); + let callback = Ok("".to_string()); - crypt4gh::keys::generate_keys(Path::new(&alice_sk_path), Path::new(&alice_pk_path), callback, None) + crypt4gh::keys::generate_keys(PathBuf::from(&alice_sk_path), PathBuf::from(&alice_pk_path), callback, None) .expect("Unable to generate Bob's keys"); count_characters(&temp_file("alice.pub"), 36 + 45 + 34); From e8711f6e2955a11a79d1927ee15d79c6b15a0983 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 18 Aug 2023 10:51:39 +1000 Subject: [PATCH 31/64] Substitute assert! for the right if equivalent... unwrap result on tests --- src/keys.rs | 4 ++-- tests/test_edit_list.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 8c78ef6..bbcd0bc 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -81,8 +81,8 @@ fn load_from_pem(filepath: &PathBuf) -> Result, Crypt4GHError> { return Err(Crypt4GHError::InvalidPEMFormatLength(filepath.into())); } - if lines.first().unwrap().starts_with("-----BEGIN ") || - lines.last().unwrap().starts_with("-----END ") { + if !lines.first().unwrap().starts_with("-----BEGIN ") || + !lines.last().unwrap().starts_with("-----END ") { return Err(Crypt4GHError::InvalidPEMHeaderOrFooter); } diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index c4b8e51..8d3a29a 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -32,7 +32,7 @@ fn test_send_message_buried() { INPUT_EDIT_LIST, &mut file, BOB_PASSPHRASE, - ); + ).unwrap(); // Decrypt CommandUnderTest::new() From 1985c939500a6b5e3438cd5f740a8b7dba8f8dac Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 18 Aug 2023 16:31:46 +1000 Subject: [PATCH 32/64] Optional sender_pubkeys must be handled more gracefully than .unwrap()... need more log::debug() on the original impl to compare: why the original sk is 64 bytes long while ours is 32? test/testfiles are not the culprit since all keys in there have the same md5sums between versions, tbc --- src/header.rs | 1 + src/keys.rs | 1 + tests/edit_list_gen.rs | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/header.rs b/src/header.rs index afd9448..ba952ef 100644 --- a/src/header.rs +++ b/src/header.rs @@ -202,6 +202,7 @@ fn decrypt_x25519_chacha20_poly1305( ) -> Result, Crypt4GHError> { let peer_pubkey = &encrypted_part[4..PublicKey::BYTES+4]; log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); + // TODO: Remove .unwrap() below, otherwise simple tests with no sender pubkeys will panic log::debug!(" RustCrypto decrypt() sender_pubkey({}): {:02x?}", sender_pubkey.clone().unwrap().as_slice().len(), sender_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { diff --git a/src/keys.rs b/src/keys.rs index bbcd0bc..9761ad2 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -585,6 +585,7 @@ pub fn generate_private_key() -> Result, Crypt4GHError> { let seckey = ChaCha20Poly1305::generate_key(OsRng).to_vec(); let pubkey = get_public_key_from_private_key(&seckey)?; assert_eq!(seckey.len(), pubkey.len()); + log::debug!("Secret key in generate_private_key(): {:#?}", &seckey); Ok(vec![seckey, pubkey].concat()) } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index c9b8e08..4002006 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -36,8 +36,8 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p let seckey = crypt4gh::keys::get_private_key(PathBuf::from(sk), callback)?; let recipient_pubkey = crypt4gh::keys::get_public_key(PathBuf::from(recipient_pk))?; - eprintln!("Sec: {:?}", seckey); - eprintln!("Pub: {:?}", recipient_pubkey); + eprintln!("Sec: {:?}\n with length: {:?}", seckey, seckey.len()); + eprintln!("Pub: {:?}\n with length: {:?}", recipient_pubkey, recipient_pubkey.len()); let keys = vec![crypt4gh::Keys { method: 0, From 185a9bec935345713d8d757cc3f34f27b2fc4d35 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 28 Aug 2023 15:35:15 +1000 Subject: [PATCH 33/64] More appropriate error control that doesn't break tests, debugging RUST_LOG issues (original impl does not print them, whereas our refactor does) --- src/error.rs | 4 ++-- src/header.rs | 4 ++-- src/keys.rs | 4 ++-- tests/edit_list_gen.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7c9b467..c8ff4f4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,8 +69,8 @@ pub enum Crypt4GHError { BadSharedKey, #[error("Invalid Peer's Public Key")] InvalidPeerPubPkey, - // #[error("Invalid paramenters passed to Scrypt")] - // ScryptParamsError(InvalidParams), + #[error("Invalid paramenters passed to Scrypt")] + ScryptParamsError, // Reading errors #[error("Unable to read {0} bytes from input (ERROR = {1:?})")] diff --git a/src/header.rs b/src/header.rs index ba952ef..ac894ac 100644 --- a/src/header.rs +++ b/src/header.rs @@ -202,13 +202,13 @@ fn decrypt_x25519_chacha20_poly1305( ) -> Result, Crypt4GHError> { let peer_pubkey = &encrypted_part[4..PublicKey::BYTES+4]; log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); - // TODO: Remove .unwrap() below, otherwise simple tests with no sender pubkeys will panic - log::debug!(" RustCrypto decrypt() sender_pubkey({}): {:02x?}", sender_pubkey.clone().unwrap().as_slice().len(), sender_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); } + log::debug!(" RustCrypto decrypt() sender_pubkey (peer_pubkey)({:#?}): {:02x?}", peer_pubkey, sender_pubkey.iter()); + let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let packet_data = &encrypted_part[44+4..]; diff --git a/src/keys.rs b/src/keys.rs index 9761ad2..134965c 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -130,14 +130,14 @@ fn derive_key( // TODO: Review last param of ScryptParams (length of what, exactly?) carefully. // TODO: Why is the output not used? // Added "dklen" for now since it seemed fitting, but needs proper review. - let params = scrypt::Params::new(14, 8, 1, dklen); + let params = scrypt::Params::new(14, 8, 1, dklen).map_err(|_| Crypt4GHError::ScryptParamsError)?; let _ = scrypt::scrypt( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); vec![0_u8; 0] }), - ¶ms.unwrap(), //TODO: .map_err() this to GA4GHError + ¶ms, &mut output, ); }, diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 4002006..1ca3d4a 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -23,7 +23,7 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p edits.push(part.len()); } - eprintln!("Edits: {:?}", edits); + log::debug!("Edits: {:?}", edits); // Fetch the keys From 050da2509862eee23752d5fe6a67ba5571f4a8f1 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 28 Aug 2023 17:20:06 +1000 Subject: [PATCH 34/64] Panic on parse_c4gh_private_key(), now it's pretty clear that the difference in secret key length is based on some difference between libsodium's IETF chacha20poly1305_ietf::seal(encrypted_data, None, &nonce, &key) and RustCrypto's ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) --- src/keys.rs | 5 +++++ tests/edit_list_gen.rs | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 134965c..0adc4c3 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -200,6 +200,8 @@ fn parse_c4gh_private_key( let private_data = decode_string_c4gh(&mut stream)?; + log::debug!("Private data: {:?}", &private_data); + if ciphername == "none" { return Ok(private_data); } @@ -219,9 +221,12 @@ fn parse_c4gh_private_key( let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; let encrypted_data = &private_data[12..]; + log::debug!("Encrypted data: {:?}", &encrypted_data); + let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; + eprintln!("Privkey plaintext: {:?}", privkey_plain); Ok(privkey_plain) } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 1ca3d4a..499a6ce 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -27,17 +27,19 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p // Fetch the keys - eprintln!("SK: {:?}", sk); - assert!(Path::new(sk).exists()); - eprintln!("Recipient PK: {:?}", recipient_pk); - assert!(Path::new(recipient_pk).exists(), "Edit list gen key not found"); + log::debug!("SK: {:?}", sk); + assert!(Path::new(sk).exists()); // TODO: Migrate to Crypt4GHError + log::debug!("Recipient PK: {:?}", recipient_pk); + assert!(Path::new(recipient_pk).exists(), "Edit list gen key not found"); // TODO: Migrate to Crypt4GHError let callback = Ok(passphrase.to_string()); let seckey = crypt4gh::keys::get_private_key(PathBuf::from(sk), callback)?; let recipient_pubkey = crypt4gh::keys::get_public_key(PathBuf::from(recipient_pk))?; eprintln!("Sec: {:?}\n with length: {:?}", seckey, seckey.len()); - eprintln!("Pub: {:?}\n with length: {:?}", recipient_pubkey, recipient_pubkey.len()); + log::debug!("Pub: {:?}\n with length: {:?}", recipient_pubkey, recipient_pubkey.len()); + + panic!(); let keys = vec![crypt4gh::Keys { method: 0, From 9bdbe195f754f6f420f3e03d8173b1a7e4c11907 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 28 Aug 2023 17:57:32 +1000 Subject: [PATCH 35/64] Show the arguments to RustCrypto's Chacha20Poly1305 functions vs libsodium's seal() ones for debugging purposes --- src/keys.rs | 4 ++++ tests/edit_list_gen.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/keys.rs b/src/keys.rs index 0adc4c3..227bfa3 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -226,6 +226,10 @@ fn parse_c4gh_private_key( let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; + eprintln!("RustCrypto's ChaCha20Poly1305::new() key argument: {:?}", &key); + eprintln!("RustCrypto's ChaCha20Poly1305::new() nonce argument: {:?}", &nonce); + eprintln!("RustCrypto's ChaCha20Poly1305::new() encrypted_data argument: {:?}", &encrypted_data); + eprintln!("Privkey plaintext: {:?}", privkey_plain); Ok(privkey_plain) } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 499a6ce..e0c2535 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -36,7 +36,7 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p let seckey = crypt4gh::keys::get_private_key(PathBuf::from(sk), callback)?; let recipient_pubkey = crypt4gh::keys::get_public_key(PathBuf::from(recipient_pk))?; - eprintln!("Sec: {:?}\n with length: {:?}", seckey, seckey.len()); + log::debug!("Sec: {:?}\n with length: {:?}", seckey, seckey.len()); log::debug!("Pub: {:?}\n with length: {:?}", recipient_pubkey, recipient_pubkey.len()); panic!(); From 15e32fc0906d81d279bf8f7906e9de65a6bb0f7f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 29 Aug 2023 09:47:16 +1000 Subject: [PATCH 36/64] It was .encrypt instead of .decrypt, thanks @mmalenic for spotting this --- src/keys.rs | 11 ++++++----- tests/edit_list_gen.rs | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 227bfa3..d400122 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -223,14 +223,15 @@ fn parse_c4gh_private_key( log::debug!("Encrypted data: {:?}", &encrypted_data); - let privkey_plain = ChaCha20Poly1305::new(key).decrypt(nonce, encrypted_data) + // TODO: Clarify why we are **encrypting** the private key in this function? + let privkey_plain = ChaCha20Poly1305::new(key).encrypt(nonce, encrypted_data) .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; - eprintln!("RustCrypto's ChaCha20Poly1305::new() key argument: {:?}", &key); - eprintln!("RustCrypto's ChaCha20Poly1305::new() nonce argument: {:?}", &nonce); - eprintln!("RustCrypto's ChaCha20Poly1305::new() encrypted_data argument: {:?}", &encrypted_data); + log::debug!("RustCrypto's ChaCha20Poly1305::new() key argument: {:?}", &key); + log::debug!("RustCrypto's ChaCha20Poly1305::new() nonce argument: {:?}", &nonce); + log::debug!("RustCrypto's ChaCha20Poly1305::new() encrypted_data argument: {:?}", &encrypted_data); - eprintln!("Privkey plaintext: {:?}", privkey_plain); + log::debug!("Privkey plaintext: {:?}", privkey_plain); Ok(privkey_plain) } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index e0c2535..7eda5c2 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -39,8 +39,6 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p log::debug!("Sec: {:?}\n with length: {:?}", seckey, seckey.len()); log::debug!("Pub: {:?}\n with length: {:?}", recipient_pubkey, recipient_pubkey.len()); - panic!(); - let keys = vec![crypt4gh::Keys { method: 0, privkey: seckey, From 81d401e71db2a37ae15859bb44c653f1cf866130 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 30 Aug 2023 15:57:26 +1000 Subject: [PATCH 37/64] peer_pubkey must have the right amount of leading 0s if it must respect the original implementation... or this requires a bit more analysis on why those zeroed headers exist in the first place? The crypt4gh spec suggests it might be packet length, but in this case, why is it all 0's on the original implementation? Those are packets not a continuous stream, so they should have length encoded there? --- src/header.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/header.rs b/src/header.rs index ac894ac..445cd64 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,3 +1,4 @@ +use core::panic; use std::collections::HashSet; use aead::consts::U32; @@ -186,12 +187,18 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) } match packet_encryption_method { - 0 => return decrypt_x25519_chacha20_poly1305(&packet[4..], &key.privkey, sender_pubkey), + 0 => { + let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[4..], &key.privkey, sender_pubkey); + log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[4..], &plaintext_packet); + panic!(); + return plaintext_packet; + }, 1 => unimplemented!("AES-256-GCM support is not implemented"), n => return Err(Crypt4GHError::BadHeaderEncryptionMethod(n)), } } + panic!(); Err(Crypt4GHError::UnableToEncryptPacket) } @@ -200,15 +207,13 @@ fn decrypt_x25519_chacha20_poly1305( privkey: &[u8], sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { - let peer_pubkey = &encrypted_part[4..PublicKey::BYTES+4]; + let peer_pubkey = &encrypted_part[8..PublicKey::BYTES+4]; log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); } - log::debug!(" RustCrypto decrypt() sender_pubkey (peer_pubkey)({:#?}): {:02x?}", peer_pubkey, sender_pubkey.iter()); - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let packet_data = &encrypted_part[44+4..]; @@ -229,6 +234,7 @@ fn decrypt_x25519_chacha20_poly1305( packet_data.iter() ); + //panic!(); let plaintext = cipher.decrypt(&nonce, packet_data) .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; From 56b79a52b8ea2d19ac53ee03c7be116d8896be48 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 4 Sep 2023 16:38:29 +1000 Subject: [PATCH 38/64] Get the nonce from part of the packet, need to adjust types from StreamCipherCoreWrapper to GenericArray now though --- src/header.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/header.rs b/src/header.rs index 445cd64..9a0b8b0 100644 --- a/src/header.rs +++ b/src/header.rs @@ -207,14 +207,15 @@ fn decrypt_x25519_chacha20_poly1305( privkey: &[u8], sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { - let peer_pubkey = &encrypted_part[8..PublicKey::BYTES+4]; + let peer_pubkey = &encrypted_part[8..32+8];//PublicKey::BYTES]; log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); + let nonce = ChaCha20Poly1305::new_from_slice(&encrypted_part[32..44]) + .map_err(|_| Crypt4GHError::NoNonce)?; let packet_data = &encrypted_part[44+4..]; let client_sk = SecretKey::try_from(&privkey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; @@ -227,7 +228,7 @@ fn decrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); log::debug!(" RustCrypto peer pubkey: {:02x?}", peer_pubkey.iter()); - log::debug!(" RustCrypto nonce: {:02x?}", nonce.iter()); + //log::debug!(" RustCrypto nonce: {:02x?}", &nonce); log::debug!( " RustCrypto encrypted data ({}): {:02x?}", packet_data.len(), From 88b9e52a7df974a619722f08c339e8590db2edf3 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 8 Sep 2023 16:27:43 +1000 Subject: [PATCH 39/64] Generating the nonce from GenericArray instead of new_from_slice() Co-authored-by: Marko Malenic --- src/header.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/header.rs b/src/header.rs index 9a0b8b0..6c87b28 100644 --- a/src/header.rs +++ b/src/header.rs @@ -207,16 +207,18 @@ fn decrypt_x25519_chacha20_poly1305( privkey: &[u8], sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { - let peer_pubkey = &encrypted_part[8..32+8];//PublicKey::BYTES]; + let peer_pubkey = &encrypted_part[0..32];//PublicKey::BYTES]; log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); } - let nonce = ChaCha20Poly1305::new_from_slice(&encrypted_part[32..44]) - .map_err(|_| Crypt4GHError::NoNonce)?; - let packet_data = &encrypted_part[44+4..]; + let nonce = GenericArray::from_slice(&encrypted_part[32..44]); + // + // let nonce = ChaCha20Poly1305::new_from_slice(&encrypted_part[32..44]) + // .map_err(|_| Crypt4GHError::NoNonce)?; + let packet_data = &encrypted_part[44..]; let client_sk = SecretKey::try_from(&privkey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; let server_pk = PublicKey::try_from(peer_pubkey).map_err(|_| Crypt4GHError::BadServerPublicKey)?; From 74d31409ecb78f09a47eecdbd642846d8cbf8365 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 12 Oct 2023 16:11:04 +1100 Subject: [PATCH 40/64] Decrypting header packet ok :D --- src/header.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/header.rs b/src/header.rs index 6c87b28..d7cee95 100644 --- a/src/header.rs +++ b/src/header.rs @@ -188,9 +188,9 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) match packet_encryption_method { 0 => { - let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[4..], &key.privkey, sender_pubkey); - log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[4..], &plaintext_packet); - panic!(); + let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[8..], &key.privkey, sender_pubkey); + log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[8..], &plaintext_packet); + //panic!(); return plaintext_packet; }, 1 => unimplemented!("AES-256-GCM support is not implemented"), From 3c29184f7ec6181714885930ab93cbb9a4ac7e1e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 13 Oct 2023 11:00:33 +1100 Subject: [PATCH 41/64] Seems to be fetching last block and panicking because it's empty? Reviewing boundary conditions of the iterator(s)... --- src/header.rs | 3 --- src/lib.rs | 9 ++++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/header.rs b/src/header.rs index d7cee95..99994f2 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,4 +1,3 @@ -use core::panic; use std::collections::HashSet; use aead::consts::U32; @@ -197,8 +196,6 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) n => return Err(Crypt4GHError::BadHeaderEncryptionMethod(n)), } } - - panic!(); Err(Crypt4GHError::UnableToEncryptPacket) } diff --git a/src/lib.rs b/src/lib.rs index 96b6761..fd48f86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,21 +367,23 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!("Fetching block {}", self.block); self.block += 1; - // Fetches a block self.buf.clear(); + + // Fetches a block self.read_buffer .take(CIPHER_SEGMENT_SIZE as u64) .read_to_end(&mut self.buf) .unwrap(); + log::debug!("Fetched block: {:#?}", &self.buf); + self.is_decrypted = false; - log::debug!(""); } fn decrypt(&mut self) { // Decrypts its buffer if !self.is_decrypted { - log::debug!("Decrypting block"); + log::debug!("Decrypting block: {:#?}", &self.buf); self.buf = decrypt_block(&self.buf, &self.session_keys).unwrap(); self.is_decrypted = true; } @@ -558,6 +560,7 @@ pub fn body_decrypt( /// Reads and returns the first successfully decrypted block, trying all the session keys against one ciphersegment. fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { + log::debug!("The cyphersegment is: {:#?}", ciphersegment); let (nonce_slice, data) = ciphersegment.split_at(12); let nonce_bytes: [u8; 12] = nonce_slice .try_into() From 700046a8a1f65431b88fc5183a6198d9cea455e7 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 13 Oct 2023 12:49:54 +1100 Subject: [PATCH 42/64] Narrowing down to DecryptedBuffer::new on body_decrypt_parts()... --- src/header.rs | 13 ++++++------- src/lib.rs | 28 ++++++++++++++++------------ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/header.rs b/src/header.rs index 99994f2..ffc7d7c 100644 --- a/src/header.rs +++ b/src/header.rs @@ -188,8 +188,7 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) match packet_encryption_method { 0 => { let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[8..], &key.privkey, sender_pubkey); - log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[8..], &plaintext_packet); - //panic!(); + //log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[8..], &plaintext_packet); return plaintext_packet; }, 1 => unimplemented!("AES-256-GCM support is not implemented"), @@ -228,11 +227,11 @@ fn decrypt_x25519_chacha20_poly1305( log::debug!(" RustCrypto peer pubkey: {:02x?}", peer_pubkey.iter()); //log::debug!(" RustCrypto nonce: {:02x?}", &nonce); - log::debug!( - " RustCrypto encrypted data ({}): {:02x?}", - packet_data.len(), - packet_data.iter() - ); + // log::debug!( + // " RustCrypto encrypted data ({}): {:02x?}", + // packet_data.len(), + // packet_data.iter() + // ); //panic!(); let plaintext = cipher.decrypt(&nonce, packet_data) diff --git a/src/lib.rs b/src/lib.rs index fd48f86..73c84d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -359,15 +359,15 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { decryptor.fetch(); decryptor.decrypt(); log::debug!("Index = {}", decryptor.index); - log::debug!(""); decryptor } fn fetch(&mut self) { - log::debug!("Fetching block {}", self.block); - self.block += 1; + //self.block += 1; //TODO: Why???? + + //self.buf.clear();//TODO: Needed???? - self.buf.clear(); + log::debug!("fetch()'s fetching block idx: {}", self.block); // Fetches a block self.read_buffer @@ -375,7 +375,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { .read_to_end(&mut self.buf) .unwrap(); - log::debug!("Fetched block: {:#?}", &self.buf); + log::debug!("fetch()'s fetched block: {:#?}", &self.buf); self.is_decrypted = false; } @@ -387,7 +387,6 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { self.buf = decrypt_block(&self.buf, &self.session_keys).unwrap(); self.is_decrypted = true; } - log::debug!(""); } fn skip(&mut self, size: usize) -> Result<(), Crypt4GHError> { @@ -396,7 +395,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { return Err(Crypt4GHError::SkipZeroBytes) } // assert!(size > 0, "You shouldn't skip 0 bytes"); - // log::debug!("Skipping {} bytes | Buffer size: {}", size, self.buf.len()); + log::debug!("Skipping {} bytes | Buffer size: {}", size, self.buf.len()); let mut remaining_size = size; @@ -407,19 +406,20 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { if remaining_size >= SEGMENT_SIZE { self.fetch(); remaining_size -= SEGMENT_SIZE; + + log::debug!("skip()'s skipping a whole segment, remaining size: {}", remaining_size); } else { if (self.index + remaining_size) > SEGMENT_SIZE { self.fetch(); } self.index = (self.index + remaining_size) % SEGMENT_SIZE; - log::debug!("Index = {}", self.index); + log::debug!("skip()'s Index for remaining_size of the segment = {}", self.index); remaining_size -= remaining_size; } } log::debug!("Finished skipping"); - log::debug!(""); // Apply self.decrypt(); @@ -478,19 +478,23 @@ pub fn body_decrypt_parts( output: WriteInfo, edit_list: Vec, ) -> Result<(), Crypt4GHError> { - log::debug!("Edit List: {:?}", edit_list); + log::debug!("body_decrypt_parts()'s Edit List: {:?}", edit_list); if edit_list.is_empty() { + log::debug!("body_decrypt_parts()'s Edit List is empty"); return Err(Crypt4GHError::EmptyEditList); } + log::debug!("body_decrypt_parts()'s session_keys: {:#?}", session_keys); let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output); + log::debug!("body_decrypt_parts()'s decrypted content length: {:#?}", decrypted.buf.len()); let mut skip = true; for edit_length in edit_list { if skip { if edit_length != 0 { + log::debug!("body_decrypt_parts()'s edit_length from edit list: {}", edit_length); decrypted.skip(edit_length as usize)?; } } @@ -504,6 +508,7 @@ pub fn body_decrypt_parts( // If we finished with a skip, read until the end loop { let n = decrypted.read(SEGMENT_SIZE)?; + log::debug!("body_decrypt_parts()'s reading until the end index: {}", n); if n == 0 { break; } @@ -545,6 +550,7 @@ pub fn body_decrypt( break; } + log::debug!("body_decrypt()'s fetched block: {:#?}", &chunk); let segment = decrypt_block(&chunk, session_keys)?; output .write_all(&segment) @@ -566,12 +572,10 @@ fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result let nonce = Nonce::from(nonce_bytes); if let Ok(decrypted) = key_1.decrypt(&nonce, data) { return Ok(decrypted); From 34ba1f66cfe431a4f3eb0a4128ddfe6f5577486f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 13 Oct 2023 13:26:07 +1100 Subject: [PATCH 43/64] Need to generate the right outfile in edit_list_gen.rs first :facepalm: outfile; // TODO: Implement the code below w/ Rustcrypto here before returning output // Output the message // sodiumoxide::init().expect("Unable to initialize libsodium"); // for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { // let nonce_bytes = sodiumoxide::randombytes::randombytes(12); // let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); // let key = chacha20poly1305_ietf::Key::from_slice(&session_key).unwrap(); // let encrypted_segment = crypt4gh::encrypt_segment(segment, nonce, &key); // outfile.write_all(&encrypted_segment).unwrap(); // } Easy repro and iteration w/ command: RUST_LOG=debug cargo test --release -p crypt4gh --test test_edit_list -- --nocapture --test-threads 1 --- src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 73c84d9..dc46cc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,6 +325,8 @@ pub fn decrypt( let mut write_info = WriteInfo::new(range_start, range_span, write_buffer); + // TODO: Might fail here on Some() due to read_buffer coming from io::stdin is not populated? See run_decrypt() in bin.rs or + // the appropriate test match edit_list { None => body_decrypt(read_buffer, &session_keys, &mut write_info, range_start)?, Some(edit_list_content) => body_decrypt_parts(read_buffer, session_keys, write_info, edit_list_content)?, @@ -356,7 +358,9 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { index: 0, }; + log::debug!("DecryptedBuffer::new() ... about to fetch()"); decryptor.fetch(); + log::debug!("DecryptedBuffer::new() ... about to decrypt()"); decryptor.decrypt(); log::debug!("Index = {}", decryptor.index); decryptor @@ -485,7 +489,7 @@ pub fn body_decrypt_parts( return Err(Crypt4GHError::EmptyEditList); } - log::debug!("body_decrypt_parts()'s session_keys: {:#?}", session_keys); + //log::debug!("body_decrypt_parts()'s session_keys: {:#?}", session_keys); let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output); log::debug!("body_decrypt_parts()'s decrypted content length: {:#?}", decrypted.buf.len()); From 6520532c5a7090361cb1748e61bbabaa9d0322bb Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 13 Oct 2023 15:38:39 +1100 Subject: [PATCH 44/64] Migrated edit_list_gen sodiumoxide remainders, getting UnableToDecryptBlock for edit_list now, progress... --- src/lib.rs | 4 ++-- tests/edit_list_gen.rs | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dc46cc3..43c3cf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,9 +367,9 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { } fn fetch(&mut self) { - //self.block += 1; //TODO: Why???? + self.block += 1; //TODO: Why? Spec says that all must be 0-indexed? - //self.buf.clear();//TODO: Needed???? + self.buf.clear();//TODO: Needed???? log::debug!("fetch()'s fetching block idx: {}", self.block); diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 7eda5c2..a9c443f 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -5,7 +5,9 @@ use std::io::Write; use std::path::{Path, PathBuf}; use crypt4gh::error::Crypt4GHError; -use rand::Rng; +use chacha20poly1305::{ self, Key, Nonce }; +use rand_chacha; +use rand::{Rng, SeedableRng}; pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, passphrase: &str) -> Result<(), Crypt4GHError> { let mut rng = rand::thread_rng(); @@ -68,16 +70,16 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p log::debug!("header length: {}", header_bytes.len()); - outfile; // TODO: Implement the code below w/ Rustcrypto here before returning output + // TODO: Perhaps migrate rest of this file to rnd instead of rng (crypto-safe PRNG?) + let rnd = rand_chacha::ChaCha20Rng::from_entropy(); + let seed = &rnd.get_seed()[..12]; // TODO: Reasonable to cut seed like this? // Output the message - // sodiumoxide::init().expect("Unable to initialize libsodium"); - // for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { - // let nonce_bytes = sodiumoxide::randombytes::randombytes(12); - // let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); - // let key = chacha20poly1305_ietf::Key::from_slice(&session_key).unwrap(); - // let encrypted_segment = crypt4gh::encrypt_segment(segment, nonce, &key); - // outfile.write_all(&encrypted_segment).unwrap(); - // } + for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { + let nonce = Nonce::from_slice(seed); + let key = Key::from_slice(&session_key); + let encrypted_segment = crypt4gh::encrypt_segment(segment, *nonce, &key)?; + outfile.write_all(&encrypted_segment)?; + } Ok(()) } From 5e5457b29c88cd56da10eca479e409594f5b9be3 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 13 Oct 2023 16:01:58 +1100 Subject: [PATCH 45/64] Better errors when they are properly propagated: 'range end index 32081 out of range for slice of length 60', on lib.rs:454:29 Although the length of the cyphertext seems to be varying one through executions: thread 'main' panicked at 'range end index 12168 out of range for slice of length 36', /Users/rvalls/dev/umccr/crypt4gh-rust/src/lib.rs:454:29 Sometimes leading to the original empty block message error: (...) DEBUG crypt4gh > Finished skipping DEBUG crypt4gh > Decrypting block: [] thread 'main' panicked at 'assertion failed: mid <= self.len()', src/lib.rs:575:45 stack backtrace: 0: _rust_begin_unwind 1: core::panicking::panic_fmt 2: core::panicking::panic 3: crypt4gh::decrypt_block 4: crypt4gh::DecryptedBuffer::decrypt 5: crypt4gh::body_decrypt_parts 6: crypt4gh::main --- src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 43c3cf3..f9c6773 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -384,13 +384,14 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { self.is_decrypted = false; } - fn decrypt(&mut self) { + fn decrypt(&mut self) -> Result<(), Crypt4GHError> { // Decrypts its buffer if !self.is_decrypted { log::debug!("Decrypting block: {:#?}", &self.buf); - self.buf = decrypt_block(&self.buf, &self.session_keys).unwrap(); + self.buf = decrypt_block(&self.buf, &self.session_keys)?; self.is_decrypted = true; } + Ok(()) } fn skip(&mut self, size: usize) -> Result<(), Crypt4GHError> { @@ -568,15 +569,16 @@ pub fn body_decrypt( Ok(()) } -/// Reads and returns the first successfully decrypted block, trying all the session keys against one ciphersegment. +/// Reads and returns the first successfully decrypted block, iterating through all the session keys against one ciphersegment. fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result, Crypt4GHError> { - log::debug!("The cyphersegment is: {:#?}", ciphersegment); + //log::debug!("Decrypt_block()'s the cyphersegment is: {:#?}", ciphersegment); let (nonce_slice, data) = ciphersegment.split_at(12); let nonce_bytes: [u8; 12] = nonce_slice .try_into() .map_err(|_| Crypt4GHError::UnableToWrapNonce)?; for key in session_keys { + log::debug!("decrypt_block()'s session key: {:#?}", key); let key_t = Key::from_slice(&key); let key_1 = chacha20poly1305::ChaCha20Poly1305::new(&key_t); From d54dfb174e16986f9f12364497f2ab1ae5aa1e88 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 16 Oct 2023 11:59:00 +1100 Subject: [PATCH 46/64] Closer look at the parameters/interaction between body_decrypt_parts(), run_decrypt() and other functions lower in the backtrace --- src/bin.rs | 2 ++ src/lib.rs | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 0312f97..05269fc 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -152,6 +152,8 @@ fn run_decrypt(sk: Option, sender_pk: Option, range: Option DecryptedBuffer<'a, W> { log::debug!("DecryptedBuffer::new() ... about to fetch()"); decryptor.fetch(); log::debug!("DecryptedBuffer::new() ... about to decrypt()"); - decryptor.decrypt(); + decryptor.decrypt().unwrap(); log::debug!("Index = {}", decryptor.index); decryptor } @@ -427,7 +427,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!("Finished skipping"); // Apply - self.decrypt(); + self.decrypt()?; Ok(()) } @@ -449,7 +449,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { let n_bytes = usize::min(SEGMENT_SIZE - self.index, remaining_size); // Process - self.decrypt(); + self.decrypt()?; self.output .write_all(&self.buf[self.index..self.index + n_bytes]) .unwrap(); From e658edbdb8fdf7ca3fcf9f0563b2d650f8560bf3 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 16 Oct 2023 12:27:48 +1100 Subject: [PATCH 47/64] Unable to decrypt **which** block... --- src/error.rs | 4 ++-- src/header.rs | 4 ++-- src/lib.rs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/error.rs b/src/error.rs index c8ff4f4..d0d682d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,8 +35,8 @@ pub enum Crypt4GHError { InvalidSSHKey, #[error("Unable to wrap nonce")] UnableToWrapNonce, - #[error("Could not decrypt that block")] - UnableToDecryptBlock, + #[error("Could not decrypt block: {0:?}")] + UnableToDecryptBlock(Vec), #[error("Unable to decode with base64 the key (ERROR = {0:?})")] BadBase64Error(Box), #[error("Unable to decode kdfname")] diff --git a/src/header.rs b/src/header.rs index ffc7d7c..ac1ca75 100644 --- a/src/header.rs +++ b/src/header.rs @@ -108,7 +108,7 @@ fn encrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); let ciphertext = cipher.encrypt(&nonce, data) - .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; + .map_err(|_| Crypt4GHError::UnableToDecryptBlock(data.to_vec()))?; Ok(vec![ [0,0,0,0].as_ref(), @@ -235,7 +235,7 @@ fn decrypt_x25519_chacha20_poly1305( //panic!(); let plaintext = cipher.decrypt(&nonce, packet_data) - .map_err(|_| Crypt4GHError::UnableToDecryptBlock)?; + .map_err(|_| Crypt4GHError::UnableToDecryptBlock(packet_data.to_vec()))?; Ok(plaintext) } diff --git a/src/lib.rs b/src/lib.rs index dbfd833..d02a5c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -347,7 +347,7 @@ struct DecryptedBuffer<'a, W: Write> { } impl<'a, W: Write> DecryptedBuffer<'a, W> { - fn new(read_buffer: &'a mut impl Read, session_keys: Vec>, output: WriteInfo<'a, W>) -> Self { + fn new(read_buffer: &'a mut impl Read, session_keys: Vec>, output: WriteInfo<'a, W>) -> Result { let mut decryptor = Self { read_buffer, session_keys, @@ -361,9 +361,9 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!("DecryptedBuffer::new() ... about to fetch()"); decryptor.fetch(); log::debug!("DecryptedBuffer::new() ... about to decrypt()"); - decryptor.decrypt().unwrap(); + decryptor.decrypt()?; log::debug!("Index = {}", decryptor.index); - decryptor + Ok(decryptor) } fn fetch(&mut self) { @@ -491,7 +491,7 @@ pub fn body_decrypt_parts( } //log::debug!("body_decrypt_parts()'s session_keys: {:#?}", session_keys); - let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output); + let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output)?; log::debug!("body_decrypt_parts()'s decrypted content length: {:#?}", decrypted.buf.len()); let mut skip = true; @@ -588,7 +588,7 @@ fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result]) -> Result, Crypt4GHError> { From 861694e9420ac4dce76c12671bff801bacc3214a Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 20 Oct 2023 15:30:23 +1100 Subject: [PATCH 48/64] Use TestResult crate/type for tests, avoiding noisy .unwrap() backtraces, kill a few more unnecessary unwrap()s on lib.rs --- Cargo.lock | 7 +++++++ Cargo.toml | 3 +++ src/lib.rs | 28 ++++++++++++++-------------- tests/edit_list_gen.rs | 2 +- tests/test_edit_list.rs | 10 +++++++--- tests/test_full_file.rs | 17 +++++++++++++---- tests/test_keygen.rs | 5 ++++- tests/test_keygen_length.rs | 9 +++++++-- tests/test_keys.rs | 13 ++++++++++--- tests/test_multiple_recipients.rs | 9 +++++++-- tests/test_segments.rs | 13 ++++++++++--- 11 files changed, 83 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b4d011..2f0ec15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,7 @@ dependencies = [ "rpassword", "scrypt", "serde", + "testresult", "thiserror", ] @@ -792,6 +793,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "testresult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e045f5cf9ad69772c1c9652f5567a75df88bbb5a1310a64e53cab140c5c459" + [[package]] name = "thiserror" version = "1.0.31" diff --git a/Cargo.toml b/Cargo.toml index 0b9add1..1d1d67e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,9 @@ rand_chacha = "0.3.1" curve25519-dalek = "4.0.0" +[dev-dependencies] +testresult = "0.3" + [profile.release] lto = true overflow-checks = true diff --git a/src/lib.rs b/src/lib.rs index d02a5c6..0ebbf2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -359,14 +359,14 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { }; log::debug!("DecryptedBuffer::new() ... about to fetch()"); - decryptor.fetch(); + decryptor.fetch()?; log::debug!("DecryptedBuffer::new() ... about to decrypt()"); decryptor.decrypt()?; log::debug!("Index = {}", decryptor.index); Ok(decryptor) } - fn fetch(&mut self) { + fn fetch(&mut self) -> Result<(), Crypt4GHError>{ self.block += 1; //TODO: Why? Spec says that all must be 0-indexed? self.buf.clear();//TODO: Needed???? @@ -376,12 +376,13 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { // Fetches a block self.read_buffer .take(CIPHER_SEGMENT_SIZE as u64) - .read_to_end(&mut self.buf) - .unwrap(); + .read_to_end(&mut self.buf)?; log::debug!("fetch()'s fetched block: {:#?}", &self.buf); self.is_decrypted = false; + + Ok(()) } fn decrypt(&mut self) -> Result<(), Crypt4GHError> { @@ -409,14 +410,14 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { log::debug!("Left to skip: {} | Buffer size: {}", remaining_size, self.buf.len()); if remaining_size >= SEGMENT_SIZE { - self.fetch(); + self.fetch()?; remaining_size -= SEGMENT_SIZE; log::debug!("skip()'s skipping a whole segment, remaining size: {}", remaining_size); } else { if (self.index + remaining_size) > SEGMENT_SIZE { - self.fetch(); + self.fetch()?; } self.index = (self.index + remaining_size) % SEGMENT_SIZE; log::debug!("skip()'s Index for remaining_size of the segment = {}", self.index); @@ -451,14 +452,13 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { // Process self.decrypt()?; self.output - .write_all(&self.buf[self.index..self.index + n_bytes]) - .unwrap(); + .write_all(&self.buf[self.index..self.index + n_bytes])?; // Advance self.index = (self.index + n_bytes) % self.buf.len(); log::debug!("Index = {}", self.index); if self.index == 0 { - self.fetch(); + self.fetch()?; } // Reduce @@ -714,21 +714,21 @@ pub fn rearrange( let data = read_buffer .by_ref() .take((SEGMENT_SIZE + CIPHER_DIFF) as u64) - .read_to_end(&mut buf); + .read_to_end(&mut buf)?; let keep_segment = segment_oracle.next().unwrap(); log::debug!("Keep segment: {:?}", keep_segment); match data { - Ok(0) => break, - Ok(n) => { + 0 => break, + n => { if keep_segment { write_buffer.write_all(&buf[0..n])?; } }, - Err(e) if e.kind() == io::ErrorKind::Interrupted => (), - Err(e) => return Err(Crypt4GHError::ReadRemainderError(e.into())), + // Err(e) if e.kind() == io::ErrorKind::Interrupted => (), + // Err(e) => return Err(Crypt4GHError::ReadRemainderError(e.into())), } } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index a9c443f..836cc4a 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -66,7 +66,7 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p .flat_map(|packet| crypt4gh::header::encrypt(&packet, &keys).unwrap()) .collect(); let header_bytes = crypt4gh::header::serialize(header_packets); - outfile.write_all(&header_bytes).unwrap(); + outfile.write_all(&header_bytes)?; log::debug!("header length: {}", header_bytes.len()); diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index 8d3a29a..3ba3064 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -4,6 +4,7 @@ mod test_common; use std::fs::File; pub use test_common::*; +use testresult::TestResult; const INPUT_EDIT_LIST: &str = "Let's have beers @@ -14,7 +15,7 @@ at 7pm? "; #[test] -fn test_send_message_buried() { +fn test_send_message_buried() -> TestResult { // Init let init = Cleanup::new(); @@ -25,14 +26,14 @@ fn test_send_message_buried() { ); // Bob encrypts a file for Alice, and tucks in an edit list. The skipped pieces are random data. - let mut file = File::create(&temp_file("message.bob.c4gh")).unwrap(); + let mut file = File::create(&temp_file("message.bob.c4gh"))?; edit_list_gen::generate( &add_prefix(BOB_SECKEY), &add_prefix(ALICE_PUBKEY), INPUT_EDIT_LIST, &mut file, BOB_PASSPHRASE, - ).unwrap(); + )?; // Decrypt CommandUnderTest::new() @@ -49,4 +50,7 @@ fn test_send_message_buried() { // Cleanup drop(init); + + // All went fine! + Ok(()) } diff --git a/tests/test_full_file.rs b/tests/test_full_file.rs index 06f0803..4ba4e88 100644 --- a/tests/test_full_file.rs +++ b/tests/test_full_file.rs @@ -1,9 +1,10 @@ mod test_common; pub use test_common::*; +use testresult::TestResult; #[test] -fn test_encrypt_decrypt_random() { +fn test_encrypt_decrypt_random() -> TestResult { // Init let init = Cleanup::new(); @@ -37,10 +38,12 @@ fn test_encrypt_decrypt_random() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_encrypt_decrypt_testfile() { +fn test_encrypt_decrypt_testfile() -> TestResult { // Init let init = Cleanup::new(); @@ -71,10 +74,12 @@ fn test_encrypt_decrypt_testfile() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_encrypt_then_reencrypt() { +fn test_encrypt_then_reencrypt() -> TestResult { // Init let init = Cleanup::new(); @@ -117,10 +122,12 @@ fn test_encrypt_then_reencrypt() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_encrypt_with_missing_key() { +fn test_encrypt_with_missing_key() -> TestResult { // Init let init = Cleanup::new(); @@ -152,4 +159,6 @@ fn test_encrypt_with_missing_key() { // Cleanup drop(init); + + Ok(()) } diff --git a/tests/test_keygen.rs b/tests/test_keygen.rs index 8673831..dc0109d 100644 --- a/tests/test_keygen.rs +++ b/tests/test_keygen.rs @@ -3,9 +3,10 @@ mod test_common; use std::path::PathBuf; pub use test_common::*; +use testresult::TestResult; #[test] -fn test_keygen() { +fn test_keygen() -> TestResult { // Init let init = Cleanup::new(); @@ -50,4 +51,6 @@ fn test_keygen() { // Cleanup drop(init); + + Ok(()) } diff --git a/tests/test_keygen_length.rs b/tests/test_keygen_length.rs index f586a98..9f66c3e 100644 --- a/tests/test_keygen_length.rs +++ b/tests/test_keygen_length.rs @@ -3,9 +3,10 @@ mod test_common; use std::path::PathBuf; pub use test_common::*; +use testresult::TestResult; #[test] -fn test_keygen_length_encrypted() { +fn test_keygen_length_encrypted() -> TestResult { // Init let init = Cleanup::new(); @@ -21,10 +22,12 @@ fn test_keygen_length_encrypted() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_keygen_length_not_encrypted() { +fn test_keygen_length_not_encrypted() -> TestResult { // Init let init = Cleanup::new(); @@ -40,4 +43,6 @@ fn test_keygen_length_not_encrypted() { // Cleanup drop(init); + + Ok(()) } diff --git a/tests/test_keys.rs b/tests/test_keys.rs index 3ccc605..0c812b0 100644 --- a/tests/test_keys.rs +++ b/tests/test_keys.rs @@ -1,9 +1,10 @@ mod test_common; pub use test_common::*; +use testresult::TestResult; #[test] -fn encrypt_ssh_decrypt() { +fn encrypt_ssh_decrypt() -> TestResult { // Init let init = Cleanup::new(); @@ -42,10 +43,12 @@ fn encrypt_ssh_decrypt() { // Cleanup drop(init); + + Ok(()) } #[test] -fn encrypt_decrypt_ssh() { +fn encrypt_decrypt_ssh() -> TestResult{ // Init let init = Cleanup::new(); @@ -84,10 +87,12 @@ fn encrypt_decrypt_ssh() { // Cleanup drop(init); + + Ok(()) } #[test] -fn encrypt_ssh_decrypt_ssh() { +fn encrypt_ssh_decrypt_ssh() -> TestResult { // Init let init = Cleanup::new(); @@ -129,4 +134,6 @@ fn encrypt_ssh_decrypt_ssh() { // Cleanup drop(init); + + Ok(()) } diff --git a/tests/test_multiple_recipients.rs b/tests/test_multiple_recipients.rs index d0a51ea..0bf6079 100644 --- a/tests/test_multiple_recipients.rs +++ b/tests/test_multiple_recipients.rs @@ -1,9 +1,10 @@ mod test_common; pub use test_common::*; +use testresult::TestResult; #[test] -fn send_to_bob_and_alice() { +fn send_to_bob_and_alice() -> TestResult { // Init let init = Cleanup::new(); @@ -48,10 +49,12 @@ fn send_to_bob_and_alice() { // Cleanup drop(init); + + Ok(()) } #[test] -fn reencrypt_to_bob_and_alice() { +fn reencrypt_to_bob_and_alice() -> TestResult { // Init let init = Cleanup::new(); @@ -106,4 +109,6 @@ fn reencrypt_to_bob_and_alice() { // Cleanup drop(init); + + Ok(()) } diff --git a/tests/test_segments.rs b/tests/test_segments.rs index 0179b4a..3716512 100644 --- a/tests/test_segments.rs +++ b/tests/test_segments.rs @@ -1,9 +1,10 @@ mod test_common; pub use test_common::*; +use testresult::TestResult; #[test] -fn test_send_all_b() { +fn test_send_all_b() -> TestResult { // Init let init = Cleanup::new(); @@ -39,10 +40,12 @@ fn test_send_all_b() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_send_one_a_all_b_one_c() { +fn test_send_one_a_all_b_one_c() -> TestResult { // Init let init = Cleanup::new(); @@ -75,10 +78,12 @@ fn test_send_one_a_all_b_one_c() { // Cleanup drop(init); + + Ok(()) } #[test] -fn test_rearrange_one_a_all_b_one_c() { +fn test_rearrange_one_a_all_b_one_c() -> TestResult { // Init let init = Cleanup::new(); @@ -133,4 +138,6 @@ fn test_rearrange_one_a_all_b_one_c() { // Cleanup drop(init); + + Ok(()) } From 854b015167cba6df87b2334cc4f5dddb56ec4c9a Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 24 Oct 2023 15:40:31 +1100 Subject: [PATCH 49/64] Fix testsuite to run the decrypt() functions instead of the cmdline. Add more informative debug messages. Carry on with the 4 vs 8 bytes on header_packets. Fix env_logger issues by fixing the aforementioned test, improving debuggability. Co-authored-by: Marko Malenic --- src/error.rs | 6 ++--- src/header.rs | 6 ++--- src/lib.rs | 33 +++++++++---------------- tests/edit_list_gen.rs | 9 ++++--- tests/test_edit_list.rs | 55 ++++++++++++++++++++++++++++++++++------- 5 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/error.rs b/src/error.rs index d0d682d..b6072d8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,8 +35,8 @@ pub enum Crypt4GHError { InvalidSSHKey, #[error("Unable to wrap nonce")] UnableToWrapNonce, - #[error("Could not decrypt block: {0:?}")] - UnableToDecryptBlock(Vec), + #[error("Could not decrypt block: {0:?}, {1:?}")] + UnableToDecryptBlock(Vec, String), #[error("Unable to decode with base64 the key (ERROR = {0:?})")] BadBase64Error(Box), #[error("Unable to decode kdfname")] @@ -52,7 +52,7 @@ pub enum Crypt4GHError { #[error("Unsupported Header Encryption Method: {0}")] BadHeaderEncryptionMethod(u32), #[error("Unable to encrypt packet: None of the keys were used")] - UnableToEncryptPacket, + UnableToEncryptPacket(String), #[error("Decryption failed -> Invalid data")] InvalidData, diff --git a/src/header.rs b/src/header.rs index ac1ca75..75a1cfc 100644 --- a/src/header.rs +++ b/src/header.rs @@ -108,7 +108,7 @@ fn encrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); let ciphertext = cipher.encrypt(&nonce, data) - .map_err(|_| Crypt4GHError::UnableToDecryptBlock(data.to_vec()))?; + .map_err(|err| Crypt4GHError::UnableToEncryptPacket(err.to_string()))?; Ok(vec![ [0,0,0,0].as_ref(), @@ -195,7 +195,7 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) n => return Err(Crypt4GHError::BadHeaderEncryptionMethod(n)), } } - Err(Crypt4GHError::UnableToEncryptPacket) + Err(Crypt4GHError::UnableToEncryptPacket("Error encrypting".to_string())) } fn decrypt_x25519_chacha20_poly1305( @@ -235,7 +235,7 @@ fn decrypt_x25519_chacha20_poly1305( //panic!(); let plaintext = cipher.decrypt(&nonce, packet_data) - .map_err(|_| Crypt4GHError::UnableToDecryptBlock(packet_data.to_vec()))?; + .map_err(|err| Crypt4GHError::UnableToDecryptBlock(packet_data.to_vec(), err.to_string()))?; Ok(plaintext) } diff --git a/src/lib.rs b/src/lib.rs index 0ebbf2d..cc20403 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -577,30 +577,19 @@ fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result]) -> Result, Crypt4GHError> { -// let (nonce_slice, data) = ciphersegment.split_at(12); -// let nonce = Nonce::from_slice(nonce_slice).ok_or(Crypt4GHError::UnableToWrapNonce)?; - -// session_keys -// .iter() -// .find_map(|key| Key::from_slice(key).and_then(|key| chacha20poly1305_ietf::open(data, None, &nonce, &key).ok())) -// .ok_or(Crypt4GHError::UnableToDecryptBlock) -// } - /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. /// /// Reads from the `read_buffer` and writes the reencrypted data to `write_buffer`. diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 836cc4a..2aacacb 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -61,14 +61,15 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p crypt4gh::header::make_packet_data_edit_list(edits), ]; - let header_packets = packets + let header_packets: Vec> = packets .into_iter() .flat_map(|packet| crypt4gh::header::encrypt(&packet, &keys).unwrap()) .collect(); - let header_bytes = crypt4gh::header::serialize(header_packets); - outfile.write_all(&header_bytes)?; + let mangled_header_packets = header_packets; + let mangled_header_packets = crypt4gh::header::serialize(mangled_header_packets); + outfile.write_all(&mangled_header_packets)?; - log::debug!("header length: {}", header_bytes.len()); + log::debug!("header length: {}", mangled_header_packets.len()); // TODO: Perhaps migrate rest of this file to rnd instead of rng (crypto-safe PRNG?) let rnd = rand_chacha::ChaCha20Rng::from_entropy(); diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index 3ba3064..7c7c302 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -1,10 +1,14 @@ mod edit_list_gen; mod test_common; -use std::fs::File; +use std::{fs::File, path::PathBuf}; pub use test_common::*; use testresult::TestResult; +use crypt4gh::keys::get_private_key; +use crypt4gh::Keys; +use std::io::Read; + const INPUT_EDIT_LIST: &str = "Let's have beers @@ -16,6 +20,8 @@ at 7pm? #[test] fn test_send_message_buried() -> TestResult { + pretty_env_logger::init(); + // Init let init = Cleanup::new(); @@ -35,15 +41,46 @@ fn test_send_message_buried() -> TestResult { BOB_PASSPHRASE, )?; + let sender_pubkey = None; + let (range_start, range_span) = (0, None); + + let seckey = get_private_key(PathBuf::from("tests/testfiles/alice.sec"), Ok(ALICE_PASSPHRASE.to_string()))?; + + let keys = vec![Keys { + method: 0, + privkey: seckey, + recipient_pubkey: vec![], + }]; + + // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); + + let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; + + let mut out = vec![]; + file.read_to_end(&mut out)?; + println!("message: {:?}", out); + + let mut buf_in = std::io::BufReader::new(&out[..]); + + let mut buf = vec![]; // Decrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - .arg("decrypt") - .arg("--sk") - .arg(ALICE_SECKEY) - .pipe_in(&temp_file("message.bob.c4gh")) - .pipe_out(&temp_file("message.alice")) - .succeeds(); + crypt4gh::decrypt( + &keys, + &mut buf_in, + &mut buf, + range_start, + range_span, + &sender_pubkey, + )?; + + // CommandUnderTest::new() + // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) + // .arg("decrypt") + // .arg("--sk") + // .arg(ALICE_SECKEY) + // .pipe_in(&temp_file("message.bob.c4gh")) + // .pipe_out(&temp_file("message.alice")) + // .succeeds(); // Compare equal(&temp_file("message.bob"), &temp_file("message.alice")); From 0db7b17d01bb66447b0e36129106f10fc5c6217c Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Oct 2023 11:29:37 +1100 Subject: [PATCH 50/64] Matching output debug messages from tests w/ sodiumoxide's impl for easy vimdiffing --- src/bin.rs | 3 +-- src/header.rs | 31 ++++++++++++------------- src/keys.rs | 63 ++++++++++++++------------------------------------- src/lib.rs | 4 ++-- 4 files changed, 35 insertions(+), 66 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 05269fc..3db315b 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -20,7 +20,6 @@ use cli::{Args, Command}; use crypt4gh::error::Crypt4GHError; use crypt4gh::keys::{get_private_key, get_public_key}; use crypt4gh::{self, keys, Keys}; -use itertools::Itertools; use regex::Regex; use rpassword::prompt_password; @@ -75,7 +74,7 @@ fn retrieve_private_key(sk: Option, generate: bool) -> Result, if generate && seckey_path.is_none() { let skey = keys::generate_private_key()?; - log::info!("Generating Private Key: {:02x?}", skey.iter().format("")); + log::info!("Generating Private Key: {:02x?}", skey); Ok(skey) } else { diff --git a/src/header.rs b/src/header.rs index 75a1cfc..3844ce6 100644 --- a/src/header.rs +++ b/src/header.rs @@ -83,17 +83,17 @@ fn encrypt_x25519_chacha20_poly1305( let pubkey = server_sk.public_key(); - log::debug!(" RustCrypto encrypt() packed data({}): {:02x?}", data.len(), data.iter()); - log::debug!(" RustCrypto encrypt() public key({}): {:02x?}", pubkey.as_ref().len(), pubkey.as_ref().iter()); + log::debug!(" packed data({}): {:02x?}", data.len(), data); + log::debug!(" public key({}): {:02x?}", pubkey.as_ref().len(), pubkey.as_ref()); log::debug!( - " RustCrypto encrypt() private key({}): {:02x?}", + " private key({}): {:02x?}", seckey[0..32].len(), - &seckey[0..32].iter() + &seckey[0..32] ); log::debug!( - " RustCrypto encrypt() recipient public key({}): {:02x?}", + " recipient public key({}): {:02x?}", recipient_pubkey.len(), - recipient_pubkey.iter() + recipient_pubkey ); // TODO: Make sure this doesn't exceed 2^32 executions, otherwise implement a counter and/or other countermeasures against repeats @@ -103,7 +103,7 @@ fn encrypt_x25519_chacha20_poly1305( let server_session_keys = keypair.session_keys_from(&client_pk); let shared_key = GenericArray::::from_slice(&server_session_keys.rx.as_ref().as_slice()); - log::debug!(" RustCrypto encrypt() shared key: {:02x?}", shared_key); + log::debug!(" shared key: {:02x?}", shared_key); let cipher = ChaCha20Poly1305::new(shared_key); @@ -204,7 +204,7 @@ fn decrypt_x25519_chacha20_poly1305( sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { let peer_pubkey = &encrypted_part[0..32];//PublicKey::BYTES]; - log::debug!(" RustCrypto decrypt() peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); + log::debug!(" peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); @@ -225,15 +225,14 @@ fn decrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); - log::debug!(" RustCrypto peer pubkey: {:02x?}", peer_pubkey.iter()); - //log::debug!(" RustCrypto nonce: {:02x?}", &nonce); - // log::debug!( - // " RustCrypto encrypted data ({}): {:02x?}", - // packet_data.len(), - // packet_data.iter() - // ); + log::debug!(" peer pubkey: {:02x?}", peer_pubkey.iter()); + log::debug!(" nonce: {:02x?}", &nonce); + log::debug!( + " encrypted data ({}): {:02x?}", + packet_data.len(), + packet_data.iter() + ); - //panic!(); let plaintext = cipher.decrypt(&nonce, packet_data) .map_err(|err| Crypt4GHError::UnableToDecryptBlock(packet_data.to_vec(), err.to_string()))?; diff --git a/src/keys.rs b/src/keys.rs index d400122..0e19d64 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -11,7 +11,6 @@ use std::path::PathBuf; use base64::engine::general_purpose; use base64::Engine; -use itertools::Itertools; use lazy_static::lazy_static; use rand_chacha; @@ -190,7 +189,7 @@ fn parse_c4gh_private_key( kdfoptions[3], ])); salt = Some(kdfoptions[4..].to_vec()); - log::debug!("Salt: {:02x?}", salt.iter().format("")); + log::debug!("Salt: {:02x?}", salt); log::debug!("Rounds: {}", rounds.unwrap()); } @@ -214,8 +213,8 @@ fn parse_c4gh_private_key( let passphrase = callback?; let shared_key = derive_key(&kdfname, &passphrase, salt, rounds, 32)?; - log::debug!("Shared Key: {:02x?}", shared_key.iter().format("")); - log::debug!("Nonce: {:02x?}", &private_data[0..12].iter().format("")); + log::debug!("Shared Key: {:02x?}", shared_key); + log::debug!("Nonce: {:02x?}", &private_data[0..12]); let nonce = chacha20poly1305::Nonce::from_slice(&private_data[0..12]);//.ok_or(Crypt4GHError::NoNonce)?; let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; @@ -227,9 +226,9 @@ fn parse_c4gh_private_key( let privkey_plain = ChaCha20Poly1305::new(key).encrypt(nonce, encrypted_data) .map_err(|_| Crypt4GHError::InvalidKeyFormat)?; - log::debug!("RustCrypto's ChaCha20Poly1305::new() key argument: {:?}", &key); - log::debug!("RustCrypto's ChaCha20Poly1305::new() nonce argument: {:?}", &nonce); - log::debug!("RustCrypto's ChaCha20Poly1305::new() encrypted_data argument: {:?}", &encrypted_data); + log::debug!(" key argument: {:?}", &key); + log::debug!(" nonce argument: {:?}", &nonce); + log::debug!(" encrypted_data argument: {:?}", &encrypted_data); log::debug!("Privkey plaintext: {:?}", privkey_plain); Ok(privkey_plain) @@ -278,7 +277,7 @@ fn parse_ssh_private_key( //assert!(kdfoptions_cursor.read_exact(&mut [0_u8]).is_err()); // Log - log::debug!("Salt: {:02x?}", salt.iter().format("")); + log::debug!("Salt: {:02x?}", salt); log::debug!("Rounds: {:?}", rounds); } }, @@ -320,7 +319,7 @@ fn parse_ssh_private_key( log::debug!("Derived Key len: {}", dklen); let derived_key = derive_key(&kdfname, &passphrase, salt, rounds, dklen)?; - log::debug!("Derived Key: {:02x?}", derived_key.iter().format("")); + log::debug!("Derived Key: {:02x?}", derived_key); let private_data = decipher(&ciphername, &derived_key, &private_ciphertext)?; get_skpk_from_decrypted_private_blob(&private_data) @@ -340,7 +339,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let iv = &data[*keylen as usize..]; log::debug!("Decryption Key ({}): {:02x?}", key.len(), key); - log::debug!("IV ({}): {:02x?}", iv.len(), iv.iter().format("")); + log::debug!("IV ({}): {:02x?}", iv.len(), iv); let output = vec![0_u8; private_ciphertext.len()]; let reader = BufReader::new(private_ciphertext); @@ -415,18 +414,18 @@ fn get_skpk_from_decrypted_private_blob(blob: &[u8]) -> Result<([u8; 32], [u8; 3 decode_string_ssh(&mut stream)?; // ignore pubkey let skpk = decode_string_ssh(&mut stream)?; - log::debug!("Private Key blob: {:02x?}", skpk.iter().format("")); + log::debug!("Private Key blob: {:02x?}", skpk.iter()); assert!(skpk.len() == 64, "The length of the private key blob must be 64"); let (sk, pk) = skpk.split_at(32); - log::debug!("ed25519 sk: {:02x?}", sk.iter().format("")); - log::debug!("ed25519 pk: {:02x?}", pk.iter().format("")); + log::debug!("ed25519 sk: {:02x?}", sk.iter()); + log::debug!("ed25519 pk: {:02x?}", pk.iter()); let seckey = convert_ed25519_sk_to_curve25519(sk)?; - log::debug!("x25519 sk: {:02x?}", seckey.iter().format("")); + log::debug!("x25519 sk: {:02x?}", seckey.iter()); let pubkey = convert_ed25519_pk_to_curve25519(pk)?; - log::debug!("x25519 pk: {:02x?}", pubkey.iter().format("")); + log::debug!("x25519 pk: {:02x?}", pubkey.iter()); Ok((seckey, pubkey)) } @@ -612,7 +611,7 @@ pub fn generate_keys( comment: Option, ) -> Result<(), Crypt4GHError> { let skpk = generate_private_key()?; - log::debug!("Private Key: {:02x?}", skpk.iter().format("")); + log::debug!("Private Key: {:02x?}", skpk); // Public key permissions (read & write) let mut pk_file = File::create(pubkey).expect("Unable to create public key file"); @@ -622,7 +621,7 @@ pub fn generate_keys( // Write public key let (sk, pk) = skpk.split_at(32); - log::debug!("Public Key: {:02x?}", pk.iter().format("")); + log::debug!("Public Key: {:02x?}", pk); pk_file.write_all(b"-----BEGIN CRYPT4GH PUBLIC KEY-----\n").unwrap(); pk_file.write_all(general_purpose::STANDARD.encode(pk).as_bytes()).unwrap(); pk_file.write_all(b"\n-----END CRYPT4GH PUBLIC KEY-----\n").unwrap(); @@ -635,7 +634,7 @@ pub fn generate_keys( log::debug!( "Encoded Private Key ({}): {:02x?}", sk_encrypted.len(), - sk.iter().format("") + sk ); sk_file.write_all(b"-----BEGIN CRYPT4GH PRIVATE KEY-----\n").unwrap(); sk_file.write_all(general_purpose::STANDARD.encode(sk_encrypted).as_bytes()).unwrap(); @@ -670,34 +669,6 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> .concat() } else { - -// let kdfname = "scrypt"; -// let (salt_size, rounds) = get_kdf(kdfname)?; -// let salt = randombytes(salt_size); -// let derived_key = derive_key(kdfname, passphrase, Some(salt.clone()), Some(rounds), 32)?; -// let nonce_bytes = randombytes(12); -// let nonce = chacha20poly1305_ietf::Nonce::from_slice(&nonce_bytes).unwrap(); -// let key = chacha20poly1305_ietf::Key::from_slice(&derived_key).unwrap(); -// let encrypted_key = chacha20poly1305_ietf::seal(skpk, None, &nonce, &key); - -// log::debug!("Derived Key: {:02x?}", derived_key.iter().format("")); -// log::debug!("Salt: {:02x?}", salt.iter().format("")); -// log::debug!("Nonce: {:02x?}", nonce.0.to_vec().iter().format("")); - -// vec![ -// C4GH_MAGIC_WORD.to_vec(), -// encode_string_c4gh(Some(kdfname.as_bytes())), -// encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt].concat())), -// encode_string_c4gh(Some(b"chacha20_poly1305")), -// encode_string_c4gh(Some(&vec![nonce.0.to_vec(), encrypted_key].concat())), -// match comment { -// Some(c) => encode_string_c4gh(Some(c.as_bytes())), -// None => [].to_vec(), -// }, -// ] -// .concat() -// }) -// } let kdfname = "scrypt"; let (salt_size, rounds) = get_kdf(kdfname)?; diff --git a/src/lib.rs b/src/lib.rs index cc20403..a273102 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,7 +378,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { .take(CIPHER_SEGMENT_SIZE as u64) .read_to_end(&mut self.buf)?; - log::debug!("fetch()'s fetched block: {:#?}", &self.buf); + log::debug!("fetch()'s fetched block: {:?}", &self.buf); self.is_decrypted = false; @@ -388,7 +388,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { fn decrypt(&mut self) -> Result<(), Crypt4GHError> { // Decrypts its buffer if !self.is_decrypted { - log::debug!("Decrypting block: {:#?}", &self.buf); + log::debug!("Decrypting block: {:?}", &self.buf); self.buf = decrypt_block(&self.buf, &self.session_keys)?; self.is_decrypted = true; } From 96179bae5b4501cf095a85eb8d28f51e3026f210 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Oct 2023 14:15:23 +1100 Subject: [PATCH 51/64] Matching outputs --- src/bin.rs | 2 +- src/header.rs | 6 ++-- tests/test_edit_list.rs | 70 ++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 3db315b..d2fe831 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -151,7 +151,7 @@ fn run_decrypt(sk: Option, sender_pk: Option, range: Option>) -> Result, Crypt4GHError> { let packet_encryption_method = bincode::deserialize::(packet).map_err(|_| Crypt4GHError::ReadPacketEncryptionMethod)?; + log::debug!("Header Packet Encryption Method: {}", packet_encryption_method); for key in keys { @@ -187,8 +187,8 @@ fn decrypt_packet(packet: &[u8], keys: &[Keys], sender_pubkey: &Option>) match packet_encryption_method { 0 => { - let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[8..], &key.privkey, sender_pubkey); - //log::debug!("Decrypting packet: {:#?}\n into plaintext packet: {:#?}\n", &packet[8..], &plaintext_packet); + let plaintext_packet = decrypt_x25519_chacha20_poly1305(&packet[4..], &key.privkey, sender_pubkey); + //log::debug!("Decrypting packet: {:?}\n into plaintext packet: {:?}\n", &packet[8..], &plaintext_packet); return plaintext_packet; }, 1 => unimplemented!("AES-256-GCM support is not implemented"), diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index 7c7c302..c201dfd 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -41,46 +41,46 @@ fn test_send_message_buried() -> TestResult { BOB_PASSPHRASE, )?; - let sender_pubkey = None; - let (range_start, range_span) = (0, None); + // let sender_pubkey = None; + // let (range_start, range_span) = (0, None); - let seckey = get_private_key(PathBuf::from("tests/testfiles/alice.sec"), Ok(ALICE_PASSPHRASE.to_string()))?; + // let seckey = get_private_key(PathBuf::from("tests/testfiles/alice.sec"), Ok(ALICE_PASSPHRASE.to_string()))?; - let keys = vec![Keys { - method: 0, - privkey: seckey, - recipient_pubkey: vec![], - }]; + // let keys = vec![Keys { + // method: 0, + // privkey: seckey, + // recipient_pubkey: vec![], + // }]; - // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); + // // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); - let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; + // let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; - let mut out = vec![]; - file.read_to_end(&mut out)?; - println!("message: {:?}", out); - - let mut buf_in = std::io::BufReader::new(&out[..]); - - let mut buf = vec![]; - // Decrypt - crypt4gh::decrypt( - &keys, - &mut buf_in, - &mut buf, - range_start, - range_span, - &sender_pubkey, - )?; - - // CommandUnderTest::new() - // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - // .arg("decrypt") - // .arg("--sk") - // .arg(ALICE_SECKEY) - // .pipe_in(&temp_file("message.bob.c4gh")) - // .pipe_out(&temp_file("message.alice")) - // .succeeds(); + // let mut out = vec![]; + // file.read_to_end(&mut out)?; + // println!("message: {:?}", out); + + // let mut buf_in = std::io::BufReader::new(&out[..]); + + // let mut buf = vec![]; + // // Decrypt + // crypt4gh::decrypt( + // &keys, + // &mut buf_in, + // &mut buf, + // range_start, + // range_span, + // &sender_pubkey, + // )?; + + CommandUnderTest::new() + .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) + .arg("decrypt") + .arg("--sk") + .arg(ALICE_SECKEY) + .pipe_in(&temp_file("message.bob.c4gh")) + .pipe_out(&temp_file("message.alice")) + .succeeds(); // Compare equal(&temp_file("message.bob"), &temp_file("message.alice")); From 4bd136e6e87dd4611074f9292698b3f13f8cf31b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Oct 2023 14:31:49 +1100 Subject: [PATCH 52/64] Output matching except secret key and shared key --- src/header.rs | 6 ++-- src/lib.rs | 22 ++++++------- tests/test_edit_list.rs | 70 ++++++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/header.rs b/src/header.rs index cc1a669..c35ff11 100644 --- a/src/header.rs +++ b/src/header.rs @@ -204,7 +204,7 @@ fn decrypt_x25519_chacha20_poly1305( sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { let peer_pubkey = &encrypted_part[0..32];//PublicKey::BYTES]; - log::debug!(" peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey.iter()); + //log::debug!(" peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey); if sender_pubkey.is_some() && sender_pubkey.clone().unwrap().as_slice() != peer_pubkey { return Err(Crypt4GHError::InvalidPeerPubPkey); @@ -225,12 +225,12 @@ fn decrypt_x25519_chacha20_poly1305( let cipher = ChaCha20Poly1305::new(shared_key); - log::debug!(" peer pubkey: {:02x?}", peer_pubkey.iter()); + log::debug!(" peer pubkey: {:02x?}", peer_pubkey); log::debug!(" nonce: {:02x?}", &nonce); log::debug!( " encrypted data ({}): {:02x?}", packet_data.len(), - packet_data.iter() + packet_data ); let plaintext = cipher.decrypt(&nonce, packet_data) diff --git a/src/lib.rs b/src/lib.rs index a273102..aaf629c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -358,11 +358,11 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { index: 0, }; - log::debug!("DecryptedBuffer::new() ... about to fetch()"); + //log::debug!("DecryptedBuffer::new() ... about to fetch()"); decryptor.fetch()?; - log::debug!("DecryptedBuffer::new() ... about to decrypt()"); + //log::debug!("DecryptedBuffer::new() ... about to decrypt()"); decryptor.decrypt()?; - log::debug!("Index = {}", decryptor.index); + //log::debug!("Index = {}", decryptor.index); Ok(decryptor) } @@ -371,14 +371,14 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { self.buf.clear();//TODO: Needed???? - log::debug!("fetch()'s fetching block idx: {}", self.block); + //log::debug!("fetch()'s fetching block idx: {}", self.block); // Fetches a block self.read_buffer .take(CIPHER_SEGMENT_SIZE as u64) .read_to_end(&mut self.buf)?; - log::debug!("fetch()'s fetched block: {:?}", &self.buf); + //log::debug!("fetch()'s fetched block: {:?}", &self.buf); self.is_decrypted = false; @@ -388,7 +388,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { fn decrypt(&mut self) -> Result<(), Crypt4GHError> { // Decrypts its buffer if !self.is_decrypted { - log::debug!("Decrypting block: {:?}", &self.buf); + //log::debug!("Decrypting block: {:?}", &self.buf); self.buf = decrypt_block(&self.buf, &self.session_keys)?; self.is_decrypted = true; } @@ -483,16 +483,16 @@ pub fn body_decrypt_parts( output: WriteInfo, edit_list: Vec, ) -> Result<(), Crypt4GHError> { - log::debug!("body_decrypt_parts()'s Edit List: {:?}", edit_list); + //log::debug!("body_decrypt_parts()'s Edit List: {:?}", edit_list); if edit_list.is_empty() { - log::debug!("body_decrypt_parts()'s Edit List is empty"); + //log::debug!("body_decrypt_parts()'s Edit List is empty"); return Err(Crypt4GHError::EmptyEditList); } //log::debug!("body_decrypt_parts()'s session_keys: {:#?}", session_keys); let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output)?; - log::debug!("body_decrypt_parts()'s decrypted content length: {:#?}", decrypted.buf.len()); + //log::debug!("body_decrypt_parts()'s decrypted content length: {:#?}", decrypted.buf.len()); let mut skip = true; @@ -513,7 +513,7 @@ pub fn body_decrypt_parts( // If we finished with a skip, read until the end loop { let n = decrypted.read(SEGMENT_SIZE)?; - log::debug!("body_decrypt_parts()'s reading until the end index: {}", n); + //log::debug!("body_decrypt_parts()'s reading until the end index: {}", n); if n == 0 { break; } @@ -584,7 +584,7 @@ fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec]) -> Result TestResult { BOB_PASSPHRASE, )?; - // let sender_pubkey = None; - // let (range_start, range_span) = (0, None); + let sender_pubkey = None; + let (range_start, range_span) = (0, None); - // let seckey = get_private_key(PathBuf::from("tests/testfiles/alice.sec"), Ok(ALICE_PASSPHRASE.to_string()))?; + let seckey = get_private_key(PathBuf::from("tests/testfiles/alice.sec"), Ok(ALICE_PASSPHRASE.to_string()))?; - // let keys = vec![Keys { - // method: 0, - // privkey: seckey, - // recipient_pubkey: vec![], - // }]; + let keys = vec![Keys { + method: 0, + privkey: seckey, + recipient_pubkey: vec![], + }]; - // // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); + // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); - // let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; + let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; - // let mut out = vec![]; - // file.read_to_end(&mut out)?; - // println!("message: {:?}", out); - - // let mut buf_in = std::io::BufReader::new(&out[..]); - - // let mut buf = vec![]; - // // Decrypt - // crypt4gh::decrypt( - // &keys, - // &mut buf_in, - // &mut buf, - // range_start, - // range_span, - // &sender_pubkey, - // )?; - - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - .arg("decrypt") - .arg("--sk") - .arg(ALICE_SECKEY) - .pipe_in(&temp_file("message.bob.c4gh")) - .pipe_out(&temp_file("message.alice")) - .succeeds(); + let mut out = vec![]; + file.read_to_end(&mut out)?; + println!("message: {:?}", out); + + let mut buf_in = std::io::BufReader::new(&out[..]); + + let mut buf = vec![]; + // Decrypt + crypt4gh::decrypt( + &keys, + &mut buf_in, + &mut buf, + range_start, + range_span, + &sender_pubkey, + )?; + + // CommandUnderTest::new() + // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) + // .arg("decrypt") + // .arg("--sk") + // .arg(ALICE_SECKEY) + // .pipe_in(&temp_file("message.bob.c4gh")) + // .pipe_out(&temp_file("message.alice")) + // .succeeds(); // Compare equal(&temp_file("message.bob"), &temp_file("message.alice")); From 3b566577dcc50b1e67a77f24befbf83c29880d8e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Oct 2023 14:48:31 +1100 Subject: [PATCH 53/64] 1-1 matching of test outputs, only edit list, nonce and encrypted data vary --- src/header.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/header.rs b/src/header.rs index c35ff11..c4af238 100644 --- a/src/header.rs +++ b/src/header.rs @@ -103,7 +103,7 @@ fn encrypt_x25519_chacha20_poly1305( let server_session_keys = keypair.session_keys_from(&client_pk); let shared_key = GenericArray::::from_slice(&server_session_keys.rx.as_ref().as_slice()); - log::debug!(" shared key: {:02x?}", shared_key); + log::debug!(" shared key: {:02x?}", shared_key.to_vec()); let cipher = ChaCha20Poly1305::new(shared_key); @@ -203,6 +203,8 @@ fn decrypt_x25519_chacha20_poly1305( privkey: &[u8], sender_pubkey: &Option>, ) -> Result, Crypt4GHError> { + log::debug!(" secret key: {:02x?}", &privkey[0..32]); + let peer_pubkey = &encrypted_part[0..32];//PublicKey::BYTES]; //log::debug!(" peer_pubkey({}): {:02x?}", peer_pubkey.len(), peer_pubkey); @@ -233,6 +235,8 @@ fn decrypt_x25519_chacha20_poly1305( packet_data ); + log::debug!("shared key: {:02x?}", shared_key); + let plaintext = cipher.decrypt(&nonce, packet_data) .map_err(|err| Crypt4GHError::UnableToDecryptBlock(packet_data.to_vec(), err.to_string()))?; From fa04f74a8fafc2127e5e65e96aa52e32fa649535 Mon Sep 17 00:00:00 2001 From: Marko Malenic Date: Thu, 26 Oct 2023 15:18:21 +1100 Subject: [PATCH 54/64] fix: use library code in tests --- src/lib.rs | 4 ++-- tests/test_edit_list.rs | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aaf629c..65a1ab0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,7 +378,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { .take(CIPHER_SEGMENT_SIZE as u64) .read_to_end(&mut self.buf)?; - //log::debug!("fetch()'s fetched block: {:?}", &self.buf); + log::debug!("fetch()'s fetched block: {:?}", &self.buf); self.is_decrypted = false; @@ -388,7 +388,7 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { fn decrypt(&mut self) -> Result<(), Crypt4GHError> { // Decrypts its buffer if !self.is_decrypted { - //log::debug!("Decrypting block: {:?}", &self.buf); + log::debug!("Decrypting block({:?}): {:?}", self.buf.len(), &self.buf); self.buf = decrypt_block(&self.buf, &self.session_keys)?; self.is_decrypted = true; } diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index 7c7c302..9c33236 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -55,7 +55,7 @@ fn test_send_message_buried() -> TestResult { // log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey ); let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; - + let mut out = vec![]; file.read_to_end(&mut out)?; println!("message: {:?}", out); @@ -72,7 +72,7 @@ fn test_send_message_buried() -> TestResult { range_span, &sender_pubkey, )?; - + // // CommandUnderTest::new() // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) // .arg("decrypt") @@ -82,8 +82,13 @@ fn test_send_message_buried() -> TestResult { // .pipe_out(&temp_file("message.alice")) // .succeeds(); + let mut bob = File::open(&temp_file("message.bob")).unwrap(); + let mut out_copy = vec![]; + bob.read_to_end(&mut out_copy)?; + assert_eq!(buf, out_copy); + // Compare - equal(&temp_file("message.bob"), &temp_file("message.alice")); + // equal(&temp_file("message.bob"), &temp_file("message.alice")); // Cleanup drop(init); From c617d36f7306bf0b9f02b8c7f4cf44ad3d67e0b6 Mon Sep 17 00:00:00 2001 From: Marko Malenic Date: Thu, 26 Oct 2023 16:26:46 +1100 Subject: [PATCH 55/64] fix: add cipher text to encrypt segment --- src/lib.rs | 4 ++-- tests/edit_list_gen.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 65a1ab0..d99f3ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,8 +249,8 @@ pub fn encrypt_header( /// Returns [ nonce + `encrypted_data` ]. pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Result, Crypt4GHError> { let cipher = ChaCha20Poly1305::new(key); - let _ciphertext = cipher.encrypt(&nonce, data); - Ok(vec![nonce.to_vec(), ].concat()) + let ciphertext = cipher.encrypt(&nonce, data).map_err(|err| Crypt4GHError::NoSupportedEncryptionMethod)?; + Ok(vec![nonce.to_vec(), ciphertext].concat()) } /// Reads from the `read_buffer` and writes the decrypted data to `write_buffer`. diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 2aacacb..0408912 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -13,7 +13,7 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p let mut rng = rand::thread_rng(); let parts = input.lines().collect::>(); - let skips = parts.iter().copied().map(|_| rng.gen_range(10_000..100_000)); + let skips = parts.iter().copied().map(|_| 50000); let mut message = Vec::new(); let mut edits = Vec::new(); @@ -78,6 +78,10 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { let nonce = Nonce::from_slice(seed); let key = Key::from_slice(&session_key); + + let a = nonce.to_vec(); + let b = key.to_vec(); + let encrypted_segment = crypt4gh::encrypt_segment(segment, *nonce, &key)?; outfile.write_all(&encrypted_segment)?; } From 344b70d313c3b70802577baa994fdf53787090d0 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Oct 2023 16:31:14 +1100 Subject: [PATCH 56/64] Fix the seeds, scrypt/bcrypt and let SSH parsing for afterwards. Co-authored-by: Marko Malenic --- Cargo.lock | 7 +++++++ Cargo.toml | 3 ++- src/error.rs | 2 ++ src/header.rs | 3 --- src/keys.rs | 38 +++++++------------------------------- src/lib.rs | 17 ++++++++++------- tests/edit_list_gen.rs | 8 +++++--- 7 files changed, 33 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f0ec15..d0cae49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,7 @@ dependencies = [ "crypto_kx", "ctr", "curve25519-dalek", + "ed25519_to_curve25519", "itertools", "lazy_static", "log", @@ -318,6 +319,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "ed25519_to_curve25519" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a976025474add79730a8df2913b114afd39bc53ce5633e045100aceb6d06bb6" + [[package]] name = "either" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 1d1d67e..d897b48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,8 @@ pretty_env_logger = "0.5" thiserror = "1" itertools = "0.11" rand = "0.8" -rand_chacha = "0.3.1" +rand_chacha = "0.3" +ed25519_to_curve25519 = "0.2" curve25519-dalek = "4.0.0" diff --git a/src/error.rs b/src/error.rs index b6072d8..e7c7aa9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,6 +71,8 @@ pub enum Crypt4GHError { InvalidPeerPubPkey, #[error("Invalid paramenters passed to Scrypt")] ScryptParamsError, + #[error("BcryptPBKDF error")] + BcryptPBKDFError, // Reading errors #[error("Unable to read {0} bytes from input (ERROR = {1:?})")] diff --git a/src/header.rs b/src/header.rs index c4af238..3191ebc 100644 --- a/src/header.rs +++ b/src/header.rs @@ -213,9 +213,6 @@ fn decrypt_x25519_chacha20_poly1305( } let nonce = GenericArray::from_slice(&encrypted_part[32..44]); - // - // let nonce = ChaCha20Poly1305::new_from_slice(&encrypted_part[32..44]) - // .map_err(|_| Crypt4GHError::NoNonce)?; let packet_data = &encrypted_part[44..]; let client_sk = SecretKey::try_from(&privkey[0..SecretKey::BYTES]).map_err(|_| Crypt4GHError::BadClientPrivateKey)?; diff --git a/src/keys.rs b/src/keys.rs index 0e19d64..189956e 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -126,11 +126,9 @@ fn derive_key( match alg { "scrypt" => { - // TODO: Review last param of ScryptParams (length of what, exactly?) carefully. - // TODO: Why is the output not used? - // Added "dklen" for now since it seemed fitting, but needs proper review. + // TODO: Is dklen the right key length always? let params = scrypt::Params::new(14, 8, 1, dklen).map_err(|_| Crypt4GHError::ScryptParamsError)?; - let _ = scrypt::scrypt( + scrypt::scrypt( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); @@ -138,10 +136,10 @@ fn derive_key( }), ¶ms, &mut output, - ); + ).map_err(|_| Crypt4GHError::ScryptParamsError)? }, "bcrypt" => { - let _ = bcrypt_pbkdf::bcrypt_pbkdf( + bcrypt_pbkdf::bcrypt_pbkdf( passphrase.as_bytes(), &salt.unwrap_or_else(|| { log::warn!("Using default salt = [0_u8; 8]"); @@ -152,7 +150,7 @@ fn derive_key( 0 }), &mut output, - ); + ).map_err(|_| Crypt4GHError::BcryptPBKDFError)? }, "pbkdf2_hmac_sha256" => unimplemented!(), unsupported_alg => return Err(Crypt4GHError::UnsupportedKdf(unsupported_alg.into())), @@ -519,6 +517,8 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { convert_ed25519_pk_to_curve25519(&pubkey_bytes) } +// TODO: Move all this SSH-key parsing related logic to a higher abstraction crate that does precisely that. +// Alternatively, use: ed25519_to_curve25519::ed25519_sk_to_curve25519(ed25519_sk) from ed25519_to_curve25519 crate fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { if ed25519_pk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); @@ -538,22 +538,8 @@ fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt curve_pk.copy_from_slice(&montgomery_point.to_bytes()); Ok(curve_pk) - - // old impl, libsodium function involved: - // https://doc.libsodium.org/advanced/ed25519-curve25519 - - // let mut curve_pk = [0_u8; 32]; - // let ok = - // unsafe { libsodium_sys::crypto_sign_ed25519_pk_to_curve25519(curve_pk.as_mut_ptr(), ed25519_pk.as_ptr()) == 0 }; - // if ok { - // Ok(curve_pk) - // } - // else { - // Err(Crypt4GHError::ConversionFailed) - // } } -// TODO: Exact copy from the convert_ function above, perhaps the input types need to be a bit more specific than just typeless slices. fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { if ed25519_sk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); @@ -573,16 +559,6 @@ fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt curve_sk.copy_from_slice(&montgomery_point.to_bytes()); Ok(curve_sk) - // let mut curve_sk = [0_u8; 32]; - // let ok = - // unsafe { libsodium_sys::crypto_sign_ed25519_sk_to_curve25519(curve_sk.as_mut_ptr(), ed25519_sk.as_ptr()) == 0 }; - // if ok { - // Ok(curve_sk) - // } - // else { - // Err(Crypt4GHError::ConversionFailed) - // } - //todo!() } /// Generates a random privary key. diff --git a/src/lib.rs b/src/lib.rs index aaf629c..2e1373d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ clippy::redundant_else )] -use rand::{SeedableRng, RngCore}; +use rand::{SeedableRng, RngCore, Rng}; use rand_chacha; use std::collections::HashSet; @@ -149,7 +149,10 @@ pub fn encrypt( log::info!("Streaming content"); let mut segment = [0_u8; SEGMENT_SIZE]; - let seed = &rnd.get_seed(); + + let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); + let mut random_buf = [0u8; 12]; + rnd.fill(&mut random_buf); // The whole file match range_span { @@ -160,7 +163,7 @@ pub fn encrypt( } else if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); //.map_err(|_| Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -169,7 +172,7 @@ pub fn encrypt( break; } else { - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key);//.ok_or(Crypt4GHError::NoKey)?; let encrypted_data = encrypt_segment(&segment, *nonce, &key)?; @@ -183,7 +186,7 @@ pub fn encrypt( // Stop if segment_len >= remaining_length { let (data, _) = segment.split_at(remaining_length); - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -195,7 +198,7 @@ pub fn encrypt( // Not a full segment if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -204,7 +207,7 @@ pub fn encrypt( break; } - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 2aacacb..0f42bd8 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -72,11 +72,13 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p log::debug!("header length: {}", mangled_header_packets.len()); // TODO: Perhaps migrate rest of this file to rnd instead of rng (crypto-safe PRNG?) - let rnd = rand_chacha::ChaCha20Rng::from_entropy(); - let seed = &rnd.get_seed()[..12]; // TODO: Reasonable to cut seed like this? + let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); + let mut random_buf = [0u8; 12]; + rnd.fill(&mut random_buf); + // Output the message for segment in message.chunks(crypt4gh::SEGMENT_SIZE) { - let nonce = Nonce::from_slice(seed); + let nonce = Nonce::from_slice(&random_buf); let key = Key::from_slice(&session_key); let encrypted_segment = crypt4gh::encrypt_segment(segment, *nonce, &key)?; outfile.write_all(&encrypted_segment)?; From bf982d4b731da73d34f6db0e60e835722a824b55 Mon Sep 17 00:00:00 2001 From: Marko Malenic Date: Thu, 26 Oct 2023 16:48:39 +1100 Subject: [PATCH 57/64] fix: correct generic array lengths for key generation --- src/keys.rs | 21 ++++++++++++++------- src/lib.rs | 14 +++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 189956e..3e7763a 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -14,11 +14,12 @@ use base64::Engine; use lazy_static::lazy_static; use rand_chacha; -use rand::{SeedableRng, RngCore}; +use rand::{SeedableRng, RngCore, Rng}; use crypto_kx::{Keypair, SecretKey}; use aes::cipher::{KeyInit, KeyIvInit}; +use aes::cipher::consts::U48; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; @@ -520,6 +521,7 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { // TODO: Move all this SSH-key parsing related logic to a higher abstraction crate that does precisely that. // Alternatively, use: ed25519_to_curve25519::ed25519_sk_to_curve25519(ed25519_sk) from ed25519_to_curve25519 crate fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { + panic!(); if ed25519_pk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); } @@ -541,6 +543,7 @@ fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt } fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { + panic!(); if ed25519_sk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); } @@ -653,27 +656,31 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> rnd.try_fill_bytes(&mut salt).map_err(|_| Crypt4GHError::NoRandomNonce)?; + let mut nonce_bytes = [0u8; 12]; + rnd.fill(&mut nonce_bytes); + let nonce_array = GenericArray::from_slice(&nonce_bytes); + let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; - let nonce = ChaCha20Poly1305::generate_nonce(OsRng); + // let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let key = chacha20poly1305::Key::from_slice(&derived_key); - let salt_ga = GenericArray::from_slice(salt.as_slice()); + // let salt_ga = GenericArray::from_slice(salt.as_slice()); let encrypted_key = ChaCha20Poly1305::new(&key) - .encrypt(salt_ga, skpk) + .encrypt(&nonce_array, skpk) .map_err(|_| Crypt4GHError::BadKey)?; - let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); + let encrypted_key_ga = GenericArray::::from_slice(encrypted_key.as_slice()); log::debug!("Derived Key: {:02x?}", derived_key); log::debug!("Salt: {:02x?}", salt); - log::debug!("Nonce: {:02x?}", nonce); + // log::debug!("Nonce: {:02x?}", nonce); vec![ C4GH_MAGIC_WORD.to_vec(), encode_string_c4gh(Some(kdfname.as_bytes())), encode_string_c4gh(Some(&vec![(rounds as u32).to_be_bytes().to_vec(), salt.to_vec()].concat())), encode_string_c4gh(Some(b"chacha20_poly1305")), - encode_string_c4gh(Some(&vec![nonce, *encrypted_key_ga].concat())), + encode_string_c4gh(Some(&vec![nonce_array.to_vec(), encrypted_key_ga.to_vec()].concat())), match comment { Some(c) => encode_string_c4gh(Some(c.as_bytes())), None => [].to_vec(), diff --git a/src/lib.rs b/src/lib.rs index 7fbf82d..d17aae8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,8 +151,8 @@ pub fn encrypt( let mut segment = [0_u8; SEGMENT_SIZE]; let mut rnd = rand_chacha::ChaCha20Rng::from_entropy(); - let mut random_buf = [0u8; 12]; - rnd.fill(&mut random_buf); + let mut nonce_bytes = [0u8; 12]; + rnd.fill(&mut nonce_bytes); // The whole file match range_span { @@ -163,7 +163,7 @@ pub fn encrypt( } else if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&random_buf); + let nonce = Nonce::from_slice(&nonce_bytes); //.map_err(|_| Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -172,7 +172,7 @@ pub fn encrypt( break; } else { - let nonce = Nonce::from_slice(&random_buf); + let nonce = Nonce::from_slice(&nonce_bytes); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key);//.ok_or(Crypt4GHError::NoKey)?; let encrypted_data = encrypt_segment(&segment, *nonce, &key)?; @@ -186,7 +186,7 @@ pub fn encrypt( // Stop if segment_len >= remaining_length { let (data, _) = segment.split_at(remaining_length); - let nonce = Nonce::from_slice(&random_buf); + let nonce = Nonce::from_slice(&nonce_bytes); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -198,7 +198,7 @@ pub fn encrypt( // Not a full segment if segment_len < SEGMENT_SIZE { let (data, _) = segment.split_at(segment_len); - let nonce = Nonce::from_slice(&random_buf); + let nonce = Nonce::from_slice(&nonce_bytes); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; @@ -207,7 +207,7 @@ pub fn encrypt( break; } - let nonce = Nonce::from_slice(&random_buf); + let nonce = Nonce::from_slice(&nonce_bytes); //.ok_or(Crypt4GHError::NoRandomNonce)?; let key = Key::from_slice(&session_key); //.ok_or(Crypt4GHError::NoKey)?; From 1835ff629da3101a8ebf39d909242a2ad8f272fa Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 30 Oct 2023 10:38:35 +1100 Subject: [PATCH 58/64] Cleanup after the pre-BCN hackathon. Last non passing test is "test_keys" (involves SSH key parsing, which we might not use). --- src/keys.rs | 4 +--- src/lib.rs | 2 +- tests/edit_list_gen.rs | 3 --- tests/test_edit_list.rs | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 3e7763a..613756d 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -22,7 +22,7 @@ use aes::cipher::{KeyInit, KeyIvInit}; use aes::cipher::consts::U48; use chacha20poly1305::aead::Aead; use chacha20poly1305::aead::OsRng; -use chacha20poly1305::{self, ChaCha20Poly1305,AeadCore, consts::U12}; +use chacha20poly1305::{self, ChaCha20Poly1305}; use ctr; @@ -521,7 +521,6 @@ fn ssh_get_public_key(line: &str) -> Result<[u8; 32], Crypt4GHError> { // TODO: Move all this SSH-key parsing related logic to a higher abstraction crate that does precisely that. // Alternatively, use: ed25519_to_curve25519::ed25519_sk_to_curve25519(ed25519_sk) from ed25519_to_curve25519 crate fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { - panic!(); if ed25519_pk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); } @@ -543,7 +542,6 @@ fn convert_ed25519_pk_to_curve25519(ed25519_pk: &[u8]) -> Result<[u8; 32], Crypt } fn convert_ed25519_sk_to_curve25519(ed25519_sk: &[u8]) -> Result<[u8; 32], Crypt4GHError> { - panic!(); if ed25519_sk.len() != 32 { return Err(Crypt4GHError::ConversionFailed); } diff --git a/src/lib.rs b/src/lib.rs index d17aae8..10a2eb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,7 +252,7 @@ pub fn encrypt_header( /// Returns [ nonce + `encrypted_data` ]. pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Result, Crypt4GHError> { let cipher = ChaCha20Poly1305::new(key); - let ciphertext = cipher.encrypt(&nonce, data).map_err(|err| Crypt4GHError::NoSupportedEncryptionMethod)?; + let ciphertext = cipher.encrypt(&nonce, data).map_err(|_| Crypt4GHError::NoSupportedEncryptionMethod)?; Ok(vec![nonce.to_vec(), ciphertext].concat()) } diff --git a/tests/edit_list_gen.rs b/tests/edit_list_gen.rs index 62ea837..4d15bdb 100644 --- a/tests/edit_list_gen.rs +++ b/tests/edit_list_gen.rs @@ -81,9 +81,6 @@ pub fn generate(sk: &str, recipient_pk: &str, input: &str, outfile: &mut File, p let nonce = Nonce::from_slice(&random_buf); let key = Key::from_slice(&session_key); - let a = nonce.to_vec(); - let b = key.to_vec(); - let encrypted_segment = crypt4gh::encrypt_segment(segment, *nonce, &key)?; outfile.write_all(&encrypted_segment)?; } diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index 9c33236..d3f43c9 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -58,7 +58,7 @@ fn test_send_message_buried() -> TestResult { let mut out = vec![]; file.read_to_end(&mut out)?; - println!("message: {:?}", out); + //println!("message: {:?}", out); let mut buf_in = std::io::BufReader::new(&out[..]); From 75329f00cdc5aa726d7efbc59348c94a0a41a27c Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 30 Oct 2023 11:05:23 +1100 Subject: [PATCH 59/64] Clarify errors for failing tests, replace asserts for Crypt4GH errors --- src/error.rs | 6 +++--- src/keys.rs | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/error.rs b/src/error.rs index e7c7aa9..9493c06 100644 --- a/src/error.rs +++ b/src/error.rs @@ -51,10 +51,10 @@ pub enum Crypt4GHError { ConversionFailed, #[error("Unsupported Header Encryption Method: {0}")] BadHeaderEncryptionMethod(u32), - #[error("Unable to encrypt packet: None of the keys were used")] + #[error("Unable to encrypt packet: None of the keys were used in {0}")] UnableToEncryptPacket(String), - #[error("Decryption failed -> Invalid data")] - InvalidData, + #[error("Decryption failed -> Invalid data: {0}")] + InvalidData(String), // Keys errors #[error("Unable to extract public server key")] diff --git a/src/keys.rs b/src/keys.rs index 613756d..4572866 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -330,8 +330,13 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< .get(ciphername) .ok_or_else(|| Crypt4GHError::BadCiphername(ciphername.into()))?; - // Asserts - assert!(data.len() == (ivlen + keylen) as usize); + if ((ivlen + keylen) as usize) != data.len() { + return Err(Crypt4GHError::InvalidData(String::from("IV length and Key length should match total data length"))); + } + + if (private_ciphertext.len() % block_size(ciphername)?) == 0 { + return Err(Crypt4GHError::InvalidData(String::from("Ciphertext does not match target cipher block size"))); + } // Get params let key = &data[..*keylen as usize]; @@ -344,10 +349,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let reader = BufReader::new(private_ciphertext); let mut writer = BufWriter::new(output); - assert!((private_ciphertext.len() % block_size(ciphername)?) == 0); - // Decipher - // TODO: Why is this match not used? Was it used upstream? let _ = match ciphername { "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; From 8b51711178963949c62daa7c07892a6141436d47 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 30 Oct 2023 11:07:06 +1100 Subject: [PATCH 60/64] Clarify errors for failing tests, replace asserts for Crypt4GH errors --- src/keys.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/keys.rs b/src/keys.rs index 4572866..d509311 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -330,6 +330,9 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< .get(ciphername) .ok_or_else(|| Crypt4GHError::BadCiphername(ciphername.into()))?; + log::debug!("IV length and Key length: {} {}", ivlen, keylen); + log::debug!("Private ciphertext length and target ciphername block size: {} {}", private_ciphertext.len(), block_size(ciphername)?); + if ((ivlen + keylen) as usize) != data.len() { return Err(Crypt4GHError::InvalidData(String::from("IV length and Key length should match total data length"))); } From e220b97b2be7a0192800f62142351b6ef426dd7b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 30 Oct 2023 15:01:16 +1100 Subject: [PATCH 61/64] Narrowed down the missing bit(s) to pass the test_keys in decipher()'s match ciphername block. /cc @mmalenic. This is the last bit that needs translated to RustCrypto's primitives, I'm fairly comfident that after writing the 'aes256-ctr' case correctly, we have 100% tests passing: https://github.com/umccr/crypt4gh-rust/blob/8b51711178963949c62daa7c07892a6141436d47/src/keys.rs#L369C2-L371 --- src/keys.rs | 22 ++-- tests/test_edit_list.rs | 5 +- tests/test_keys.rs | 221 +++++++++++++++++++++++----------------- 3 files changed, 140 insertions(+), 108 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index d509311..33f9a4e 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -215,8 +215,8 @@ fn parse_c4gh_private_key( log::debug!("Shared Key: {:02x?}", shared_key); log::debug!("Nonce: {:02x?}", &private_data[0..12]); - let nonce = chacha20poly1305::Nonce::from_slice(&private_data[0..12]);//.ok_or(Crypt4GHError::NoNonce)?; - let key = chacha20poly1305::Key::from_slice(&shared_key);//.ok_or(Crypt4GHError::BadKey)?; + let nonce = chacha20poly1305::Nonce::from_slice(&private_data[0..12]); + let key = chacha20poly1305::Key::from_slice(&shared_key); let encrypted_data = &private_data[12..]; log::debug!("Encrypted data: {:?}", &encrypted_data); @@ -273,7 +273,6 @@ fn parse_ssh_private_key( if kdfoptions_cursor.read_exact(&mut [0_u8]).is_ok() { return Err(Crypt4GHError::BadKey); } - //assert!(kdfoptions_cursor.read_exact(&mut [0_u8]).is_err()); // Log log::debug!("Salt: {:02x?}", salt); @@ -337,7 +336,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< return Err(Crypt4GHError::InvalidData(String::from("IV length and Key length should match total data length"))); } - if (private_ciphertext.len() % block_size(ciphername)?) == 0 { + if (private_ciphertext.len() % block_size(ciphername)?) != 0 { return Err(Crypt4GHError::InvalidData(String::from("Ciphertext does not match target cipher block size"))); } @@ -353,23 +352,23 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let mut writer = BufWriter::new(output); // Decipher - let _ = match ciphername { + match ciphername { "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; let iv_ga = GenericArray::from_slice(iv); let mut cipher = Aes128Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()) + cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes128-ctr")))? }, // "aes192-ctr" => { // let ctr_array = GenericArray::::default(); // type Aes192Ctr = dyn ctr::flavors::CtrFlavor; // todo!() // }, - // "aes256-ctr" => { - // //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! - // todo!() - // }, + "aes256-ctr" => { + //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! + todo!(); + }, // "aes128-cbc" => { // todo!() // }, @@ -381,7 +380,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< // }, "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), - }; + } let a = writer.into_inner().unwrap(); Ok(a) @@ -666,7 +665,6 @@ fn encode_private_key(skpk: &[u8], passphrase: &str, comment: Option) -> let derived_key = derive_key(kdfname, passphrase, Some(salt.clone().to_vec()), Some(rounds), 32)?; // let nonce = ChaCha20Poly1305::generate_nonce(OsRng); let key = chacha20poly1305::Key::from_slice(&derived_key); - // let salt_ga = GenericArray::from_slice(salt.as_slice()); let encrypted_key = ChaCha20Poly1305::new(&key) .encrypt(&nonce_array, skpk) diff --git a/tests/test_edit_list.rs b/tests/test_edit_list.rs index d3f43c9..f0b536c 100644 --- a/tests/test_edit_list.rs +++ b/tests/test_edit_list.rs @@ -58,11 +58,10 @@ fn test_send_message_buried() -> TestResult { let mut out = vec![]; file.read_to_end(&mut out)?; - //println!("message: {:?}", out); let mut buf_in = std::io::BufReader::new(&out[..]); - let mut buf = vec![]; + // Decrypt crypt4gh::decrypt( &keys, @@ -72,6 +71,8 @@ fn test_send_message_buried() -> TestResult { range_span, &sender_pubkey, )?; + + // This CLI-only testing (integration tests) garble stdin/stdout which makes it very impractical to debug (with log::debug/log::info). // // CommandUnderTest::new() // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) diff --git a/tests/test_keys.rs b/tests/test_keys.rs index 0c812b0..3c7d839 100644 --- a/tests/test_keys.rs +++ b/tests/test_keys.rs @@ -1,56 +1,60 @@ mod test_common; +use std::{path::PathBuf, fs::File, io::Read}; + +use crypt4gh::{keys::get_private_key, Keys}; pub use test_common::*; use testresult::TestResult; -#[test] -fn encrypt_ssh_decrypt() -> TestResult { - // Init - let init = Cleanup::new(); - - // Create random file - new_random_file(&temp_file("random.10MB"), 10); - - remove_file(&temp_file(BOB_PUBKEY_SSH)); - remove_file(&temp_file(BOB_SECKEY_SSH)); - - ssh_gen(&temp_file(BOB_SECKEY_SSH), BOB_PASSPHRASE); - - // Encrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", BOB_PASSPHRASE) - .arg("encrypt") - .arg("--sk") - .arg(&strip_prefix(BOB_SECKEY_SSH)) - .arg("--recipient_pk") - .arg(ALICE_PUBKEY) - .pipe_in(&temp_file("random.10MB")) - .pipe_out(&temp_file("random.10MB.c4gh")) - .succeeds(); - - // Decrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - .arg("decrypt") - .arg("--sk") - .arg(ALICE_SECKEY) - .pipe_in(&temp_file("random.10MB.c4gh")) - .pipe_out(&temp_file("random.10MB.received")) - .succeeds(); - - // Compare - equal(&temp_file("random.10MB"), &temp_file("random.10MB.received")); - - // Cleanup - drop(init); - - Ok(()) -} +// #[test] +// fn encrypt_ssh_decrypt() -> TestResult { +// // Init +// let init = Cleanup::new(); + +// // Create random file +// new_random_file(&temp_file("random.10MB"), 10); + +// remove_file(&temp_file(BOB_PUBKEY_SSH)); +// remove_file(&temp_file(BOB_SECKEY_SSH)); + +// ssh_gen(&temp_file(BOB_SECKEY_SSH), BOB_PASSPHRASE); + +// // Encrypt +// CommandUnderTest::new() +// .env("C4GH_PASSPHRASE", BOB_PASSPHRASE) +// .arg("encrypt") +// .arg("--sk") +// .arg(&strip_prefix(BOB_SECKEY_SSH)) +// .arg("--recipient_pk") +// .arg(ALICE_PUBKEY) +// .pipe_in(&temp_file("random.10MB")) +// .pipe_out(&temp_file("random.10MB.c4gh")) +// .succeeds(); + +// // Decrypt +// CommandUnderTest::new() +// .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) +// .arg("decrypt") +// .arg("--sk") +// .arg(ALICE_SECKEY) +// .pipe_in(&temp_file("random.10MB.c4gh")) +// .pipe_out(&temp_file("random.10MB.received")) +// .succeeds(); + +// // Compare +// equal(&temp_file("random.10MB"), &temp_file("random.10MB.received")); + +// // Cleanup +// drop(init); + +// Ok(()) +// } #[test] fn encrypt_decrypt_ssh() -> TestResult{ // Init let init = Cleanup::new(); + pretty_env_logger::init(); // Create random file new_random_file(&temp_file("random.10MB"), 10); @@ -60,59 +64,31 @@ fn encrypt_decrypt_ssh() -> TestResult{ ssh_gen(&temp_file(ALICE_SECKEY_SSH), ALICE_PASSPHRASE); - // Encrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", BOB_PASSPHRASE) - .arg("encrypt") - .arg("--sk") - .arg(BOB_SECKEY) - .arg("--recipient_pk") - .arg(&strip_prefix(ALICE_PUBKEY_SSH)) - .pipe_in(&temp_file("random.10MB")) - .pipe_out(&temp_file("random.10MB.c4gh")) - .succeeds(); - - // Decrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - .arg("decrypt") - .arg("--sk") - .arg(&strip_prefix(ALICE_SECKEY_SSH)) - .pipe_in(&temp_file("random.10MB.c4gh")) - .pipe_out(&temp_file("random.10MB.received")) - .succeeds(); - - // Compare - equal(&temp_file("random.10MB"), &temp_file("random.10MB.received")); - - // Cleanup - drop(init); + let sender_pubkey = None; + let (range_start, range_span) = (0, None); - Ok(()) -} + let seckey = get_private_key(PathBuf::from("tests/tempfiles/alice.sshkey"), Ok(ALICE_PASSPHRASE.to_string()))?; -#[test] -fn encrypt_ssh_decrypt_ssh() -> TestResult { - // Init - let init = Cleanup::new(); + let keys = vec![Keys { + method: 0, + privkey: seckey, + recipient_pubkey: vec![], + }]; - // Create random file - new_random_file(&temp_file("random.10MB"), 10); - - remove_file(&temp_file(ALICE_PUBKEY_SSH)); - remove_file(&temp_file(ALICE_SECKEY_SSH)); - remove_file(&temp_file(BOB_PUBKEY_SSH)); - remove_file(&temp_file(BOB_SECKEY_SSH)); + let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; - ssh_gen(&temp_file(ALICE_SECKEY_SSH), ALICE_PASSPHRASE); - ssh_gen(&temp_file(BOB_SECKEY_SSH), BOB_PASSPHRASE); + let mut out = vec![]; + file.read_to_end(&mut out)?; + let mut buf_in = std::io::BufReader::new(&out[..]); + let mut buf = vec![]; + // Encrypt CommandUnderTest::new() .env("C4GH_PASSPHRASE", BOB_PASSPHRASE) .arg("encrypt") .arg("--sk") - .arg(&strip_prefix(BOB_SECKEY_SSH)) + .arg(BOB_SECKEY) .arg("--recipient_pk") .arg(&strip_prefix(ALICE_PUBKEY_SSH)) .pipe_in(&temp_file("random.10MB")) @@ -120,14 +96,24 @@ fn encrypt_ssh_decrypt_ssh() -> TestResult { .succeeds(); // Decrypt - CommandUnderTest::new() - .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) - .arg("decrypt") - .arg("--sk") - .arg(&strip_prefix(ALICE_SECKEY_SSH)) - .pipe_in(&temp_file("random.10MB.c4gh")) - .pipe_out(&temp_file("random.10MB.received")) - .succeeds(); + crypt4gh::decrypt( + &keys, + &mut buf_in, + &mut buf, + range_start, + range_span, + &sender_pubkey, + )?; + + // Decrypt + // CommandUnderTest::new() + // .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) + // .arg("decrypt") + // .arg("--sk") + // .arg(&strip_prefix(ALICE_SECKEY_SSH)) + // .pipe_in(&temp_file("random.10MB.c4gh")) + // .pipe_out(&temp_file("random.10MB.received")) + // .succeeds(); // Compare equal(&temp_file("random.10MB"), &temp_file("random.10MB.received")); @@ -137,3 +123,50 @@ fn encrypt_ssh_decrypt_ssh() -> TestResult { Ok(()) } + +// #[test] +// fn encrypt_ssh_decrypt_ssh() -> TestResult { +// // Init +// let init = Cleanup::new(); + +// // Create random file +// new_random_file(&temp_file("random.10MB"), 10); + +// remove_file(&temp_file(ALICE_PUBKEY_SSH)); +// remove_file(&temp_file(ALICE_SECKEY_SSH)); +// remove_file(&temp_file(BOB_PUBKEY_SSH)); +// remove_file(&temp_file(BOB_SECKEY_SSH)); + +// ssh_gen(&temp_file(ALICE_SECKEY_SSH), ALICE_PASSPHRASE); +// ssh_gen(&temp_file(BOB_SECKEY_SSH), BOB_PASSPHRASE); + +// // Encrypt +// CommandUnderTest::new() +// .env("C4GH_PASSPHRASE", BOB_PASSPHRASE) +// .arg("encrypt") +// .arg("--sk") +// .arg(&strip_prefix(BOB_SECKEY_SSH)) +// .arg("--recipient_pk") +// .arg(&strip_prefix(ALICE_PUBKEY_SSH)) +// .pipe_in(&temp_file("random.10MB")) +// .pipe_out(&temp_file("random.10MB.c4gh")) +// .succeeds(); + +// // Decrypt +// CommandUnderTest::new() +// .env("C4GH_PASSPHRASE", ALICE_PASSPHRASE) +// .arg("decrypt") +// .arg("--sk") +// .arg(&strip_prefix(ALICE_SECKEY_SSH)) +// .pipe_in(&temp_file("random.10MB.c4gh")) +// .pipe_out(&temp_file("random.10MB.received")) +// .succeeds(); + +// // Compare +// equal(&temp_file("random.10MB"), &temp_file("random.10MB.received")); + +// // Cleanup +// drop(init); + +// Ok(()) +// } From 98d1473755187feed1b216e4be21a642ab266a3f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 30 Oct 2023 15:23:08 +1100 Subject: [PATCH 62/64] Refactored AES256-CTR to RustCrypto, still getting 'Bad ciphername (aes256-ctr)', tests/test_keys.rs:70:18' though... --- src/keys.rs | 37 +++++++++++++++++++------------------ tests/test_keys.rs | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 33f9a4e..3b9054d 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -343,6 +343,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< // Get params let key = &data[..*keylen as usize]; let iv = &data[*keylen as usize..]; + let iv_ga = GenericArray::from_slice(iv); log::debug!("Decryption Key ({}): {:02x?}", key.len(), key); log::debug!("IV ({}): {:02x?}", iv.len(), iv); @@ -351,33 +352,33 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let reader = BufReader::new(private_ciphertext); let mut writer = BufWriter::new(output); + // Decipher match ciphername { "aes128-ctr" => { - type Aes128Ctr = ctr::Ctr128LE; - let iv_ga = GenericArray::from_slice(iv); - + type Aes128Ctr = ctr::Ctr128LE; let mut cipher = Aes128Ctr::new(key.into(), iv_ga); cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes128-ctr")))? }, - // "aes192-ctr" => { - // let ctr_array = GenericArray::::default(); - // type Aes192Ctr = dyn ctr::flavors::CtrFlavor; - // todo!() - // }, + "aes192-ctr" => { + type Aes192Ctr = ctr::Ctr128LE; + let mut cipher = Aes192Ctr::new(key.into(), iv_ga); + cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes192-ctr")))? + }, "aes256-ctr" => { - //type Aes256Ctr = ctr::Ctr256; // Ctr256 not implemented in RustCrypto ctr crate! + type Aes256Ctr = ctr::Ctr128LE; + let mut cipher = Aes256Ctr::new(key.into(), iv_ga); + cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes256-ctr")))? + }, + "aes128-cbc" => { + todo!(); + }, + "aes192-cbc" => { + todo!(); + }, + "aes256-cbc" => { todo!(); }, - // "aes128-cbc" => { - // todo!() - // }, - // "aes192-cbc" => { - // todo!() // Ditto above - // }, - // "aes256-cbc" => { - // todo!() // Ditto above - // }, "3des-cbc" => unimplemented!(), unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), } diff --git a/tests/test_keys.rs b/tests/test_keys.rs index 3c7d839..755f270 100644 --- a/tests/test_keys.rs +++ b/tests/test_keys.rs @@ -75,7 +75,7 @@ fn encrypt_decrypt_ssh() -> TestResult{ recipient_pubkey: vec![], }]; - let mut file = File::open(PathBuf::from("tests/tempfiles/message.bob.c4gh"))?; + let mut file = File::open(PathBuf::from("tests/tempfiles/random.10MB.c4gh"))?; let mut out = vec![]; file.read_to_end(&mut out)?; From dbad146fe142ba7ef08676cc8dbfdad94bd93929 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 31 Oct 2023 13:24:17 +1100 Subject: [PATCH 63/64] Pinpoint and panic on the current location at fault /cc @mmalenic --- src/keys.rs | 6 ++++-- src/lib.rs | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 3b9054d..48b06ba 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -352,6 +352,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< let reader = BufReader::new(private_ciphertext); let mut writer = BufWriter::new(output); + log::debug!("Input ciphername is: {}", ciphername); // Decipher match ciphername { @@ -367,8 +368,9 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< }, "aes256-ctr" => { type Aes256Ctr = ctr::Ctr128LE; - let mut cipher = Aes256Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes256-ctr")))? + let mut _cipher = Aes256Ctr::new(key.into(), iv_ga); + panic!("Failing here on aes256-ctr!!! Probably apply_keystream_b2b is seeing wrong args/use?"); + _cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes256-ctr")))? }, "aes128-cbc" => { todo!(); diff --git a/src/lib.rs b/src/lib.rs index 10a2eb7..5d5adfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -469,7 +469,6 @@ impl<'a, W: Write> DecryptedBuffer<'a, W> { } log::debug!("Finished reading"); - log::debug!(""); Ok(size) } From bdbd14df5f783521de2e1e807b84bf10b5b12816 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 31 Oct 2023 13:45:24 +1100 Subject: [PATCH 64/64] Fixing reader, narrowing the error down to a writer I/O issue... --- src/keys.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 48b06ba..29838cd 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -348,29 +348,32 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< log::debug!("Decryption Key ({}): {:02x?}", key.len(), key); log::debug!("IV ({}): {:02x?}", iv.len(), iv); + let mut reader: Vec = vec![]; let output = vec![0_u8; private_ciphertext.len()]; - let reader = BufReader::new(private_ciphertext); + + BufReader::new(private_ciphertext).read_to_end(&mut reader)?; let mut writer = BufWriter::new(output); - log::debug!("Input ciphername is: {}", ciphername); + // log::debug!("Input ciphername is: {}", ciphername); + // log::debug!("Private ciphertext is: {:#?}", private_ciphertext); // Decipher match ciphername { "aes128-ctr" => { type Aes128Ctr = ctr::Ctr128LE; let mut cipher = Aes128Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes128-ctr")))? + cipher.apply_keystream_b2b(&reader, writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes128-ctr")))? }, "aes192-ctr" => { type Aes192Ctr = ctr::Ctr128LE; let mut cipher = Aes192Ctr::new(key.into(), iv_ga); - cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes192-ctr")))? + cipher.apply_keystream_b2b(&reader, writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes192-ctr")))? }, "aes256-ctr" => { type Aes256Ctr = ctr::Ctr128LE; - let mut _cipher = Aes256Ctr::new(key.into(), iv_ga); - panic!("Failing here on aes256-ctr!!! Probably apply_keystream_b2b is seeing wrong args/use?"); - _cipher.apply_keystream_b2b(reader.buffer(), writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes256-ctr")))? + let mut cipher = Aes256Ctr::new(key.into(), iv_ga); + log::debug!("Reading reader: {:#?}", &reader); + cipher.apply_keystream_b2b(&reader, &mut writer.get_mut()).map_err(|_| Crypt4GHError::BadCiphername(String::from("aes256-ctr")))? }, "aes128-cbc" => { todo!(); @@ -385,8 +388,7 @@ fn decipher(ciphername: &str, data: &[u8], private_ciphertext: &[u8]) -> Result< unknown_cipher => return Err(Crypt4GHError::BadCiphername(unknown_cipher.into())), } - let a = writer.into_inner().unwrap(); - Ok(a) + writer.into_inner().map_err(|err| Crypt4GHError::IoError(err.into_error())) } fn block_size(ciphername: &str) -> Result {