feat: sign-file works
This commit is contained in:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user