feat: poc works

This commit is contained in:
2025-01-05 18:49:34 +08:00
parent d26a138df6
commit cbcc25a49f
5 changed files with 428 additions and 14 deletions

View File

@@ -0,0 +1,35 @@
use ecdsa::VerifyingKey;
use p256::NistP256;
use p256::ecdsa::signature::hazmat::PrehashVerifier;
use p384::NistP384;
use ecdsa::Signature;
use rust_util::{opt_result, simple_error, XResult};
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum EcdsaAlgorithm {
P256,
P384,
}
macro_rules! ecdsa_verify_signature {
($algo: tt, $pk_point: tt, $prehash: tt, $signature: tt) => ({
let verifying_key: VerifyingKey<$algo> = opt_result!(VerifyingKey::<$algo>::from_sec1_bytes($pk_point), "Parse public key failed: {}");
let sign = if let Ok(signature) = Signature::from_der($signature) {
signature
} else if let Ok(signature) = Signature::from_slice($signature) {
signature
} else {
return simple_error!("Parse signature failed: {}", hex::encode($signature));
};
opt_result!(verifying_key.verify_prehash($prehash, &sign), "Verify signature failed: {}");
})
}
pub fn ecdsaverify(algo: EcdsaAlgorithm, pk_point: &[u8], prehash: &[u8], signature: &[u8]) -> XResult<()> {
match algo {
EcdsaAlgorithm::P256 => ecdsa_verify_signature!(NistP256, pk_point, prehash, signature),
EcdsaAlgorithm::P384 => ecdsa_verify_signature!(NistP384, pk_point, prehash, signature),
}
Ok(())
}

View File

