feat: sign-file works

This commit is contained in:
2023-11-26 12:26:47 +08:00
parent 3b3ce7b623
commit dd2e804ee6
3 changed files with 590 additions and 24 deletions

View File

@@ -3,7 +3,9 @@ use std::time::SystemTime;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::{util_msg, XResult};
use rust_util::util_clap::{Command, CommandError};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use spki::der::Encode;
use x509_parser::nom::AsBytes;
use yubikey::{Key, YubiKey};
use yubikey::piv::{sign_data, SlotId};
@@ -15,6 +17,7 @@ pub const SIMPLE_SIG_V1: &str = "v1";
pub const SIMPLE_SIG_SPECIFICATION: &str = "https://openwebstandard.org/simple-sign-file/v1";
pub const HASH_ALGORITHM_SHA256: &str = "sha256";
pub const SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA: &str = "SHA256withECDSA";
pub const CERTIFICATES_SEARCH_URL: &str = "https://hatter.ink/ca/fetch_certificates.json?fingerprint=";
pub struct SignFileRequest {
pub filename: Option<String>,
@@ -173,8 +176,13 @@ impl Command for CommandImpl {
let key = opt_value_result!(key, "Cannot find key in slot: {}", slot_id);
let certificate = key.certificate();
// let tbs_certificate = &certificate.cert.tbs_certificate;
// TODO check certs matches with key in slot
let tbs_certificate = &certificate.cert.tbs_certificate;
let spki_der = opt_result!(tbs_certificate.subject_public_key_info.to_der(), "SPKI to DER failed: {}");
debugging!("Slot public DER: {}", hex::encode(&spki_der));
let spki_der_fingerprint = hex::encode(sha256_bytes(&spki_der));
debugging!("Slot public fingerprint: {}", &spki_der_fingerprint);
let certificates = fetch_certificates(&spki_der_fingerprint)?;
let algorithm_id = opt_result!(
pivutil::get_algorithm_id_by_certificate(certificate), "Get slot key algorithm failed: {}");
debugging!("PIV algorithm: {:?}", algorithm_id);
@@ -202,7 +210,7 @@ impl Command for CommandImpl {
let signature = SimpleSignFileSignature {
algorithm: SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA.to_string(),
signature: format!("{}", base64_encode(&signature_bytes)),
certificates: vec![],
certificates,
};
let simple_sig = SimpleSignFile {
specification: SIMPLE_SIG_SPECIFICATION.to_string(),
@@ -220,6 +228,34 @@ impl Command for CommandImpl {
}
}
#[derive(Deserialize)]
struct FetchCertificateResponseData {
pub certificates: Vec<String>,
}
#[derive(Deserialize)]
struct FetchCertificateResponse {
pub status: i32,
pub message: String,
pub data: Option<FetchCertificateResponseData>,
}
fn fetch_certificates(fingerprint: &str) -> XResult<Vec<String>> {
let url = format!("{}{}", CERTIFICATES_SEARCH_URL, fingerprint);
let certificates_response = opt_result!( reqwest::blocking::get(&url), "Fetch certificates failed: {}");
let certificates_response_bytes = opt_result!(certificates_response.bytes(), "Fetch certificates failed: {}");
let response = opt_result!(
serde_json::from_slice::<FetchCertificateResponse>(certificates_response_bytes.as_bytes()),
"Parse fetch certificates response failed: {}");
if response.status != 200 {
return simple_error!("Fetch certificates failed, status: {}, message: {}", response.status, response.message);
}
match response.data {
None => simple_error!("Fetch certificates failed, empty."),
Some(data) => Ok(data.certificates),
}
}
fn find_key(yk: &mut YubiKey, slot_id: &SlotId) -> XResult<Option<Key>> {
match Key::list(yk) {
Err(e) => warning!("List keys failed: {}", e),