feat: v1.1.0, add static x25519 support

This commit is contained in:
2023-12-08 21:36:03 +08:00
parent 883ed0918b
commit fd7e8d35a6
12 changed files with 209 additions and 39 deletions

2
Cargo.lock generated
View File

@@ -1691,7 +1691,7 @@ dependencies = [
[[package]] [[package]]
name = "tiny-encrypt" name = "tiny-encrypt"
version = "1.0.2" version = "1.1.0"
dependencies = [ dependencies = [
"aes-gcm-stream", "aes-gcm-stream",
"base64", "base64",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "tiny-encrypt" name = "tiny-encrypt"
version = "1.0.2" version = "1.1.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"
@@ -9,8 +9,8 @@ repository = "https://git.hatter.ink/hatter/tiny-encrypt-rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["smartcard", "macos"] default = ["decrypt", "macos"]
smartcard = ["openpgp-card", "openpgp-card-pcsc", "yubikey"] decrypt = ["openpgp-card", "openpgp-card-pcsc", "yubikey"]
macos = ["security-framework"] macos = ["security-framework"]
[dependencies] [dependencies]
@@ -38,7 +38,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
simpledateformat = "0.1" simpledateformat = "0.1"
tabled = "0.14" tabled = "0.14"
x25519-dalek = "2.0" x25519-dalek = { version = "2.0", features = ["static_secrets", "getrandom"] }
x509-parser = "0.15" x509-parser = "0.15"
yubikey = { version = "0.8", features = ["untested"], optional = true } yubikey = { version = "0.8", features = ["untested"], optional = true }
zeroize = "1.7" zeroize = "1.7"

View File

@@ -9,7 +9,7 @@ use std::time::{Instant, SystemTime};
use clap::Args; use clap::Args;
use flate2::Compression; use flate2::Compression;
use openpgp_card::crypto_data::Cryptogram; use openpgp_card::crypto_data::Cryptogram;
use rust_util::{debugging, failure, iff, information, opt_result, println_ex, simple_error, success, util_cmd, util_msg, util_size, util_time, warning, XResult}; use rust_util::{debugging, failure, iff, information, opt_result, opt_value_result, println_ex, simple_error, success, util_cmd, util_msg, util_size, util_time, warning, XResult};
use rust_util::util_time::UnixEpochTime; use rust_util::util_time::UnixEpochTime;
use x509_parser::prelude::FromDer; use x509_parser::prelude::FromDer;
use x509_parser::x509::SubjectPublicKeyInfo; use x509_parser::x509::SubjectPublicKeyInfo;
@@ -18,6 +18,8 @@ use yubikey::YubiKey;
use zeroize::Zeroize; use zeroize::Zeroize;
use crate::{cmd_encrypt, consts, crypto_simple, util, util_enc_file, util_env, util_envelop, util_file, util_pgp, util_piv}; use crate::{cmd_encrypt, consts, crypto_simple, util, util_enc_file, util_env, util_envelop, util_file, util_pgp, util_piv};
#[cfg(feature = "macos")]
use crate::util_keychainpasskey;
use crate::compress::GzStreamDecoder; use crate::compress::GzStreamDecoder;
use crate::config::TinyEncryptConfig; use crate::config::TinyEncryptConfig;
use crate::consts::{ use crate::consts::{
@@ -61,9 +63,12 @@ pub struct CmdDecrypt {
/// Digest file /// Digest file
#[arg(long, short = 'D')] #[arg(long, short = 'D')]
pub digest_file: bool, pub digest_file: bool,
// Edit file /// Edit file
#[arg(long, short = 'E')] #[arg(long, short = 'E')]
pub edit_file: bool, pub edit_file: bool,
// Readonly
#[arg(long)]
pub readonly: bool,
/// Digest algorithm (sha1, sha256[default], sha384, sha512 ...) /// Digest algorithm (sha1, sha256[default], sha384, sha512 ...)
#[arg(long, short = 'A')] #[arg(long, short = 'A')]
pub digest_algorithm: Option<String>, pub digest_algorithm: Option<String>,
@@ -196,6 +201,10 @@ pub fn decrypt_single(config: &Option<TinyEncryptConfig>,
let do_edit_file = || -> XResult<()> { let do_edit_file = || -> XResult<()> {
let temp_file_content_bytes = run_file_editor_and_wait_content(&editor, &temp_file, secure_editor, &temp_encryption_key_nonce)?; let temp_file_content_bytes = run_file_editor_and_wait_content(&editor, &temp_file, secure_editor, &temp_encryption_key_nonce)?;
if cmd_decrypt.readonly {
information!("Readonly, do not check temp file is changed.");
return Ok(());
}
let temp_file_content_bytes = if secure_editor { let temp_file_content_bytes = if secure_editor {
let mut decryptor = temp_cryptor.decryptor(&temp_key_nonce)?; let mut decryptor = temp_cryptor.decryptor(&temp_key_nonce)?;
decryptor.decrypt(&temp_file_content_bytes)? decryptor.decrypt(&temp_file_content_bytes)?
@@ -421,6 +430,8 @@ pub fn try_decrypt_key(config: &Option<TinyEncryptConfig>,
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::PgpX25519 => try_decrypt_key_ecdh_pgp_x25519(envelop, pin),
#[cfg(feature = "macos")]
TinyEncryptEnvelopType::StaticX25519 => try_decrypt_key_ecdh_static_x25519(config, envelop),
TinyEncryptEnvelopType::Ecdh | TinyEncryptEnvelopType::EcdhP384 => try_decrypt_key_ecdh(config, envelop, pin, 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()), unknown_type => simple_error!("Unknown or unsupported type: {}", unknown_type.get_name()),
} }
@@ -491,6 +502,36 @@ fn try_decrypt_key_ecdh_pgp_x25519(envelop: &TinyEncryptEnvelop, pin: &Option<St
Ok(decrypted_key) Ok(decrypted_key)
} }
#[cfg(feature = "macos")]
fn try_decrypt_key_ecdh_static_x25519(config: &Option<TinyEncryptConfig>, envelop: &TinyEncryptEnvelop) -> XResult<Vec<u8>> {
let wrap_key = WrapKey::parse(&envelop.encrypted_key)?;
let cryptor = match wrap_key.header.enc.as_str() {
ENC_AES256_GCM_X25519 => Cryptor::Aes256Gcm,
ENC_CHACHA20_POLY1305_X25519 => Cryptor::ChaCha20Poly1305,
_ => return simple_error!("Unsupported header enc: {}", &wrap_key.header.enc),
};
let e_pub_key_bytes = wrap_key.header.get_e_pub_key_bytes()?;
let config = opt_value_result!(config, "Tiny encrypt config is not found");
let config_envelop = opt_value_result!(
config.find_by_kid(&envelop.kid), "Cannot find config for: {}", &envelop.kid);
let config_envelop_args = opt_value_result!(&config_envelop.args, "No arguments found for: {}", &envelop.kid);
if config_envelop_args.len() < 3 {
return simple_error!("Not enough arguments for: {}", &envelop.kid);
}
let service_name = &config_envelop_args[1];
let key_name = &config_envelop_args[2];
let shared_secret = opt_result!(
util_keychainpasskey::decrypt_data(service_name, key_name, &e_pub_key_bytes), "Decrypt static x25519 failed: {}");
let key = util::simple_kdf(shared_secret.as_slice());
let key_nonce = KeyNonce { k: &key, n: &wrap_key.nonce };
let decrypted_key = crypto_simple::decrypt(
cryptor, &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>> { fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
let mut pgp = util_pgp::get_openpgp()?; let mut pgp = util_pgp::get_openpgp()?;
let mut trans = opt_result!(pgp.transaction(), "Connect OpenPGP card failed: {}"); let mut trans = opt_result!(pgp.transaction(), "Connect OpenPGP card failed: {}");
@@ -523,7 +564,9 @@ pub fn select_envelop<'a>(meta: &'a TinyEncryptMeta, key_id: &Option<String>, co
if envelops.len() == 1 { if envelops.len() == 1 {
let selected_envelop = &envelops[0]; let selected_envelop = &envelops[0];
success!("Auto selected envelop: #{} {}", 1, util_envelop::format_envelop(selected_envelop, config)); success!("Auto selected envelop: #{} {}", 1, util_envelop::format_envelop(selected_envelop, config));
util::read_line("Press enter to continue: "); if !selected_envelop.r#type.auto_select() {
util::read_line("Press enter to continue: ");
}
return Ok(selected_envelop); return Ok(selected_envelop);
} }

View File

@@ -268,7 +268,7 @@ fn encrypt_envelops(cryptor: Cryptor, key: &[u8], envelops: &[&TinyEncryptConfig
TinyEncryptEnvelopType::Pgp => { TinyEncryptEnvelopType::Pgp => {
encrypted_envelops.push(encrypt_envelop_pgp(key, envelop)?); encrypted_envelops.push(encrypt_envelop_pgp(key, envelop)?);
} }
TinyEncryptEnvelopType::PgpX25519 => { TinyEncryptEnvelopType::PgpX25519 | TinyEncryptEnvelopType::StaticX25519 => {
encrypted_envelops.push(encrypt_envelop_ecdh_x25519(cryptor, key, envelop)?); encrypted_envelops.push(encrypt_envelop_ecdh_x25519(cryptor, key, envelop)?);
} }
TinyEncryptEnvelopType::Ecdh => { TinyEncryptEnvelopType::Ecdh => {

View File

@@ -40,6 +40,7 @@ impl Drop for CmdExecEnv {
} }
pub fn exec_env(cmd_exec_env: CmdExecEnv) -> XResult<()> { pub fn exec_env(cmd_exec_env: CmdExecEnv) -> XResult<()> {
util_msg::set_logger_std_out(false);
debugging!("Cmd exec env: {:?}", cmd_exec_env); debugging!("Cmd exec env: {:?}", cmd_exec_env);
let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE).ok(); let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE).ok();
if cmd_exec_env.arguments.is_empty() { if cmd_exec_env.arguments.is_empty() {

View File

@@ -1,11 +1,16 @@
use clap::Args; use clap::Args;
use rust_util::XResult; use rust_util::{debugging, information, opt_result, simple_error, success, XResult};
use security_framework::os::macos::keychain::SecKeychain;
use crate::config::TinyEncryptConfigEnvelop;
use crate::spec::TinyEncryptEnvelopType;
use crate::util_keychainpasskey;
#[derive(Debug, Args)] #[derive(Debug, Args)]
pub struct CmdKeychainKey { pub struct CmdKeychainKey {
/// Keychain name, or default // /// Keychain name, or default
#[arg(long, short = 'c')] // #[arg(long, short = 'c')]
pub keychain_name: Option<String>, // pub keychain_name: Option<String>,
/// Service name, or tiny-encrypt /// Service name, or tiny-encrypt
#[arg(long, short = 's')] #[arg(long, short = 's')]
pub server_name: Option<String>, pub server_name: Option<String>,
@@ -20,21 +25,38 @@ pub struct CmdKeychainKey {
#[allow(dead_code)] #[allow(dead_code)]
const DEFAULT_SERVICE_NAME: &str = "tiny-encrypt"; const DEFAULT_SERVICE_NAME: &str = "tiny-encrypt";
#[allow(dead_code)] pub fn keychain_key(cmd_keychain_key: CmdKeychainKey) -> XResult<()> {
pub enum KeyType { let service_name = cmd_keychain_key.server_name.as_deref().unwrap_or(DEFAULT_SERVICE_NAME);
P256, let sec_keychain = opt_result!(SecKeychain::default(), "Get keychain failed: {}");
P384, if sec_keychain.find_generic_password(service_name, &cmd_keychain_key.key_name).is_ok() {
X25519, return simple_error!("Static x25519 exists: {}.{}", service_name, &cmd_keychain_key.key_name);
} }
let (keychain_key, public_key) = util_keychainpasskey::generate_pass_x25519_static_secret();
opt_result!(
sec_keychain.set_generic_password(service_name, &cmd_keychain_key.key_name, keychain_key.as_bytes()),
"Write static x25519 failed: {}"
);
let public_key_hex = hex::encode(public_key.as_bytes());
debugging!("Keychain key : {}", keychain_key);
success!("Keychain name: {}", &cmd_keychain_key.key_name);
success!("Public key : {}", &public_key_hex);
let config_envelop = TinyEncryptConfigEnvelop {
r#type: TinyEncryptEnvelopType::StaticX25519,
sid: Some(cmd_keychain_key.key_name.clone()),
kid: format!("keychain:{}", &public_key_hex),
desc: Some("Keychain static".to_string()),
args: Some(vec![
"".to_string(),
service_name.to_string(),
cmd_keychain_key.key_name.clone(),
]),
public_part: public_key_hex,
};
information!("Config envelop:\n{}", serde_json::to_string_pretty(&config_envelop).unwrap());
// TODO Under developing
// keychain://keychain_name?sn=service_name&kt=kp-p256&kn=key_name&fp=fingerprint
// keychain_name -> default
// service_name -> tiny-encrypt
// kt=kp-p256|kp-p384|kp-x25519 -> keypair P256, P385 or X25519
// key_name -> key name in keychain
// fingerprint -> hex(SHA256(public_key)[0..4])
pub fn keychain_key(_cmd_keychain_key: CmdKeychainKey) -> XResult<()> {
println!();
Ok(()) Ok(())
} }

View File

@@ -42,9 +42,12 @@ pub struct TinyEncryptConfig {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TinyEncryptConfigEnvelop { pub struct TinyEncryptConfigEnvelop {
pub r#type: TinyEncryptEnvelopType, pub r#type: TinyEncryptEnvelopType,
#[serde(skip_serializing_if = "Option::is_none")]
pub sid: Option<String>, pub sid: Option<String>,
pub kid: String, pub kid: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub desc: Option<String>, pub desc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub args: Option<Vec<String>>, pub args: Option<Vec<String>>,
pub public_part: String, pub public_part: String,
} }

View File

@@ -1,10 +1,10 @@
pub use cmd_config::CmdConfig; pub use cmd_config::CmdConfig;
pub use cmd_config::config; pub use cmd_config::config;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
pub use cmd_decrypt::CmdDecrypt; pub use cmd_decrypt::CmdDecrypt;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
pub use cmd_decrypt::decrypt; pub use cmd_decrypt::decrypt;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
pub use cmd_decrypt::decrypt_single; pub use cmd_decrypt::decrypt_single;
pub use cmd_directdecrypt::CmdDirectDecrypt; pub use cmd_directdecrypt::CmdDirectDecrypt;
pub use cmd_directdecrypt::direct_decrypt; pub use cmd_directdecrypt::direct_decrypt;
@@ -21,7 +21,9 @@ pub use cmd_version::version;
pub use cmd_initkeychainkey::CmdKeychainKey; pub use cmd_initkeychainkey::CmdKeychainKey;
#[cfg(feature = "macos")] #[cfg(feature = "macos")]
pub use cmd_initkeychainkey::keychain_key; pub use cmd_initkeychainkey::keychain_key;
#[cfg(feature = "decrypt")]
pub use cmd_execenv::CmdExecEnv; pub use cmd_execenv::CmdExecEnv;
#[cfg(feature = "decrypt")]
pub use cmd_execenv::exec_env; pub use cmd_execenv::exec_env;
@@ -30,9 +32,9 @@ mod util;
mod util_env; mod util_env;
mod util_digest; mod util_digest;
mod util_progress; mod util_progress;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
mod util_piv; mod util_piv;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
mod util_pgp; mod util_pgp;
mod util_p256; mod util_p256;
mod util_p384; mod util_p384;
@@ -50,11 +52,14 @@ mod util_enc_file;
mod cmd_version; mod cmd_version;
mod cmd_config; mod cmd_config;
mod cmd_info; mod cmd_info;
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
mod cmd_decrypt; mod cmd_decrypt;
mod cmd_encrypt; mod cmd_encrypt;
mod cmd_directdecrypt; mod cmd_directdecrypt;
#[cfg(feature = "macos")] #[cfg(feature = "macos")]
mod cmd_initkeychainkey; mod cmd_initkeychainkey;
#[cfg(feature = "macos")]
mod util_keychainpasskey;
#[cfg(feature = "decrypt")]
mod cmd_execenv; mod cmd_execenv;

View File

@@ -3,9 +3,11 @@ extern crate core;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use rust_util::XResult; use rust_util::XResult;
use tiny_encrypt::{CmdConfig, CmdDirectDecrypt, CmdEncrypt, CmdExecEnv, CmdInfo, CmdVersion}; use tiny_encrypt::{CmdConfig, CmdDirectDecrypt, CmdEncrypt, CmdInfo, CmdVersion};
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
use tiny_encrypt::CmdDecrypt; use tiny_encrypt::CmdDecrypt;
#[cfg(feature = "decrypt")]
use tiny_encrypt::CmdExecEnv;
#[cfg(feature = "macos")] #[cfg(feature = "macos")]
use tiny_encrypt::CmdKeychainKey; use tiny_encrypt::CmdKeychainKey;
@@ -22,7 +24,7 @@ enum Commands {
/// Encrypt file(s) /// Encrypt file(s)
#[command(arg_required_else_help = true, short_flag = 'e')] #[command(arg_required_else_help = true, short_flag = 'e')]
Encrypt(CmdEncrypt), Encrypt(CmdEncrypt),
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
/// Decrypt file(s) /// Decrypt file(s)
#[command(arg_required_else_help = true, short_flag = 'd')] #[command(arg_required_else_help = true, short_flag = 'd')]
Decrypt(CmdDecrypt), Decrypt(CmdDecrypt),
@@ -36,6 +38,7 @@ enum Commands {
/// Keychain Key [pending implementation] /// Keychain Key [pending implementation]
#[command(arg_required_else_help = true, short_flag = 'k')] #[command(arg_required_else_help = true, short_flag = 'k')]
KeychainKey(CmdKeychainKey), KeychainKey(CmdKeychainKey),
#[cfg(feature = "decrypt")]
/// Execute env /// Execute env
#[command(arg_required_else_help = true, short_flag = 'X')] #[command(arg_required_else_help = true, short_flag = 'X')]
ExecEnv(CmdExecEnv), ExecEnv(CmdExecEnv),
@@ -51,12 +54,13 @@ fn main() -> XResult<()> {
let args = Cli::parse(); let args = Cli::parse();
match args.command { match args.command {
Commands::Encrypt(cmd_encrypt) => tiny_encrypt::encrypt(cmd_encrypt), Commands::Encrypt(cmd_encrypt) => tiny_encrypt::encrypt(cmd_encrypt),
#[cfg(feature = "smartcard")] #[cfg(feature = "decrypt")]
Commands::Decrypt(cmd_decrypt) => tiny_encrypt::decrypt(cmd_decrypt), Commands::Decrypt(cmd_decrypt) => tiny_encrypt::decrypt(cmd_decrypt),
Commands::DirectDecrypt(cmd_direct_decrypt) => tiny_encrypt::direct_decrypt(cmd_direct_decrypt), Commands::DirectDecrypt(cmd_direct_decrypt) => tiny_encrypt::direct_decrypt(cmd_direct_decrypt),
Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info), Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info),
#[cfg(feature = "macos")] #[cfg(feature = "macos")]
Commands::KeychainKey(cmd_keychain_key) => tiny_encrypt::keychain_key(cmd_keychain_key), Commands::KeychainKey(cmd_keychain_key) => tiny_encrypt::keychain_key(cmd_keychain_key),
#[cfg(feature = "decrypt")]
Commands::ExecEnv(cmd_exec_env) => tiny_encrypt::exec_env(cmd_exec_env), Commands::ExecEnv(cmd_exec_env) => tiny_encrypt::exec_env(cmd_exec_env),
Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version), Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version),
Commands::Config(cmd_config) => tiny_encrypt::config(cmd_config), Commands::Config(cmd_config) => tiny_encrypt::config(cmd_config),

View File

@@ -71,6 +71,9 @@ pub enum TinyEncryptEnvelopType {
// OpenPGP X25519 // OpenPGP X25519
#[serde(rename = "pgp-x25519")] #[serde(rename = "pgp-x25519")]
PgpX25519, PgpX25519,
// Static X25519 (less secure)
#[serde(rename = "static-x25519")]
StaticX25519,
// Age, tiny-encrypt-rs is not supported // Age, tiny-encrypt-rs is not supported
#[serde(rename = "age")] #[serde(rename = "age")]
Age, Age,
@@ -89,16 +92,30 @@ impl TinyEncryptEnvelopType {
pub fn get_upper_name(&self) -> String { pub fn get_upper_name(&self) -> String {
self.get_name().to_uppercase() self.get_name().to_uppercase()
} }
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::PgpX25519 => "pgp-x25519",
TinyEncryptEnvelopType::StaticX25519 => "static-x25519",
TinyEncryptEnvelopType::Age => "age", TinyEncryptEnvelopType::Age => "age",
TinyEncryptEnvelopType::Ecdh => "ecdh", TinyEncryptEnvelopType::Ecdh => "ecdh",
TinyEncryptEnvelopType::EcdhP384 => "ecdh-p384", TinyEncryptEnvelopType::EcdhP384 => "ecdh-p384",
TinyEncryptEnvelopType::Kms => "kms", TinyEncryptEnvelopType::Kms => "kms",
} }
} }
pub fn auto_select(&self) -> bool {
match self {
TinyEncryptEnvelopType::Pgp => false,
TinyEncryptEnvelopType::PgpX25519 => false,
TinyEncryptEnvelopType::StaticX25519 => true,
TinyEncryptEnvelopType::Age => false,
TinyEncryptEnvelopType::Ecdh => false,
TinyEncryptEnvelopType::EcdhP384 => false,
TinyEncryptEnvelopType::Kms => true,
}
}
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]

View File

@@ -26,7 +26,7 @@ fn get_envelop_desc(envelop: &TinyEncryptEnvelop, config_envelop: &Option<&TinyE
} }
pub fn with_width_type(s: &str) -> String { pub fn with_width_type(s: &str) -> String {
with_width(s, 10) with_width(s, 13)
} }
pub fn with_width(s: &str, width: usize) -> String { pub fn with_width(s: &str, width: usize) -> String {

View File

@@ -0,0 +1,75 @@
use rust_util::{opt_result, simple_error, XResult};
use security_framework::os::macos::keychain::SecKeychain;
use x25519_dalek::{PublicKey, StaticSecret};
use zeroize::Zeroize;
const X2559_PLAIN_PREFIX: &str = "x25519-plain:";
pub struct X25519StaticSecret {
pub secret: Vec<u8>,
}
impl Zeroize for X25519StaticSecret {
fn zeroize(&mut self) {
self.secret.zeroize();
}
}
impl X25519StaticSecret {
pub fn parse(key: &str) -> XResult<Self> {
if !key.starts_with(X2559_PLAIN_PREFIX) {
return simple_error!("Not X25519 plain key");
}
let extract_key_hex = &key[X2559_PLAIN_PREFIX.len()..];
let extract_key = opt_result!(hex::decode(extract_key_hex), "Decode X25519 plain key failed: {}");
Ok(Self {
secret: extract_key,
})
}
pub fn to_str(&self) -> String {
let mut v = String::new();
v.push_str(X2559_PLAIN_PREFIX);
v.push_str(&hex::encode(&self.secret));
v
}
pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
secret: bytes.to_vec(),
}
}
pub fn to_static_secret(&self) -> XResult<StaticSecret> {
let secret_slice = self.secret.as_slice();
let mut inner_secret: [u8; 32] = opt_result!(secret_slice.try_into(), "X25519 secret key error: {}");
let static_secret = StaticSecret::from(inner_secret);
inner_secret.zeroize();
Ok(static_secret)
}
}
pub fn decrypt_data(service_name: &str, key_name: &str, ephemeral_public_key_bytes: &[u8]) -> XResult<Vec<u8>> {
let sec_keychain = opt_result!(SecKeychain::default(), "Get keychain failed: {}");
let (static_x25519, _) = opt_result!(sec_keychain.find_generic_password(service_name, key_name),
"Cannot find static x25519 {}.{}: {}", service_name, key_name);
let static_x25519_bytes = static_x25519.as_ref();
let static_x25519_str = opt_result!(String::from_utf8(static_x25519_bytes.to_vec()), "Parse static x25519 failed: {}");
let x25519_static_secret = X25519StaticSecret::parse(&static_x25519_str)?;
let static_secret = x25519_static_secret.to_static_secret()?;
let inner_ephemeral_public_key: [u8; 32] = opt_result!(
ephemeral_public_key_bytes.try_into(), "X25519 public key error: {}");
let ephemeral_public_key = PublicKey::from(inner_ephemeral_public_key);
let shared_secret = static_secret.diffie_hellman(&ephemeral_public_key);
Ok(shared_secret.as_bytes().to_vec())
}
pub fn generate_pass_x25519_static_secret() -> (String, PublicKey) {
let static_secret = StaticSecret::random();
let public_key: PublicKey = (&static_secret).into();
let static_secret_bytes = static_secret.as_bytes();
let x25519_static_secret = X25519StaticSecret::from_bytes(static_secret_bytes);
(x25519_static_secret.to_str(), public_key)
}