feat: decrypt ecdh works

This commit is contained in:
2023-09-07 23:12:04 +08:00
parent 3a4af50e27
commit 3d40730cb5
6 changed files with 799 additions and 52 deletions

View File

@@ -2,17 +2,24 @@ use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::str::FromStr;
use aes_gcm_stream::Aes256GcmStreamDecryptor;
use openpgp_card::crypto_data::Cryptogram;
use openpgp_card::OpenPgp;
use rust_util::{debugging, failure, opt_result, simple_error, success, util_term, XResult};
use x509_parser::prelude::FromDer;
use x509_parser::x509::SubjectPublicKeyInfo;
use yubikey::piv::{AlgorithmId, decrypt_data, RetiredSlotId, SlotId};
use yubikey::YubiKey;
use crate::{file, util};
use crate::card::get_card;
use crate::spec::{TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta};
use crate::util::{decode_base64, TINY_ENC_FILE_EXT};
use crate::util::{decode_base64, decode_base64_url_no_pad, ENC_AES256_GCM_P256, simple_kdf, TINY_ENC_FILE_EXT};
use crate::wrap_key::WrapKey;
pub fn decrypt(path: &PathBuf, pin: &Option<String>) -> XResult<()> {
pub fn decrypt(path: &PathBuf, pin: &Option<String>, slot: &Option<String>) -> XResult<()> {
let path_display = format!("{}", path.display());
if !path_display.ends_with(TINY_ENC_FILE_EXT) {
return simple_error!("File is not tiny encrypt file: {}", &path_display);
@@ -28,7 +35,7 @@ pub fn decrypt(path: &PathBuf, pin: &Option<String>) -> XResult<()> {
debugging!("Found meta: {}", serde_json::to_string_pretty(&meta).unwrap());
let selected_envelop = select_envelop(&meta)?;
let key = try_decrypt_key(selected_envelop, pin)?;
let key = try_decrypt_key(selected_envelop, pin, slot)?;
let nonce = opt_result!( decode_base64(&meta.nonce), "Decode nonce failed: {}");
debugging!("Decrypt key: {}", hex::encode(&key));
@@ -61,13 +68,51 @@ fn decrypt_file(file_in: &mut File, file_out: &mut File, key: &[u8], nonce: &[u8
Ok(total_len)
}
fn try_decrypt_key(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<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::Ecdh => try_decrypt_key_ecdh(envelop, pin, slot),
unknown_type => return simple_error!("Unknown or not supported type: {}", unknown_type.get_name())
}
}
fn try_decrypt_key_ecdh(envelop: &TinyEncryptEnvelop, pin: &Option<String>, slot: &Option<String>) -> XResult<Vec<u8>> {
let is_slot_none = slot.as_ref().map(|s| s.is_empty()).unwrap_or(true);
if is_slot_none {
return simple_error!("--slot is required for ecdh");
}
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.");
}
let e_pub_key = &wrap_key.header.e_pub_key;
let e_pub_key_bytes = opt_result!(decode_base64_url_no_pad(e_pub_key), "Invalid envelop: {}");
let (_, subject_public_key_info) = opt_result!( SubjectPublicKeyInfo::from_der(&e_pub_key_bytes), "Invalid envelop: {}");
let slot = slot.as_ref().unwrap();
let pin = read_pin(pin);
let epk_bytes = subject_public_key_info.subject_public_key.as_ref();
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
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(
&mut yk,
&epk_bytes,
AlgorithmId::EccP256,
slot_id,
), "Decrypt piv failed: {}");
let key = simple_kdf(decrypted_shared_secret.as_slice());
let key: [u8; 32] = opt_result!(key.as_slice().try_into(), "Invalid envelop: {}");
let mut aes256_gcm = Aes256GcmStreamDecryptor::new(key, &wrap_key.nonce);
let mut b1 = aes256_gcm.update(&wrap_key.encrypted_data);
let b2 = opt_result!(aes256_gcm.finalize(), "Invalid envelop: {}");
b1.extend_from_slice(&b2);
Ok(b1)
}
fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
let card = match get_card() {
Err(e) => {