@@ -1,11 +1,18 @@
use base64::engine::general_purpose as general_base64;
use crate::ecdsautil::EcdsaAlgorithm;
use base64::engine::general_purpose::STANDARD as standard_base64;
use base64::Engine;
use digest::Digest;
use regex::Regex;
use rust_util::{opt_result, opt_value_result, simple_error, XResult};
use rust_util::{
debugging, opt_result, opt_value_result, simple_error, util_cmd, XResult,
};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use std::collections::HashMap;
use std::fs;
mod ecdsautil;
const SIGNATURE_PREFIX: &str = "// @SCRIPT-SIGNATURE-";
fn main() {
@@ -15,19 +22,16 @@ fn main() {
let mut script = Script::parse(&content).unwrap();
let digest_sha256 = script.normalize_content_lines_and_sha256();
println!("{}", general_base64::STANDARD.encode(&digest_sha256));
println!("{}", standard_base64.encode(&digest_sha256));
println!("{}", hex::encode(&digest_sha256));
if script.signature.is_some() {
println!("File is signed.");
println!("Verify: {:?}", script.verify(&KeyMap::default().unwrap()));
return;
}
script.signature = Some(ScriptSignature{
key_id: "yk-r1".to_string(),
algorithm: ScriptSignatureAlgorithm::ES256,
signature: general_base64::STANDARD.decode("MEUCIQCRbIPTC7jTMiRYSLF+u4gt7JDcDO4m4Y5pqbk3NjTvogIgKYgbkeGXl6XTiChN7dWJZ04GmLeEnKF9xnnPN3o7W8w=").unwrap(),
});
script.sign().unwrap();
fs::write(test_file, script.as_string()).unwrap();
// println!("{}", script.as_string());
@@ -54,6 +58,36 @@ struct Script {
signature: Option<ScriptSignature>,
}
#[derive(Debug, Serialize, Deserialize)]
struct KeyMap {
key_map: HashMap<String, String>,
}
#[derive(Debug, Deserialize)]
struct CardEcSignResult {
pub algorithm: String,
pub hash_hex: String,
pub signed_data_base64: String,
pub signed_data_hex: String,
pub slot: String,
}
impl KeyMap {
fn default() -> XResult<Self> {
let mut key_map = HashMap::new();
key_map.insert(
"yk-r1".to_string(),
"04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0".to_string()
);
Ok(KeyMap { key_map })
}
fn find(&self, key_id: &str) -> Option<&String> {
self.key_map.get(key_id)
}
}
impl ScriptSignatureAlgorithm {
fn try_from(algo: &str) -> XResult<Self> {
let upper_algo = algo.to_uppercase();
@@ -79,7 +113,7 @@ impl ScriptSignatureAlgorithm {
impl ScriptSignature {
fn parse(script_signature_line: &str) -> XResult<ScriptSignature> {
// e.g. // @SCRIPT-SIGNATURE-V1: <key-id>.<algotirhm>.<signature-value-in-base64>
let script_signature_v1_regex = Regex::new(r##"^//\s*@SCRIPT-SIGNATURE-V1:\s*([a-zA-Z0-9-_]+)\.([a-zA-Z0-9]+)\.([a-zA-Z0-9-+_/=]+)$"##).unwrap();
let script_signature_v1_regex = Regex::new(r##"^\s*//\s*@SCRIPT-SIGNATURE-V1:\s*([a-zA-Z0-9-_]+)\.([a-zA-Z0-9]+)\.([a-zA-Z0-9-+_/=]+)\s*$"##).unwrap();
let script_signature_v1_captures = opt_value_result!(
script_signature_v1_regex.captures(script_signature_line),
"Parse script signature failed: {}",
@@ -88,7 +122,7 @@ impl ScriptSignature {
let (_, [key_id, algorithm, signature]) = script_signature_v1_captures.extract();
let signature = opt_result!(
general_base64::STANDARD.decode(signature),
standard_base64.decode(signature),
"Parse script signature failed, decode signature failed: {}"
);
Ok(ScriptSignature {
@@ -106,7 +140,7 @@ impl ScriptSignature {
s.push('.');
s.push_str(self.algorithm.as_str());
s.push('.');
s.push_str(&general_base64::STANDARD.encode(&self.signature));
s.push_str(&standard_base64.encode(&self.signature));
s
}
}
@@ -142,10 +176,64 @@ impl Script {
fn as_string(&self) -> String {
match &self.signature {
None => self.content_lines.join("\n"),
Some(signature) => self.content_lines.join("\n") + "\n" + &signature.as_string(),
Some(signature) => {
self.content_lines.join("\n") + "\n\n" + &signature.as_string() + "\n"
}
}
}
fn verify(&self, key_map: &KeyMap) -> XResult<bool> {
let signature = match &self.signature {
None => return simple_error!("Script is not signed."),
Some(signature) => signature,
};
let key = match key_map.find(&signature.key_id) {
None => return simple_error!("Sign key id: {} not found", &signature.key_id),
Some(key) => key,
};
let key_bytes = hex::decode(key)?;
let digest_sha256 = self.normalize_content_lines_and_sha256();
match signature.algorithm {
ScriptSignatureAlgorithm::ES256 => {
match ecdsautil::ecdsaverify(
EcdsaAlgorithm::P256,
&key_bytes,
&digest_sha256,
&signature.signature,
) {
Ok(_) => Ok(true),
Err(e) => {
debugging!("Verify ecdsa signature failed: {}", e);
Ok(false)
}
}
}
_ => simple_error!("Not supported algorithm: {:?}", signature.algorithm),
}
}
fn sign(&mut self) -> XResult<()> {
let digest_sha256_hex = hex::encode(&self.normalize_content_lines_and_sha256());
let output = util_cmd::run_command_or_exit(
"card-cli",
&["piv-ecsign", "--json", "-s", "r1", "-x", &digest_sha256_hex],
);
let ecsign_result: CardEcSignResult = opt_result!(
serde_json::from_slice(&output.stdout),
"Parse card piv-ecsign failed: {}"
);
if ecsign_result.algorithm == "ecdsa_p256_with_sha256" {
self.signature = Some(ScriptSignature {
key_id: "yk-r1".to_string(),
algorithm: ScriptSignatureAlgorithm::ES256,
signature: hex::decode(&ecsign_result.signed_data_hex)?,
});
} else {
return simple_error!("Not supported algorithm: {}", ecsign_result.algorithm);
}
Ok(())
}
fn normalize_content_lines(&self) -> Vec<String> {
let mut normalized_content_lines = Vec::with_capacity(self.content_lines.len());
for ln in &self.content_lines {
@@ -194,7 +282,7 @@ fn test_script_parse_02() {
assert_eq!("", script.content_lines[1]);
assert_eq!(
"Gfvd4W/64/WaSMaztA1nlq81vBLam8gcJokoOlGuGpc=",
general_base64::STANDARD.encode(&script.normalize_content_lines_and_sha256())
standard_base64.encode(&script.normalize_content_lines_and_sha256())
);
assert!(script.signature.is_some());
let s = script.signature.unwrap();