v0.2-x25519 #1
81
Cargo.lock
generated
81
Cargo.lock
generated
@@ -390,6 +390,33 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "4.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"curve25519-dalek-derive",
|
||||||
|
"fiat-crypto",
|
||||||
|
"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.38",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@@ -574,6 +601,12 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fiat-crypto"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flagset"
|
name = "flagset"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@@ -986,9 +1019,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.8"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db"
|
checksum = "45786cec4d5e54a224b15cb9f06751883103a27c19c93eda09b0b4f5f08fefac"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
@@ -1114,9 +1147,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.16"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"libm",
|
"libm",
|
||||||
@@ -1339,6 +1372,12 @@ version = "0.3.27"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platforms"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -1579,6 +1618,15 @@ version = "0.1.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusticata-macros"
|
name = "rusticata-macros"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
@@ -1709,6 +1757,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.188"
|
version = "1.0.188"
|
||||||
@@ -1866,9 +1920,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.4.1"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
@@ -2009,7 +2063,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-stream",
|
"aes-gcm-stream",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -2029,6 +2083,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sha256",
|
"sha256",
|
||||||
"simpledateformat",
|
"simpledateformat",
|
||||||
|
"x25519-dalek",
|
||||||
"x509-parser",
|
"x509-parser",
|
||||||
"yubikey",
|
"yubikey",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -2428,6 +2483,18 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x25519-dalek"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"rand_core",
|
||||||
|
"serde",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x509-cert"
|
name = "x509-cert"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "A simple and tiny file encrypt tool"
|
description = "A simple and tiny file encrypt tool"
|
||||||
@@ -26,6 +26,7 @@ serde = { version = "1.0.152", features = ["derive"] }
|
|||||||
serde_json = "1.0.93"
|
serde_json = "1.0.93"
|
||||||
sha256 = "1.4.0"
|
sha256 = "1.4.0"
|
||||||
simpledateformat = "0.1.4"
|
simpledateformat = "0.1.4"
|
||||||
|
x25519-dalek = "2.0.0"
|
||||||
x509-parser = "0.15.1"
|
x509-parser = "0.15.1"
|
||||||
yubikey = { version = "0.8.0", features = ["untested"] }
|
yubikey = { version = "0.8.0", features = ["untested"] }
|
||||||
zeroize = "1.6.0"
|
zeroize = "1.6.0"
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use std::str::FromStr;
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
|
use openpgp_card::{OpenPgp, OpenPgpTransaction};
|
||||||
use openpgp_card::crypto_data::Cryptogram;
|
use openpgp_card::crypto_data::Cryptogram;
|
||||||
use openpgp_card::OpenPgp;
|
|
||||||
use rust_util::{debugging, failure, iff, information, opt_result, simple_error, success, util_msg, util_term, warning, XResult};
|
use rust_util::{debugging, failure, iff, information, opt_result, simple_error, success, util_msg, util_term, warning, XResult};
|
||||||
use x509_parser::prelude::FromDer;
|
use x509_parser::prelude::FromDer;
|
||||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||||
@@ -19,7 +19,7 @@ use crate::{card, file, util};
|
|||||||
use crate::compress::GzStreamDecoder;
|
use crate::compress::GzStreamDecoder;
|
||||||
use crate::crypto_aes::aes_gcm_decrypt;
|
use crate::crypto_aes::aes_gcm_decrypt;
|
||||||
use crate::spec::{TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta};
|
use crate::spec::{TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta};
|
||||||
use crate::util::{ENC_AES256_GCM_P256, TINY_ENC_FILE_EXT};
|
use crate::util::{ENC_AES256_GCM_P256, ENC_AES256_GCM_X25519, TINY_ENC_FILE_EXT};
|
||||||
use crate::wrap_key::WrapKey;
|
use crate::wrap_key::WrapKey;
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
@@ -157,6 +157,7 @@ fn decrypt_file(file_in: &mut File, file_out: &mut File, key: &[u8], nonce: &[u8
|
|||||||
fn try_decrypt_key(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Option<String>) -> XResult<Vec<u8>> {
|
fn try_decrypt_key(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Option<String>) -> XResult<Vec<u8>> {
|
||||||
match envelop.r#type {
|
match envelop.r#type {
|
||||||
TinyEncryptEnvelopType::Pgp => try_decrypt_key_pgp(envelop, pin),
|
TinyEncryptEnvelopType::Pgp => try_decrypt_key_pgp(envelop, pin),
|
||||||
|
TinyEncryptEnvelopType::PgpX25519 => try_decrypt_key_ecdh_pgp_x25519(envelop, pin),
|
||||||
TinyEncryptEnvelopType::Ecdh => try_decrypt_key_ecdh(envelop, pin, slot),
|
TinyEncryptEnvelopType::Ecdh => try_decrypt_key_ecdh(envelop, pin, slot),
|
||||||
unknown_type => {
|
unknown_type => {
|
||||||
return simple_error!("Unknown or not supported type: {}", unknown_type.get_name());
|
return simple_error!("Unknown or not supported type: {}", unknown_type.get_name());
|
||||||
@@ -167,7 +168,7 @@ fn try_decrypt_key(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Op
|
|||||||
fn try_decrypt_key_ecdh(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Option<String>) -> XResult<Vec<u8>> {
|
fn try_decrypt_key_ecdh(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Option<String>) -> XResult<Vec<u8>> {
|
||||||
let wrap_key = WrapKey::parse(&envelop.encrypted_key)?;
|
let wrap_key = WrapKey::parse(&envelop.encrypted_key)?;
|
||||||
if wrap_key.header.enc.as_str() != ENC_AES256_GCM_P256 {
|
if wrap_key.header.enc.as_str() != ENC_AES256_GCM_P256 {
|
||||||
return simple_error!("Unsupported header enc.");
|
return simple_error!("Unsupported header requires: {}, actual: {}", ENC_AES256_GCM_P256, &wrap_key.header.enc);
|
||||||
}
|
}
|
||||||
let e_pub_key = &wrap_key.header.e_pub_key;
|
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 e_pub_key_bytes = opt_result!(util::decode_base64_url_no_pad(e_pub_key), "Invalid envelop: {}");
|
||||||
@@ -181,35 +182,46 @@ fn try_decrypt_key_ecdh(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot
|
|||||||
let retired_slot_id = opt_result!(RetiredSlotId::from_str(&slot), "Slot not found: {}");
|
let retired_slot_id = opt_result!(RetiredSlotId::from_str(&slot), "Slot not found: {}");
|
||||||
let slot_id = SlotId::Retired(retired_slot_id);
|
let slot_id = SlotId::Retired(retired_slot_id);
|
||||||
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
|
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
|
||||||
let decrypted_shared_secret = opt_result!(decrypt_data(
|
let shared_secret = opt_result!(decrypt_data(
|
||||||
&mut yk,
|
&mut yk,
|
||||||
&epk_bytes,
|
&epk_bytes,
|
||||||
AlgorithmId::EccP256,
|
AlgorithmId::EccP256,
|
||||||
slot_id,
|
slot_id,
|
||||||
), "Decrypt via PIV card failed: {}");
|
), "Decrypt via PIV card failed: {}");
|
||||||
let key = util::simple_kdf(decrypted_shared_secret.as_slice());
|
let key = util::simple_kdf(shared_secret.as_slice());
|
||||||
let decrypted_key = aes_gcm_decrypt(&key, &wrap_key.nonce, &wrap_key.encrypted_data)?;
|
let decrypted_key = aes_gcm_decrypt(&key, &wrap_key.nonce, &wrap_key.encrypted_data)?;
|
||||||
util::zeroize(key);
|
util::zeroize(key);
|
||||||
|
util::zeroize(shared_secret);
|
||||||
|
Ok(decrypted_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_decrypt_key_ecdh_pgp_x25519(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
|
||||||
|
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 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: {}");
|
||||||
|
|
||||||
|
let mut pgp = get_openpgp()?;
|
||||||
|
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
|
||||||
|
|
||||||
|
read_and_verify_openpgp_pin(&mut trans, pin)?;
|
||||||
|
|
||||||
|
let shared_secret = trans.decipher(Cryptogram::ECDH(&epk_bytes))?;
|
||||||
|
|
||||||
|
let key = util::simple_kdf(shared_secret.as_slice());
|
||||||
|
let decrypted_key = aes_gcm_decrypt(&key, &wrap_key.nonce, &wrap_key.encrypted_data)?;
|
||||||
|
util::zeroize(key);
|
||||||
|
util::zeroize(shared_secret);
|
||||||
Ok(decrypted_key)
|
Ok(decrypted_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
|
fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
|
||||||
let card = match card::get_card() {
|
let mut pgp = get_openpgp()?;
|
||||||
Err(e) => {
|
|
||||||
failure!("Get PGP card failed: {}", e);
|
|
||||||
return simple_error!("Get card failed: {}", e);
|
|
||||||
}
|
|
||||||
Ok(card) => card
|
|
||||||
};
|
|
||||||
let mut pgp = OpenPgp::new(card);
|
|
||||||
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
|
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
|
||||||
|
|
||||||
let pin = read_pin(pin);
|
read_and_verify_openpgp_pin(&mut trans, pin)?;
|
||||||
if let Err(e) = trans.verify_pw1_user(pin.as_ref()) {
|
|
||||||
failure!("Verify user pin failed: {}", e);
|
|
||||||
return simple_error!("User pin verify failed: {}", e);
|
|
||||||
}
|
|
||||||
success!("User pin verify success!");
|
|
||||||
|
|
||||||
let pgp_envelop = &envelop.encrypted_key;
|
let pgp_envelop = &envelop.encrypted_key;
|
||||||
debugging!("PGP envelop: {}", &pgp_envelop);
|
debugging!("PGP envelop: {}", &pgp_envelop);
|
||||||
@@ -219,6 +231,27 @@ fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XR
|
|||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_and_verify_openpgp_pin(trans: &mut OpenPgpTransaction, pin: &Option<String>) -> XResult<()> {
|
||||||
|
let pin = read_pin(pin);
|
||||||
|
if let Err(e) = trans.verify_pw1_user(pin.as_ref()) {
|
||||||
|
failure!("Verify user pin failed: {}", e);
|
||||||
|
return simple_error!("User pin verify failed: {}", e);
|
||||||
|
}
|
||||||
|
success!("User pin verify success!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_openpgp() -> XResult<OpenPgp> {
|
||||||
|
let card = match card::get_card() {
|
||||||
|
Err(e) => {
|
||||||
|
failure!("Get PGP card failed: {}", e);
|
||||||
|
return simple_error!("Get card failed: {}", e);
|
||||||
|
}
|
||||||
|
Ok(card) => card
|
||||||
|
};
|
||||||
|
Ok(OpenPgp::new(card))
|
||||||
|
}
|
||||||
|
|
||||||
fn read_slot(slot: &Option<String>) -> XResult<String> {
|
fn read_slot(slot: &Option<String>) -> XResult<String> {
|
||||||
match slot {
|
match slot {
|
||||||
Some(slot) => Ok(slot.to_string()),
|
Some(slot) => Ok(slot.to_string()),
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ use rsa::Pkcs1v15Encrypt;
|
|||||||
use rust_util::{debugging, failure, information, opt_result, simple_error, success, util_msg, warning, XResult};
|
use rust_util::{debugging, failure, information, opt_result, simple_error, success, util_msg, warning, XResult};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::{util, util_ecdh};
|
use crate::{util, util_ecdh, util_x25519};
|
||||||
use crate::compress::GzStreamEncoder;
|
use crate::compress::GzStreamEncoder;
|
||||||
use crate::config::{TinyEncryptConfig, TinyEncryptConfigEnvelop};
|
use crate::config::{TinyEncryptConfig, TinyEncryptConfigEnvelop};
|
||||||
use crate::crypto_aes::aes_gcm_encrypt;
|
use crate::crypto_aes::aes_gcm_encrypt;
|
||||||
use crate::crypto_rsa::parse_spki;
|
use crate::crypto_rsa::parse_spki;
|
||||||
use crate::spec::{EncMetadata, TINY_ENCRYPT_VERSION_10, TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta};
|
use crate::spec::{EncMetadata, TINY_ENCRYPT_VERSION_10, TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta};
|
||||||
use crate::util::{ENC_AES256_GCM_P256, TINY_ENC_CONFIG_FILE};
|
use crate::util::{ENC_AES256_GCM_P256, ENC_AES256_GCM_X25519, TINY_ENC_CONFIG_FILE};
|
||||||
use crate::wrap_key::{WrapKey, WrapKeyHeader};
|
use crate::wrap_key::{WrapKey, WrapKeyHeader};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
@@ -233,6 +233,9 @@ fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResu
|
|||||||
TinyEncryptEnvelopType::Pgp => {
|
TinyEncryptEnvelopType::Pgp => {
|
||||||
encrypted_envelops.push(encrypt_envelop_pgp(key, envelop)?);
|
encrypted_envelops.push(encrypt_envelop_pgp(key, envelop)?);
|
||||||
}
|
}
|
||||||
|
TinyEncryptEnvelopType::PgpX25519 => {
|
||||||
|
encrypted_envelops.push(encrypt_envelop_ecdh_x25519(key, envelop)?);
|
||||||
|
}
|
||||||
TinyEncryptEnvelopType::Ecdh => {
|
TinyEncryptEnvelopType::Ecdh => {
|
||||||
encrypted_envelops.push(encrypt_envelop_ecdh(key, envelop)?);
|
encrypted_envelops.push(encrypt_envelop_ecdh(key, envelop)?);
|
||||||
}
|
}
|
||||||
@@ -245,7 +248,23 @@ fn encrypt_envelops(key: &[u8], envelops: &[&TinyEncryptConfigEnvelop]) -> XResu
|
|||||||
fn encrypt_envelop_ecdh(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult<TinyEncryptEnvelop> {
|
fn encrypt_envelop_ecdh(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult<TinyEncryptEnvelop> {
|
||||||
let public_key_point_hex = &envelop.public_part;
|
let public_key_point_hex = &envelop.public_part;
|
||||||
let (shared_secret, ephemeral_spki) = util_ecdh::compute_shared_secret(public_key_point_hex)?;
|
let (shared_secret, ephemeral_spki) = util_ecdh::compute_shared_secret(public_key_point_hex)?;
|
||||||
let shared_key = util::simple_kdf(shared_secret.as_slice());
|
|
||||||
|
encrypt_envelop_shared_secret(key, &shared_secret, &ephemeral_spki, ENC_AES256_GCM_P256, envelop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt_envelop_ecdh_x25519(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResult<TinyEncryptEnvelop> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt_envelop_shared_secret(key: &[u8],
|
||||||
|
shared_secret: &[u8],
|
||||||
|
ephemeral_spki: &[u8],
|
||||||
|
enc_type: &str,
|
||||||
|
envelop: &TinyEncryptConfigEnvelop) -> XResult<TinyEncryptEnvelop> {
|
||||||
|
let shared_key = util::simple_kdf(shared_secret);
|
||||||
let (_, nonce) = util::make_key256_and_nonce();
|
let (_, nonce) = util::make_key256_and_nonce();
|
||||||
|
|
||||||
let encrypted_key = aes_gcm_encrypt(&shared_key, &nonce, key)?;
|
let encrypted_key = aes_gcm_encrypt(&shared_key, &nonce, key)?;
|
||||||
@@ -253,7 +272,7 @@ fn encrypt_envelop_ecdh(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResu
|
|||||||
let wrap_key = WrapKey {
|
let wrap_key = WrapKey {
|
||||||
header: WrapKeyHeader {
|
header: WrapKeyHeader {
|
||||||
kid: Some(envelop.kid.clone()),
|
kid: Some(envelop.kid.clone()),
|
||||||
enc: ENC_AES256_GCM_P256.to_string(),
|
enc: enc_type.to_string(),
|
||||||
e_pub_key: util::encode_base64_url_no_pad(&ephemeral_spki),
|
e_pub_key: util::encode_base64_url_no_pad(&ephemeral_spki),
|
||||||
},
|
},
|
||||||
nonce,
|
nonce,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub struct TinyEncryptConfigEnvelop {
|
|||||||
pub r#type: TinyEncryptEnvelopType,
|
pub r#type: TinyEncryptEnvelopType,
|
||||||
pub kid: String,
|
pub kid: String,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
|
pub args: Option<Vec<String>>,
|
||||||
pub public_part: String,
|
pub public_part: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +53,23 @@ impl TinyEncryptConfig {
|
|||||||
let config_contents = opt_result!(
|
let config_contents = opt_result!(
|
||||||
fs::read_to_string(&resolved_file), "Read file: {}, failed: {}", file
|
fs::read_to_string(&resolved_file), "Read file: {}, failed: {}", file
|
||||||
);
|
);
|
||||||
// TODO Replace with Human JSON
|
let mut config: TinyEncryptConfig = opt_result!(
|
||||||
Ok(opt_result!(serde_json::from_str(&config_contents), "Parse file: {}, failed: {}", file))
|
serde_json::from_str(&config_contents),"Parse file: {}, failed: {}", file);
|
||||||
|
let mut splitted_profiles = HashMap::new();
|
||||||
|
for (k, v) in config.profiles.into_iter() {
|
||||||
|
if !k.contains(",") {
|
||||||
|
splitted_profiles.insert(k, v);
|
||||||
|
} else {
|
||||||
|
k.split(",")
|
||||||
|
.map(|k| k.trim())
|
||||||
|
.filter(|k| k.len() > 0)
|
||||||
|
.for_each(|k| {
|
||||||
|
splitted_profiles.insert(k.to_string(), v.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.profiles = splitted_profiles;
|
||||||
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_envelops(&self, profile: &Option<String>) -> XResult<Vec<&TinyEncryptConfigEnvelop>> {
|
pub fn find_envelops(&self, profile: &Option<String>) -> XResult<Vec<&TinyEncryptConfigEnvelop>> {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::cmd_info::CmdInfo;
|
|||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
mod util_ecdh;
|
mod util_ecdh;
|
||||||
|
mod util_x25519;
|
||||||
mod compress;
|
mod compress;
|
||||||
mod config;
|
mod config;
|
||||||
mod spec;
|
mod spec;
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ pub struct TinyEncryptEnvelop {
|
|||||||
pub enum TinyEncryptEnvelopType {
|
pub enum TinyEncryptEnvelopType {
|
||||||
#[serde(rename = "pgp")]
|
#[serde(rename = "pgp")]
|
||||||
Pgp,
|
Pgp,
|
||||||
|
#[serde(rename = "pgp-x25519")]
|
||||||
|
PgpX25519,
|
||||||
#[serde(rename = "age")]
|
#[serde(rename = "age")]
|
||||||
Age,
|
Age,
|
||||||
#[serde(rename = "ecdh")]
|
#[serde(rename = "ecdh")]
|
||||||
@@ -65,6 +67,7 @@ impl TinyEncryptEnvelopType {
|
|||||||
pub fn get_name(&self) -> &'static str {
|
pub fn get_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
TinyEncryptEnvelopType::Pgp => "pgp",
|
TinyEncryptEnvelopType::Pgp => "pgp",
|
||||||
|
TinyEncryptEnvelopType::PgpX25519 => "pgp-x25519",
|
||||||
TinyEncryptEnvelopType::Age => "age",
|
TinyEncryptEnvelopType::Age => "age",
|
||||||
TinyEncryptEnvelopType::Ecdh => "ecdh",
|
TinyEncryptEnvelopType::Ecdh => "ecdh",
|
||||||
TinyEncryptEnvelopType::Kms => "kms",
|
TinyEncryptEnvelopType::Kms => "kms",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use rust_util::{simple_error, warning, XResult};
|
|||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
pub const ENC_AES256_GCM_P256: &str = "aes256-gcm-p256";
|
pub const ENC_AES256_GCM_P256: &str = "aes256-gcm-p256";
|
||||||
|
pub const ENC_AES256_GCM_X25519: &str = "aes256-gcm-x25519";
|
||||||
pub const TINY_ENC_FILE_EXT: &str = ".tinyenc";
|
pub const TINY_ENC_FILE_EXT: &str = ".tinyenc";
|
||||||
pub const TINY_ENC_CONFIG_FILE: &str = "~/.tinyencrypt/config-rs.json";
|
pub const TINY_ENC_CONFIG_FILE: &str = "~/.tinyencrypt/config-rs.json";
|
||||||
|
|
||||||
|
|||||||
19
src/util_x25519.rs
Normal file
19
src/util_x25519.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use rand::rngs::OsRng;
|
||||||
|
use rust_util::{opt_result, simple_error, XResult};
|
||||||
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
|
pub fn compute_x25519_shared_secret(public_key_point_hex: &str) -> XResult<(Vec<u8>, Vec<u8>)> {
|
||||||
|
let public_key_bytes = opt_result!(hex::decode(public_key_point_hex), "Parse X25519 public key hex failed: {}");
|
||||||
|
if public_key_bytes.len() != 32 {
|
||||||
|
return simple_error!("Parse X25519 key failed: not 32 bytes");
|
||||||
|
}
|
||||||
|
let public_key_bytes: [u8; 32] = public_key_bytes.try_into().unwrap();
|
||||||
|
let public_key_card = PublicKey::from(public_key_bytes);
|
||||||
|
|
||||||
|
let ephemeral_secret = EphemeralSecret::random_from_rng(OsRng);
|
||||||
|
let ephemeral_public = PublicKey::from(&ephemeral_secret);
|
||||||
|
|
||||||
|
let shared_secret = ephemeral_secret.diffie_hellman(&public_key_card);
|
||||||
|
|
||||||
|
Ok((shared_secret.as_bytes().to_vec(), ephemeral_public.as_bytes().to_vec()))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user