From 2a7d28372ef28b01ec7f9a73a922408505f23739 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 22 Oct 2023 22:31:26 +0800 Subject: [PATCH] feat: v0.5.1, envelop supports ChaCha20/Poly1305 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_decrypt.rs | 40 ++++++++++++++++++++---------------- src/cmd_encrypt.rs | 51 ++++++++++++++++++++++++++++------------------ src/consts.rs | 3 +++ 5 files changed, 58 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1e87e3..3a89901 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "tiny-encrypt" -version = "0.5.0" +version = "0.5.1" dependencies = [ "aes-gcm-stream", "base64", diff --git a/Cargo.toml b/Cargo.toml index 1566a0f..b493816 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tiny-encrypt" -version = "0.5.0" +version = "0.5.1" edition = "2021" license = "MIT" description = "A simple and tiny file encrypt tool" diff --git a/src/cmd_decrypt.rs b/src/cmd_decrypt.rs index b6d8a53..9858314 100644 --- a/src/cmd_decrypt.rs +++ b/src/cmd_decrypt.rs @@ -19,7 +19,9 @@ use crate::{consts, crypto_simple, util, util_enc_file, util_envelop, util_file, use crate::compress::GzStreamDecoder; use crate::config::TinyEncryptConfig; use crate::consts::{ - DATE_TIME_FORMAT, ENC_AES256_GCM_P256, ENC_AES256_GCM_P384, ENC_AES256_GCM_X25519, + DATE_TIME_FORMAT, + ENC_AES256_GCM_P256, ENC_AES256_GCM_P384, ENC_AES256_GCM_X25519, + ENC_CHACHA20_POLY1305_P256, ENC_CHACHA20_POLY1305_P384, ENC_CHACHA20_POLY1305_X25519, SALT_COMMENT, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT, }; use crate::crypto_cryptor::Cryptor; @@ -108,8 +110,8 @@ pub fn decrypt_single(config: &Option, util_enc_file::read_tiny_encrypt_meta_and_normalize(&mut file_in), "Read file: {}, failed: {}", &path_display); debugging!("Found meta: {}", serde_json::to_string_pretty(&meta).unwrap()); - let encryption_algorithm = meta.encryption_algorithm - .as_ref().map(String::as_str).unwrap_or(consts::TINY_ENC_AES_GCM); + let encryption_algorithm = meta.encryption_algorithm.as_deref() + .unwrap_or(consts::TINY_ENC_AES_GCM); let cryptor = Cryptor::from(encryption_algorithm)?; let do_skip_file_out = cmd_decrypt.skip_decrypt_file || cmd_decrypt.direct_print || cmd_decrypt.digest_file; @@ -181,7 +183,7 @@ pub fn decrypt_single(config: &Option, let encrypt_duration = start.elapsed(); debugging!("Inner decrypt file{}: {} elapsed: {} ms", compressed_desc, path_display, encrypt_duration.as_millis()); - if do_skip_file_out & &cmd_decrypt.remove_file { util::remove_file_with_msg(path); } + if do_skip_file_out && cmd_decrypt.remove_file { util::remove_file_with_msg(path); } Ok(meta.file_length) } @@ -270,8 +272,7 @@ fn try_decrypt_key(config: &Option, match envelop.r#type { TinyEncryptEnvelopType::Pgp => try_decrypt_key_pgp(envelop, pin), TinyEncryptEnvelopType::PgpX25519 => try_decrypt_key_ecdh_pgp_x25519(envelop, pin), - TinyEncryptEnvelopType::Ecdh => try_decrypt_key_ecdh(config, envelop, pin, ENC_AES256_GCM_P256, slot), - TinyEncryptEnvelopType::EcdhP384 => try_decrypt_key_ecdh(config, envelop, pin, ENC_AES256_GCM_P384, slot), + TinyEncryptEnvelopType::Ecdh | TinyEncryptEnvelopType::EcdhP384 => try_decrypt_key_ecdh(config, envelop, pin, slot), unknown_type => simple_error!("Unknown or unsupported type: {}", unknown_type.get_name()), } } @@ -279,12 +280,15 @@ fn try_decrypt_key(config: &Option, fn try_decrypt_key_ecdh(config: &Option, envelop: &TinyEncryptEnvelop, pin: &Option, - expected_enc_type: &str, slot: &Option) -> XResult> { let wrap_key = WrapKey::parse(&envelop.encrypted_key)?; - if wrap_key.header.enc.as_str() != expected_enc_type { - return simple_error!("Unsupported header, requires: {} actual: {}", expected_enc_type, &wrap_key.header.enc); - } + let (cryptor, algo_id) = match wrap_key.header.enc.as_str() { + ENC_AES256_GCM_P256 => (Cryptor::Aes256Gcm, AlgorithmId::EccP256), + ENC_AES256_GCM_P384 => (Cryptor::Aes256Gcm, AlgorithmId::EccP384), + ENC_CHACHA20_POLY1305_P256 => (Cryptor::ChaCha20Poly1305, AlgorithmId::EccP256), + ENC_CHACHA20_POLY1305_P384 => (Cryptor::ChaCha20Poly1305, AlgorithmId::EccP384), + _ => return simple_error!("Unsupported header enc: {}", &wrap_key.header.enc), + }; let e_pub_key = &wrap_key.header.e_pub_key; let e_pub_key_bytes = opt_result!(util::decode_base64_url_no_pad(e_pub_key), "Invalid envelop: {}"); let (_, subject_public_key_info) = opt_result!( @@ -297,9 +301,7 @@ fn try_decrypt_key_ecdh(config: &Option, let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); let slot_id = util_piv::get_slot_id(&slot)?; opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); - let algo_id = iff!( - expected_enc_type == ENC_AES256_GCM_P256, AlgorithmId::EccP256, AlgorithmId::EccP384 - ); + let shared_secret = opt_result!(decrypt_data( &mut yk, epk_bytes, @@ -308,7 +310,7 @@ fn try_decrypt_key_ecdh(config: &Option, ), "Decrypt via PIV card failed: {}"); let key = util::simple_kdf(shared_secret.as_slice()); let decrypted_key = crypto_simple::decrypt( - Cryptor::Aes256Gcm, &key, &wrap_key.nonce, &wrap_key.encrypted_data)?; + cryptor, &key, &wrap_key.nonce, &wrap_key.encrypted_data)?; util::zeroize(key); util::zeroize(shared_secret); Ok(decrypted_key) @@ -316,9 +318,11 @@ fn try_decrypt_key_ecdh(config: &Option, fn try_decrypt_key_ecdh_pgp_x25519(envelop: &TinyEncryptEnvelop, pin: &Option) -> XResult> { let wrap_key = WrapKey::parse(&envelop.encrypted_key)?; - if wrap_key.header.enc.as_str() != ENC_AES256_GCM_X25519 { - return simple_error!("Unsupported header, requires: {} actual: {}", ENC_AES256_GCM_X25519, &wrap_key.header.enc); - } + let cryptor = match wrap_key.header.enc.as_str() { + ENC_AES256_GCM_X25519 => Cryptor::Aes256Gcm, + ENC_CHACHA20_POLY1305_X25519 => Cryptor::Aes256Gcm, + _ => return simple_error!("Unsupported header enc: {}", &wrap_key.header.enc), + }; let e_pub_key = &wrap_key.header.e_pub_key; let epk_bytes = opt_result!(util::decode_base64_url_no_pad(e_pub_key), "Invalid envelop: {}"); @@ -331,7 +335,7 @@ fn try_decrypt_key_ecdh_pgp_x25519(envelop: &TinyEncryptEnvelop, pin: &Option Cryptor::Aes256Gcm, @@ -136,7 +137,7 @@ fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_en util::require_file_not_exists(path_out.as_str())?; let (key, nonce) = util::make_key256_and_nonce(); - let envelops = encrypt_envelops(&key.0, envelops)?; + let envelops = encrypt_envelops(cryptor, &key.0, envelops)?; let encrypted_comment = match &cmd_encrypt.encrypted_comment { None => None, @@ -276,7 +277,7 @@ fn encrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write, cr Ok(total_len) } -fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResult> { +fn encrypt_envelops(cryptor: Cryptor, key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResult> { let mut encrypted_envelops = vec![]; for envelop in envelops { match envelop.r#type { @@ -284,13 +285,13 @@ fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResu encrypted_envelops.push(encrypt_envelop_pgp(key, envelop)?); } TinyEncryptEnvelopType::PgpX25519 => { - encrypted_envelops.push(encrypt_envelop_ecdh_x25519(key, envelop)?); + encrypted_envelops.push(encrypt_envelop_ecdh_x25519(cryptor, key, envelop)?); } TinyEncryptEnvelopType::Ecdh => { - encrypted_envelops.push(encrypt_envelop_ecdh(key, envelop)?); + encrypted_envelops.push(encrypt_envelop_ecdh(cryptor, key, envelop)?); } TinyEncryptEnvelopType::EcdhP384 => { - encrypted_envelops.push(encrypt_envelop_ecdh_p384(key, envelop)?); + encrypted_envelops.push(encrypt_envelop_ecdh_p384(cryptor, key, envelop)?); } _ => return simple_error!("Not supported type: {:?}", envelop.r#type), } @@ -298,28 +299,38 @@ fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResu Ok(encrypted_envelops) } -fn encrypt_envelop_ecdh(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { +fn encrypt_envelop_ecdh(cryptor: Cryptor, key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { let public_key_point_hex = &envelop.public_part; let (shared_secret, ephemeral_spki) = util_p256::compute_shared_secret(public_key_point_hex)?; - - encrypt_envelop_shared_secret(key, &shared_secret, &ephemeral_spki, ENC_AES256_GCM_P256, envelop) + let enc_type = match cryptor { + Cryptor::Aes256Gcm => ENC_AES256_GCM_P256, + Cryptor::ChaCha20Poly1305 => ENC_CHACHA20_POLY1305_P256, + }; + encrypt_envelop_shared_secret(cryptor, key, &shared_secret, &ephemeral_spki, enc_type, envelop) } -fn encrypt_envelop_ecdh_p384(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { +fn encrypt_envelop_ecdh_p384(cryptor: Cryptor, key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { let public_key_point_hex = &envelop.public_part; let (shared_secret, ephemeral_spki) = util_p384::compute_p384_shared_secret(public_key_point_hex)?; - - encrypt_envelop_shared_secret(key, &shared_secret, &ephemeral_spki, ENC_AES256_GCM_P384, envelop) + let enc_type = match cryptor { + Cryptor::Aes256Gcm => ENC_AES256_GCM_P384, + Cryptor::ChaCha20Poly1305 => ENC_CHACHA20_POLY1305_P384, + }; + encrypt_envelop_shared_secret(cryptor, key, &shared_secret, &ephemeral_spki, enc_type, envelop) } -fn encrypt_envelop_ecdh_x25519(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { +fn encrypt_envelop_ecdh_x25519(cryptor: Cryptor, key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { let public_key_point_hex = &envelop.public_part; let (shared_secret, ephemeral_spki) = util_x25519::compute_x25519_shared_secret(public_key_point_hex)?; - - encrypt_envelop_shared_secret(key, &shared_secret, &ephemeral_spki, ENC_AES256_GCM_X25519, envelop) + let enc_type = match cryptor { + Cryptor::Aes256Gcm => ENC_AES256_GCM_X25519, + Cryptor::ChaCha20Poly1305 => ENC_CHACHA20_POLY1305_X25519, + }; + encrypt_envelop_shared_secret(cryptor, key, &shared_secret, &ephemeral_spki, enc_type, envelop) } -fn encrypt_envelop_shared_secret(key: &[u8], +fn encrypt_envelop_shared_secret(cryptor: Cryptor, + key: &[u8], shared_secret: &[u8], ephemeral_spki: &[u8], enc_type: &str, @@ -328,7 +339,7 @@ fn encrypt_envelop_shared_secret(key: &[u8], let (_, nonce) = util::make_key256_and_nonce(); let encrypted_key = crypto_simple::encrypt( - Cryptor::Aes256Gcm, &shared_key, &nonce.0, key)?; + cryptor, &shared_key, &nonce.0, key)?; let wrap_key = WrapKey { header: WrapKeyHeader { @@ -350,7 +361,7 @@ fn encrypt_envelop_shared_secret(key: &[u8], } fn encrypt_envelop_pgp(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult { - let pgp_public_key = opt_result!(parse_spki(&envelop.public_part), "Parse PGP public key failed: {}"); + let pgp_public_key = opt_result!(crypto_rsa::parse_spki(&envelop.public_part), "Parse PGP public key failed: {}"); let mut rng = rand::thread_rng(); let encrypted_key = opt_result!(pgp_public_key.encrypt(&mut rng, Pkcs1v15Encrypt, key), "PGP public key encrypt failed: {}"); Ok(TinyEncryptEnvelop { diff --git a/src/consts.rs b/src/consts.rs index eee3fa6..105ecda 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -2,6 +2,9 @@ pub const ENC_AES256_GCM_P256: &str = "aes256-gcm-p256"; pub const ENC_AES256_GCM_P384: &str = "aes256-gcm-p384"; pub const ENC_AES256_GCM_X25519: &str = "aes256-gcm-x25519"; +pub const ENC_CHACHA20_POLY1305_P256: &str = "chacha20-poly1305-p256"; +pub const ENC_CHACHA20_POLY1305_P384: &str = "chacha20-poly1305-p384"; +pub const ENC_CHACHA20_POLY1305_X25519: &str = "chacha20-poly1305-x25519"; // Extend and config file pub const TINY_ENC_FILE_EXT: &str = ".tinyenc";