Skip to content

Commit

Permalink
Merge pull request #3 from umccr/fix_rust_crypto_et_al
Browse files Browse the repository at this point in the history
Sodiumoxide migration and future plans for this crate
  • Loading branch information
brainstorm authored Jan 31, 2024
2 parents ce0c336 + bdbd14d commit 2d41a17
Show file tree
Hide file tree
Showing 16 changed files with 1,314 additions and 714 deletions.
748 changes: 532 additions & 216 deletions Cargo.lock

Large diffs are not rendered by default.

36 changes: 23 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,33 @@ 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"
base64 = "0.21"
lazy_static = "1.4"
chacha20poly1305 = "0.10"
crypto_kx = { version = "0.2.1" }
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"] }
log = "0.4"
pretty_env_logger = "0.4"
thiserror = "1.0"
libsodium-sys = "0.2.7"
itertools = "0.10"
pretty_env_logger = "0.5"
thiserror = "1"
itertools = "0.11"
rand = "0.8"
rand_chacha = "0.3"
ed25519_to_curve25519 = "0.2"

curve25519-dalek = "4.0.0"

[dev-dependencies]
rand = "0.8"
testresult = "0.3"

[profile.release]
lto = true
Expand Down
43 changes: 23 additions & 20 deletions src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ 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};
use crypt4gh::{self, keys, Keys};
use itertools::Itertools;
use regex::Regex;
use rpassword::prompt_password;

Expand Down Expand Up @@ -74,8 +73,8 @@ fn retrieve_private_key(sk: Option<PathBuf>, generate: bool) -> Result<Vec<u8>,
let seckey_path = sk;

if generate && seckey_path.is_none() {
let skey = keys::generate_private_key();
log::info!("Generating Private Key: {:02x?}", skey.iter().format(""));
let skey = keys::generate_private_key()?;
log::info!("Generating Private Key: {:02x?}", skey);
Ok(skey)
}
else {
Expand All @@ -84,18 +83,18 @@ fn retrieve_private_key(sk: Option<PathBuf>, generate: bool) -> Result<Vec<u8>,
return Err(Crypt4GHError::ReadSecretKeyFileError(path));
}

let callback: Box<dyn Fn() -> Result<String, Crypt4GHError>> = match std::env::var(PASSPHRASE) {
let passphrase: Result<String, Crypt4GHError> = 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, callback)
get_private_key(path.to_owned(), passphrase)
}
}

Expand All @@ -111,7 +110,7 @@ fn build_recipients(recipient_pk: &[PathBuf], sk: &[u8]) -> Result<HashSet<Keys>
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()
Expand All @@ -138,7 +137,7 @@ fn run_encrypt(sk: Option<PathBuf>, recipient_pk: &[PathBuf], range: Option<Stri

fn run_decrypt(sk: Option<PathBuf>, sender_pk: Option<PathBuf>, range: Option<String>) -> 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,
};

Expand All @@ -152,6 +151,8 @@ fn run_decrypt(sk: Option<PathBuf>, sender_pk: Option<PathBuf>, range: Option<St
recipient_pubkey: vec![],
}];

//log::debug!("run_decrypt()'s parameters: {:#?}, {}, {:#?}, {:#?}", &keys, range_start, range_span, &sender_pubkey );

crypt4gh::decrypt(
&keys,
&mut io::stdin(),
Expand Down Expand Up @@ -193,13 +194,13 @@ fn run_reencrypt(sk: Option<PathBuf>, 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<String>, nocrypt: bool, force: bool) -> Result<(), Crypt4GHError> {
fn run_keygen(sk: PathBuf, pk: PathBuf, comment: Option<String>, 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.to_owned(), pubkey.to_owned()] {
// If key exists and it is a file
if key.is_file() {
// Force overwrite?
Expand All @@ -221,17 +222,19 @@ fn run_keygen(sk: &Path, pk: &Path, comment: Option<String>, nocrypt: bool, forc
// 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> {
Expand Down Expand Up @@ -263,7 +266,7 @@ fn run() -> Result<(), Crypt4GHError> {
comment,
nocrypt,
force,
} => run_keygen(&sk, &pk, comment, nocrypt, force)?,
} => run_keygen(sk, pk, comment, nocrypt, force)?,
}

Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum Command {
sk: Option<PathBuf>,

/// Recipient's Curve25519-based Public key
#[clap(long = "recipient_pk", multiple_values = true)]
#[clap(long = "recipient_pk", num_args = 1..)]
recipient_pk: Vec<PathBuf>,

/// Byte-range either as <start-end> or just <start> (Start included, End excluded)
Expand Down Expand Up @@ -49,7 +49,7 @@ pub enum Command {
sk: Option<PathBuf>,

/// Recipient's Curve25519-based Public key
#[clap(long = "recipient_pk", multiple_values = true)]
#[clap(long = "recipient_pk", num_args = 1..)]
recipient_pk: Vec<PathBuf>,

/// Keep only header packets that you can decrypt
Expand Down
34 changes: 24 additions & 10 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::error::Error;
use std::path::PathBuf;

use crypto::symmetriccipher::SymmetricCipherError;
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -13,26 +12,31 @@ pub enum Crypt4GHError {
InvalidRangeSpan(Option<usize>),
#[error("The edit list is empty")]
EmptyEditList,

// Sodiumoxide errors
#[error("Unable to create random nonce")]
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")]
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 PEM file length. The file ({0:?}) is not 3 lines long")]
InvalidPEMFormatLength(PathBuf),
#[error("Invalid PEM file header or footer: -----BEGIN or -----END")]
InvalidPEMHeaderOrFooter,
#[error("Invalid SSH key format")]
InvalidSSHKey,
#[error("Unable to wrap nonce")]
UnableToWrapNonce,
#[error("Could not decrypt that block")]
UnableToDecryptBlock,
#[error("Could not decrypt block: {0:?}, {1:?}")]
UnableToDecryptBlock(Vec<u8>, String),
#[error("Unable to decode with base64 the key (ERROR = {0:?})")]
BadBase64Error(Box<dyn Error + Send + Sync>),
#[error("Unable to decode kdfname")]
Expand All @@ -47,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")]
UnableToEncryptPacket,
#[error("Decryption failed -> Invalid data")]
InvalidData,
#[error("Unable to encrypt packet: None of the keys were used in {0}")]
UnableToEncryptPacket(String),
#[error("Decryption failed -> Invalid data: {0}")]
InvalidData(String),

// Keys errors
#[error("Unable to extract public server key")]
Expand All @@ -65,10 +69,16 @@ pub enum Crypt4GHError {
BadSharedKey,
#[error("Invalid Peer's Public Key")]
InvalidPeerPubPkey,
#[error("Invalid paramenters passed to Scrypt")]
ScryptParamsError,
#[error("BcryptPBKDF error")]
BcryptPBKDFError,

// Reading errors
#[error("Unable to read {0} bytes from input (ERROR = {1:?})")]
NotEnoughInput(usize, Box<dyn Error + Send + Sync>),
#[error("You shouldn't skip 0 bytes")]
SkipZeroBytes,
#[error("Unable to read header info (ERROR = {0:?})")]
ReadHeaderError(Box<dyn Error + Send + Sync>),
#[error("Unable to read header packet length (ERROR = {0:?})")]
Expand All @@ -95,6 +105,10 @@ pub enum Crypt4GHError {
ReadCheckNumber2Error,
#[error("Unable to read magic word from private key (ERROR = {0:?})")]
ReadMagicWord(Box<dyn Error + Send + Sync>),
#[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}")]
Expand Down
Loading

0 comments on commit 2d41a17

Please sign in to comment.