v0.2-x25519 #1
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -1019,9 +1019,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db"
|
||||
checksum = "45786cec4d5e54a224b15cb9f06751883103a27c19c93eda09b0b4f5f08fefac"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -1147,9 +1147,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
@@ -1920,9 +1920,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -2063,7 +2063,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiny-encrypt"
|
||||
version = "0.2.0-dev"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"aes-gcm-stream",
|
||||
"base64",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tiny-encrypt"
|
||||
version = "0.2.0-dev"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "A simple and tiny file encrypt tool"
|
||||
|
||||
@@ -6,8 +6,8 @@ use std::str::FromStr;
|
||||
use std::time::Instant;
|
||||
|
||||
use clap::Args;
|
||||
use openpgp_card::{OpenPgp, OpenPgpTransaction};
|
||||
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 x509_parser::prelude::FromDer;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
@@ -19,7 +19,7 @@ use crate::{card, file, util};
|
||||
use crate::compress::GzStreamDecoder;
|
||||
use crate::crypto_aes::aes_gcm_decrypt;
|
||||
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;
|
||||
|
||||
#[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>> {
|
||||
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(envelop, pin, slot),
|
||||
unknown_type => {
|
||||
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>> {
|
||||
let wrap_key = WrapKey::parse(&envelop.encrypted_key)?;
|
||||
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_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 slot_id = SlotId::Retired(retired_slot_id);
|
||||
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,
|
||||
&epk_bytes,
|
||||
AlgorithmId::EccP256,
|
||||
slot_id,
|
||||
), "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)?;
|
||||
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)
|
||||
}
|
||||
|
||||
fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
|
||||
let card = match card::get_card() {
|
||||
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 pgp = get_openpgp()?;
|
||||
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
|
||||
|
||||
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!");
|
||||
read_and_verify_openpgp_pin(&mut trans, pin)?;
|
||||
|
||||
let pgp_envelop = &envelop.encrypted_key;
|
||||
debugging!("PGP envelop: {}", &pgp_envelop);
|
||||
@@ -219,6 +231,27 @@ fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XR
|
||||
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> {
|
||||
match slot {
|
||||
Some(slot) => Ok(slot.to_string()),
|
||||
|
||||
@@ -43,6 +43,7 @@ pub struct TinyEncryptConfigEnvelop {
|
||||
pub r#type: TinyEncryptEnvelopType,
|
||||
pub kid: String,
|
||||
pub desc: Option<String>,
|
||||
pub args: Option<Vec<String>>,
|
||||
pub public_part: String,
|
||||
}
|
||||
|
||||
@@ -52,8 +53,23 @@ impl TinyEncryptConfig {
|
||||
let config_contents = opt_result!(
|
||||
fs::read_to_string(&resolved_file), "Read file: {}, failed: {}", file
|
||||
);
|
||||
// TODO Replace with Human JSON
|
||||
Ok(opt_result!(serde_json::from_str(&config_contents), "Parse file: {}, failed: {}", file))
|
||||
let mut config: TinyEncryptConfig = opt_result!(
|
||||
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>> {
|
||||
|
||||
Reference in New Issue
Block a user