feat: v1.3.0, support init-piv
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -1700,7 +1700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiny-encrypt"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"aes-gcm-stream",
|
||||
"base64",
|
||||
@@ -1724,6 +1724,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simpledateformat",
|
||||
"spki",
|
||||
"swift-rs",
|
||||
"tabled",
|
||||
"x25519-dalek",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tiny-encrypt"
|
||||
version = "1.2.2"
|
||||
version = "1.3.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "A simple and tiny file encrypt tool"
|
||||
@@ -10,7 +10,8 @@ repository = "https://git.hatter.ink/hatter/tiny-encrypt-rs"
|
||||
|
||||
[features]
|
||||
default = ["decrypt", "macos", "secure-enclave"]
|
||||
decrypt = ["openpgp-card", "openpgp-card-pcsc", "yubikey"]
|
||||
decrypt = ["smartcard"]
|
||||
smartcard = ["openpgp-card", "openpgp-card-pcsc", "yubikey"]
|
||||
macos = ["security-framework"]
|
||||
secure-enclave = ["macos", "swift-rs"]
|
||||
|
||||
@@ -44,6 +45,7 @@ x509-parser = "0.15"
|
||||
yubikey = { version = "0.8", features = ["untested"], optional = true }
|
||||
zeroize = "1.7"
|
||||
swift-rs = { path = "swift-rs", optional = true }
|
||||
spki = "0.7.3"
|
||||
|
||||
[build-dependencies]
|
||||
swift-rs = { path = "swift-rs", features = ["build"], optional = true }
|
||||
|
||||
2
justfile
2
justfile
@@ -12,6 +12,8 @@ build-no-features:
|
||||
# Try build all
|
||||
try-build-all:
|
||||
cargo build --no-default-features
|
||||
cargo build --no-default-features --features smartcard
|
||||
cargo build --no-default-features --features decrypt
|
||||
cargo build --no-default-features --features macos
|
||||
cargo build --no-default-features --features secure-enclave
|
||||
cargo build
|
||||
|
||||
@@ -436,7 +436,7 @@ pub fn try_decrypt_key(config: &Option<TinyEncryptConfig>,
|
||||
TinyEncryptEnvelopType::PgpX25519 => try_decrypt_key_ecdh_pgp_x25519(envelop, pin),
|
||||
#[cfg(feature = "macos")]
|
||||
TinyEncryptEnvelopType::StaticX25519 => try_decrypt_key_ecdh_static_x25519(config, envelop),
|
||||
TinyEncryptEnvelopType::PivP256 | TinyEncryptEnvelopType::EcdhP384 => try_decrypt_key_ecdh(config, envelop, pin, slot),
|
||||
TinyEncryptEnvelopType::PivP256 | TinyEncryptEnvelopType::PivP384 => try_decrypt_key_ecdh(config, envelop, pin, slot),
|
||||
#[cfg(feature = "secure-enclave")]
|
||||
TinyEncryptEnvelopType::KeyP256 => try_decrypt_se_key_ecdh(config, envelop),
|
||||
unknown_type => simple_error!("Unknown or unsupported type: {}", unknown_type.get_name()),
|
||||
|
||||
@@ -274,7 +274,7 @@ fn encrypt_envelops(cryptor: Cryptor, key: &[u8], envelops: &[&TinyEncryptConfig
|
||||
TinyEncryptEnvelopType::PivP256 | TinyEncryptEnvelopType::KeyP256 => {
|
||||
encrypted_envelops.push(encrypt_envelop_ecdh(cryptor, key, envelop)?);
|
||||
}
|
||||
TinyEncryptEnvelopType::EcdhP384 => {
|
||||
TinyEncryptEnvelopType::PivP384 => {
|
||||
encrypted_envelops.push(encrypt_envelop_ecdh_p384(cryptor, key, envelop)?);
|
||||
}
|
||||
_ => return simple_error!("Not supported type: {:?}", envelop.r#type),
|
||||
|
||||
@@ -24,10 +24,9 @@ pub struct CmdInitKeychain {
|
||||
pub key_name: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
const DEFAULT_SERVICE_NAME: &str = "tiny-encrypt";
|
||||
|
||||
pub fn keychain_key(cmd_init_keychain: CmdInitKeychain) -> XResult<()> {
|
||||
pub fn init_keychain(cmd_init_keychain: CmdInitKeychain) -> XResult<()> {
|
||||
if cmd_init_keychain.secure_enclave {
|
||||
#[cfg(feature = "secure-enclave")]
|
||||
return keychain_key_se(cmd_init_keychain);
|
||||
|
||||
157
src/cmd_initpiv.rs
Normal file
157
src/cmd_initpiv.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use clap::Args;
|
||||
use p256::pkcs8::der::Decode;
|
||||
use rust_util::{failure, iff, information, opt_result, simple_error, warning, XResult};
|
||||
use spki::{ObjectIdentifier, SubjectPublicKeyInfoOwned};
|
||||
use spki::der::Encode;
|
||||
use x509_parser::prelude::FromDer;
|
||||
use x509_parser::public_key::RSAPublicKey;
|
||||
use yubikey::Certificate;
|
||||
use yubikey::Key;
|
||||
use yubikey::piv::{AlgorithmId, RetiredSlotId, SlotId};
|
||||
use yubikey::YubiKey;
|
||||
|
||||
use crate::config::TinyEncryptConfigEnvelop;
|
||||
use crate::spec::TinyEncryptEnvelopType;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct CmdInitPiv {
|
||||
/// PIV slot
|
||||
#[arg(long, short = 's')]
|
||||
pub slot: String,
|
||||
}
|
||||
|
||||
const RSA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
|
||||
const ECC: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
|
||||
|
||||
const ECC_P256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
|
||||
const ECC_P384: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.34");
|
||||
|
||||
pub fn init_piv(cmd_init_piv: CmdInitPiv) -> XResult<()> {
|
||||
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
|
||||
let slot_id = get_slot_id(&cmd_init_piv.slot)?;
|
||||
let slot_id_hex = to_slot_hex(&slot_id);
|
||||
let keys = opt_result!(Key::list(&mut yk), "List keys failed: {}");
|
||||
|
||||
let find_key = || {
|
||||
for k in &keys {
|
||||
let key_slot_str = format!("{:x}", Into::<u8>::into(k.slot()));
|
||||
if slot_equals(&slot_id, &key_slot_str) {
|
||||
return Some(k);
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
let key = match find_key() {
|
||||
None => {
|
||||
warning!("Key not found.");
|
||||
return Ok(());
|
||||
}
|
||||
Some(key) => key,
|
||||
};
|
||||
let cert = &key.certificate().cert.tbs_certificate;
|
||||
if let Ok(algorithm_id) = get_algorithm_id_by_certificate(key.certificate()) {
|
||||
let public_key_bit_string = &cert.subject_public_key_info.subject_public_key;
|
||||
match algorithm_id {
|
||||
AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
|
||||
let pk_point_hex = public_key_bit_string.raw_bytes();
|
||||
let public_key_point_hex = hex::encode(pk_point_hex);
|
||||
let compressed_public_key_point_hex = format!("02{}", hex::encode(&pk_point_hex[1..(pk_point_hex.len() / 2) + 1]));
|
||||
|
||||
let is_p256 = algorithm_id == AlgorithmId::EccP256;
|
||||
let config_envelop = TinyEncryptConfigEnvelop {
|
||||
r#type: iff!(is_p256, TinyEncryptEnvelopType::PivP256, TinyEncryptEnvelopType::PivP384),
|
||||
sid: Some(format!("piv-{}-ecdh-{}", &slot_id_hex, iff!(is_p256, "p256", "p384"))),
|
||||
kid: compressed_public_key_point_hex.clone(),
|
||||
desc: Some(format!("PIV --slot {}", &slot_id_hex)),
|
||||
args: Some(vec![
|
||||
slot_id_hex.clone()
|
||||
]),
|
||||
public_part: public_key_point_hex,
|
||||
};
|
||||
|
||||
information!("Config envelop:\n{}", serde_json::to_string_pretty(&config_envelop).unwrap());
|
||||
}
|
||||
_ => {
|
||||
failure!("Only support P256 or P384, actual: {:?}", algorithm_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn get_algorithm_id_by_certificate(certificate: &Certificate) -> XResult<AlgorithmId> {
|
||||
let tbs_certificate = &certificate.cert.tbs_certificate;
|
||||
get_algorithm_id(&tbs_certificate.subject_public_key_info)
|
||||
}
|
||||
|
||||
fn get_algorithm_id(public_key_info: &SubjectPublicKeyInfoOwned) -> XResult<AlgorithmId> {
|
||||
if public_key_info.algorithm.oid == RSA {
|
||||
let rsa_public_key = opt_result!(
|
||||
RSAPublicKey::from_der(public_key_info.subject_public_key.raw_bytes()), "Parse public key failed: {}");
|
||||
let starts_with_0 = rsa_public_key.1.modulus.starts_with(&[0]);
|
||||
let public_key_bits = (rsa_public_key.1.modulus.len() - if starts_with_0 { 1 } else { 0 }) * 8;
|
||||
if public_key_bits == 1024 {
|
||||
return Ok(AlgorithmId::Rsa1024);
|
||||
}
|
||||
if public_key_bits == 2048 {
|
||||
return Ok(AlgorithmId::Rsa2048);
|
||||
}
|
||||
return simple_error!("Unknown rsa bits: {}", public_key_bits);
|
||||
}
|
||||
if public_key_info.algorithm.oid == ECC {
|
||||
if let Some(any) = &public_key_info.algorithm.parameters {
|
||||
let any_parameter_der = opt_result!(any.to_der(), "Bad any parameter: {}");
|
||||
let any_parameter_oid = opt_result!(ObjectIdentifier::from_der(&any_parameter_der), "Bad any parameter der: {}");
|
||||
if any_parameter_oid == ECC_P256 {
|
||||
return Ok(AlgorithmId::EccP256);
|
||||
}
|
||||
if any_parameter_oid == ECC_P384 {
|
||||
return Ok(AlgorithmId::EccP384);
|
||||
}
|
||||
return simple_error!("Unknown any parameter oid: {}", any_parameter_oid);
|
||||
}
|
||||
}
|
||||
simple_error!("Unknown algorithm: {}", public_key_info.algorithm.oid)
|
||||
}
|
||||
|
||||
fn slot_equals(slot_id: &SlotId, slot: &str) -> bool {
|
||||
get_slot_id(slot).map(|sid| &sid == slot_id).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn to_slot_hex(slot: &SlotId) -> String {
|
||||
let slot_id: u8 = (*slot).into();
|
||||
format!("{:x}", slot_id)
|
||||
}
|
||||
|
||||
fn get_slot_id(slot: &str) -> XResult<SlotId> {
|
||||
let slot_lower = slot.to_lowercase();
|
||||
Ok(match slot_lower.as_str() {
|
||||
"9a" | "auth" | "authentication" => SlotId::Authentication,
|
||||
"9c" | "sign" | "signature" => SlotId::Signature,
|
||||
"9d" | "keym" | "keymanagement" => SlotId::KeyManagement,
|
||||
"9e" | "card" | "cardauthentication" => SlotId::CardAuthentication,
|
||||
"r1" | "82" => SlotId::Retired(RetiredSlotId::R1),
|
||||
"r2" | "83" => SlotId::Retired(RetiredSlotId::R2),
|
||||
"r3" | "84" => SlotId::Retired(RetiredSlotId::R3),
|
||||
"r4" | "85" => SlotId::Retired(RetiredSlotId::R4),
|
||||
"r5" | "86" => SlotId::Retired(RetiredSlotId::R5),
|
||||
"r6" | "87" => SlotId::Retired(RetiredSlotId::R6),
|
||||
"r7" | "88" => SlotId::Retired(RetiredSlotId::R7),
|
||||
"r8" | "89" => SlotId::Retired(RetiredSlotId::R8),
|
||||
"r9" | "8a" => SlotId::Retired(RetiredSlotId::R9),
|
||||
"r10" | "8b" => SlotId::Retired(RetiredSlotId::R10),
|
||||
"r11" | "8c" => SlotId::Retired(RetiredSlotId::R11),
|
||||
"r12" | "8d" => SlotId::Retired(RetiredSlotId::R12),
|
||||
"r13" | "8e" => SlotId::Retired(RetiredSlotId::R13),
|
||||
"r14" | "8f" => SlotId::Retired(RetiredSlotId::R14),
|
||||
"r15" | "90" => SlotId::Retired(RetiredSlotId::R15),
|
||||
"r16" | "91" => SlotId::Retired(RetiredSlotId::R16),
|
||||
"r17" | "92" => SlotId::Retired(RetiredSlotId::R17),
|
||||
"r18" | "93" => SlotId::Retired(RetiredSlotId::R18),
|
||||
"r19" | "94" => SlotId::Retired(RetiredSlotId::R19),
|
||||
"r20" | "95" => SlotId::Retired(RetiredSlotId::R20),
|
||||
_ => return simple_error!("Unknown slot: {}", slot),
|
||||
})
|
||||
}
|
||||
@@ -14,6 +14,8 @@ pub fn version(_cmd_version: CmdVersion) -> XResult<()> {
|
||||
features.push("decrypt".to_string());
|
||||
#[cfg(feature = "macos")]
|
||||
features.push("macos".to_string());
|
||||
#[cfg(feature = "smartcard")]
|
||||
features.push("smartcard".to_string());
|
||||
#[cfg(feature = "secure-enclave")]
|
||||
features.push(format!("secure-enclave{}", iff!(util_keychainkey::is_support_se(), "*", "")));
|
||||
if features.is_empty() { features.push("-".to_string()); }
|
||||
|
||||
@@ -22,7 +22,11 @@ pub use cmd_info::info_single;
|
||||
#[cfg(feature = "macos")]
|
||||
pub use cmd_initkeychain::CmdInitKeychain;
|
||||
#[cfg(feature = "macos")]
|
||||
pub use cmd_initkeychain::keychain_key;
|
||||
pub use cmd_initkeychain::init_keychain;
|
||||
#[cfg(feature = "smartcard")]
|
||||
pub use cmd_initpiv::CmdInitPiv;
|
||||
#[cfg(feature = "smartcard")]
|
||||
pub use cmd_initpiv::init_piv;
|
||||
pub use cmd_version::CmdVersion;
|
||||
pub use cmd_version::version;
|
||||
|
||||
@@ -57,6 +61,8 @@ mod cmd_encrypt;
|
||||
mod cmd_directdecrypt;
|
||||
#[cfg(feature = "macos")]
|
||||
mod cmd_initkeychain;
|
||||
#[cfg(feature = "smartcard")]
|
||||
mod cmd_initpiv;
|
||||
#[cfg(feature = "macos")]
|
||||
mod util_keychainstatic;
|
||||
#[cfg(feature = "decrypt")]
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -10,6 +10,8 @@ use tiny_encrypt::CmdDecrypt;
|
||||
use tiny_encrypt::CmdExecEnv;
|
||||
#[cfg(feature = "macos")]
|
||||
use tiny_encrypt::CmdInitKeychain;
|
||||
#[cfg(feature = "smartcard")]
|
||||
use tiny_encrypt::CmdInitPiv;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "tiny-encrypt-rs")]
|
||||
@@ -38,6 +40,10 @@ enum Commands {
|
||||
/// Init Keychain (Secure Enclave or Static)
|
||||
#[command(arg_required_else_help = true, short_flag = 'K')]
|
||||
InitKeychain(CmdInitKeychain),
|
||||
#[cfg(feature = "smartcard")]
|
||||
/// Init PIV
|
||||
#[command(arg_required_else_help = true, short_flag = 'P')]
|
||||
InitPiv(CmdInitPiv),
|
||||
#[cfg(feature = "decrypt")]
|
||||
/// Execute environment
|
||||
#[command(arg_required_else_help = true, short_flag = 'X')]
|
||||
@@ -59,7 +65,9 @@ fn main() -> XResult<()> {
|
||||
Commands::DirectDecrypt(cmd_direct_decrypt) => tiny_encrypt::direct_decrypt(cmd_direct_decrypt),
|
||||
Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info),
|
||||
#[cfg(feature = "macos")]
|
||||
Commands::InitKeychain(cmd_keychain_key) => tiny_encrypt::keychain_key(cmd_keychain_key),
|
||||
Commands::InitKeychain(cmd_keychain_key) => tiny_encrypt::init_keychain(cmd_keychain_key),
|
||||
#[cfg(feature = "smartcard")]
|
||||
Commands::InitPiv(cmd_init_piv) => tiny_encrypt::init_piv(cmd_init_piv),
|
||||
#[cfg(feature = "decrypt")]
|
||||
Commands::ExecEnv(cmd_exec_env) => tiny_encrypt::exec_env(cmd_exec_env),
|
||||
Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::fs::Metadata;
|
||||
|
||||
use rust_util::{opt_result, util_time, XResult};
|
||||
use rust_util::util_time::get_millis;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::Metadata;
|
||||
|
||||
use crate::{compress, crypto_simple};
|
||||
use crate::consts::SALT_META;
|
||||
@@ -85,7 +84,7 @@ pub enum TinyEncryptEnvelopType {
|
||||
PivP256,
|
||||
// PIV ECDH P384
|
||||
#[serde(rename = "piv-p384", alias = "ecdh-p384")]
|
||||
EcdhP384,
|
||||
PivP384,
|
||||
// KMS, tiny-encrypt-rs is not supported
|
||||
#[serde(rename = "kms")]
|
||||
Kms,
|
||||
@@ -104,7 +103,7 @@ impl TinyEncryptEnvelopType {
|
||||
TinyEncryptEnvelopType::KeyP256 => "key-p256",
|
||||
TinyEncryptEnvelopType::Age => "age",
|
||||
TinyEncryptEnvelopType::PivP256 => "piv-p256",
|
||||
TinyEncryptEnvelopType::EcdhP384 => "piv-p384",
|
||||
TinyEncryptEnvelopType::PivP384 => "piv-p384",
|
||||
TinyEncryptEnvelopType::Kms => "kms",
|
||||
}
|
||||
}
|
||||
@@ -117,7 +116,7 @@ impl TinyEncryptEnvelopType {
|
||||
TinyEncryptEnvelopType::KeyP256 => true,
|
||||
TinyEncryptEnvelopType::Age => false,
|
||||
TinyEncryptEnvelopType::PivP256 => false,
|
||||
TinyEncryptEnvelopType::EcdhP384 => false,
|
||||
TinyEncryptEnvelopType::PivP384 => false,
|
||||
TinyEncryptEnvelopType::Kms => true,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user