Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5a80ff9870
|
55
src/lib.rs
55
src/lib.rs
@@ -6,7 +6,8 @@ mod util;
|
||||
pub use crate::keymap::KeyMap;
|
||||
use crate::sign::{ecdsaverify, EcdsaAlgorithm};
|
||||
use crate::signature::{
|
||||
CardEcSignResult, ScriptSignature, ScriptSignatureAlgorithm, SIGNATURE_PREFIX,
|
||||
CardEcSignResult, ScriptSignature, ScriptSignatureAlgorithm, ScriptSignatureVersion,
|
||||
SIGNATURE_PREFIX,
|
||||
};
|
||||
use crate::util::current_time;
|
||||
use digest::Digest;
|
||||
@@ -135,7 +136,48 @@ impl Script {
|
||||
None => return simple_error!("Sign key id: {} not found", &signature.key_id),
|
||||
Some(key) => key,
|
||||
};
|
||||
let key_bytes = hex::decode(&key.public_key_point_hex)?;
|
||||
|
||||
let mut verify_public_key = key.public_key_point_hex.clone();
|
||||
if ScriptSignatureVersion::V2 == signature.ver {
|
||||
match &signature.embed_signing_key {
|
||||
Some(embed_signing_key) => {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(embed_signing_key.time.as_bytes());
|
||||
hasher.update(&embed_signing_key.public_key);
|
||||
let embed_digest_sha256 = hasher.finalize().to_vec();
|
||||
let key_bytes = hex::decode(&key.public_key_point_hex)?;
|
||||
match embed_signing_key.algorithm {
|
||||
ScriptSignatureAlgorithm::ES256 => {
|
||||
match ecdsaverify(
|
||||
EcdsaAlgorithm::P256,
|
||||
&key_bytes,
|
||||
&embed_digest_sha256,
|
||||
&embed_signing_key.signature,
|
||||
) {
|
||||
Ok(_) => {
|
||||
verify_public_key = hex::encode(&embed_signing_key.public_key);
|
||||
}
|
||||
Err(e) => {
|
||||
debugging!("Verify embed ecdsa signature failed: {}", e);
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return simple_error!(
|
||||
"Not supported algorithm: {:?}",
|
||||
signature.algorithm
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return simple_error!("Embed signing key not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let key_bytes = hex::decode(&verify_public_key)?;
|
||||
let digest_sha256 = self.normalize_content_lines_and_sha256(&signature.time);
|
||||
match signature.algorithm {
|
||||
ScriptSignatureAlgorithm::ES256 => {
|
||||
@@ -169,7 +211,9 @@ impl Script {
|
||||
);
|
||||
if ecsign_result.algorithm == "ecdsa_p256_with_sha256" {
|
||||
self.signature = Some(ScriptSignature {
|
||||
ver: ScriptSignatureVersion::V1,
|
||||
key_id: "yk-r1".to_string(),
|
||||
embed_signing_key: None,
|
||||
algorithm: ScriptSignatureAlgorithm::ES256,
|
||||
time,
|
||||
signature: hex::decode(&ecsign_result.signed_data_hex)?,
|
||||
@@ -262,13 +306,16 @@ console.log("Hello world.");
|
||||
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8Wn6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A=="##)
|
||||
.unwrap();
|
||||
let script_str = script.as_string();
|
||||
assert_eq!(r##"#!/usr/bin/env -S deno run --allow-env
|
||||
assert_eq!(
|
||||
r##"#!/usr/bin/env -S deno run --allow-env
|
||||
|
||||
console.log("Hello world.");
|
||||
|
||||
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8W
|
||||
// n6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A==
|
||||
"##, script_str);
|
||||
"##,
|
||||
script_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
110
src/signature.rs
110
src/signature.rs
@@ -6,6 +6,7 @@ use serde::Deserialize;
|
||||
|
||||
pub const SIGNATURE_PREFIX: &str = "// @SCRIPT-SIGNATURE-";
|
||||
pub const SIGNATURE_V1: &str = "V1";
|
||||
pub const SIGNATURE_V2: &str = "V2";
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum ScriptSignatureAlgorithm {
|
||||
@@ -15,6 +16,12 @@ pub enum ScriptSignatureAlgorithm {
|
||||
ES521,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum ScriptSignatureVersion {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
impl ScriptSignatureAlgorithm {
|
||||
pub fn try_from(algo: &str) -> XResult<Self> {
|
||||
let upper_algo = algo.to_uppercase();
|
||||
@@ -37,9 +44,19 @@ impl ScriptSignatureAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EmbedSigningKey {
|
||||
pub algorithm: ScriptSignatureAlgorithm,
|
||||
pub time: String,
|
||||
pub public_key: Vec<u8>,
|
||||
pub signature: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScriptSignature {
|
||||
pub ver: ScriptSignatureVersion,
|
||||
pub key_id: String,
|
||||
pub embed_signing_key: Option<EmbedSigningKey>,
|
||||
pub algorithm: ScriptSignatureAlgorithm,
|
||||
pub time: String,
|
||||
pub signature: Vec<u8>,
|
||||
@@ -57,22 +74,75 @@ pub struct CardEcSignResult {
|
||||
|
||||
impl ScriptSignature {
|
||||
pub fn parse(script_signature_line: &str) -> XResult<ScriptSignature> {
|
||||
// e.g. // @SCRIPT-SIGNATURE-V1: <key-id>.<algotirhm>.<time>.<signature-value-in-base64>
|
||||
if script_signature_line.contains("SCRIPT-SIGNATURE-V1") {
|
||||
Self::parse_v1(script_signature_line)
|
||||
} else if script_signature_line.contains("SCRIPT-SIGNATURE-V2") {
|
||||
Self::parse_v2(script_signature_line)
|
||||
} else {
|
||||
simple_error!("Invalid script signature: {}", script_signature_line)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_v1(script_signature_line: &str) -> XResult<ScriptSignature> {
|
||||
// e.g. // @SCRIPT-SIGNATURE-V1: <key-id>.<algorithm>.<time>.<signature-value-in-base64>
|
||||
let script_signature_v1_regex = Regex::new(
|
||||
r##"^\s*//\s*@SCRIPT-SIGNATURE-V1:\s*([a-zA-Z0-9-_]+)\.([a-zA-Z0-9]+)\.([0-9a-zA-Z\-+:]+)\.([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: {}",
|
||||
"Parse script signature v1 failed: {}",
|
||||
script_signature_line
|
||||
);
|
||||
let (_, [key_id, algorithm, time, signature]) = script_signature_v1_captures.extract();
|
||||
|
||||
let signature = opt_result!(
|
||||
standard_base64.decode(signature),
|
||||
"Parse script signature failed, decode signature failed: {}"
|
||||
"Parse script signature v1 failed, decode signature failed: {}"
|
||||
);
|
||||
Ok(ScriptSignature {
|
||||
ver: ScriptSignatureVersion::V1,
|
||||
key_id: key_id.to_string(),
|
||||
embed_signing_key: None,
|
||||
algorithm: ScriptSignatureAlgorithm::try_from(algorithm)?,
|
||||
time: time.to_string(),
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_v2(script_signature_line: &str) -> XResult<ScriptSignature> {
|
||||
// e.g. // @SCRIPT-SIGNATURE-V2: <key-id>.[<algorithm>.<time>.<public-key>.<signature-value-in-base64>].<algorithm>.<time>.<signature-value-in-base64>
|
||||
let script_signature_v2_regex = Regex::new(
|
||||
r##"^\s*//\s*@SCRIPT-SIGNATURE-V2:\s*([a-zA-Z0-9-_]+)\.\[([a-zA-Z0-9]+)\.([0-9a-zA-Z\-+:]+)\.([a-zA-Z0-9\-+_/=]+).([a-zA-Z0-9\-+_/=]+)\]\.([a-zA-Z0-9]+)\.([0-9a-zA-Z\-+:]+)\.([a-zA-Z0-9\-+_/=]+)\s*$"##).unwrap();
|
||||
let script_signature_v2_captures = opt_value_result!(
|
||||
script_signature_v2_regex.captures(script_signature_line),
|
||||
"Parse script signature v2 failed: {}",
|
||||
script_signature_line
|
||||
);
|
||||
let (
|
||||
_,
|
||||
[key_id, embed_algorithm, embed_time, embed_public_key, embed_signature, algorithm, time, signature],
|
||||
) = script_signature_v2_captures.extract();
|
||||
|
||||
let embed_public_key = opt_result!(
|
||||
hex::decode(embed_public_key),
|
||||
"Parse script signature v2 failed, decode embed public key failed: {}"
|
||||
);
|
||||
let embed_signature = opt_result!(
|
||||
standard_base64.decode(embed_signature),
|
||||
"Parse script signature v2 failed, decode embed signature failed: {}"
|
||||
);
|
||||
let signature = opt_result!(
|
||||
standard_base64.decode(signature),
|
||||
"Parse script signature v2 failed, decode signature failed: {}"
|
||||
);
|
||||
Ok(ScriptSignature {
|
||||
ver: ScriptSignatureVersion::V2,
|
||||
key_id: key_id.to_string(),
|
||||
embed_signing_key: Some(EmbedSigningKey {
|
||||
algorithm: ScriptSignatureAlgorithm::try_from(embed_algorithm)?,
|
||||
time: embed_time.to_string(),
|
||||
public_key: embed_public_key,
|
||||
signature: embed_signature,
|
||||
}),
|
||||
algorithm: ScriptSignatureAlgorithm::try_from(algorithm)?,
|
||||
time: time.to_string(),
|
||||
signature,
|
||||
@@ -95,6 +165,13 @@ impl ScriptSignature {
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
match self.ver {
|
||||
ScriptSignatureVersion::V1 => self.as_string_v1(),
|
||||
ScriptSignatureVersion::V2 => self.as_string_v2(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_string_v1(&self) -> String {
|
||||
let mut s = String::with_capacity(245);
|
||||
s.push_str(SIGNATURE_PREFIX);
|
||||
s.push_str(SIGNATURE_V1);
|
||||
@@ -108,6 +185,33 @@ impl ScriptSignature {
|
||||
s.push_str(&standard_base64.encode(&self.signature));
|
||||
s
|
||||
}
|
||||
|
||||
fn as_string_v2(&self) -> String {
|
||||
let mut s = String::with_capacity(245);
|
||||
s.push_str(SIGNATURE_PREFIX);
|
||||
s.push_str(SIGNATURE_V2);
|
||||
s.push_str(": ");
|
||||
s.push_str(&self.key_id);
|
||||
s.push('.');
|
||||
s.push('[');
|
||||
if let Some(embed_signing_key) = &self.embed_signing_key {
|
||||
s.push_str(embed_signing_key.algorithm.as_str());
|
||||
s.push('.');
|
||||
s.push_str(&embed_signing_key.time);
|
||||
s.push('.');
|
||||
s.push_str(&hex::encode(&embed_signing_key.public_key));
|
||||
s.push('.');
|
||||
s.push_str(&standard_base64.encode(&embed_signing_key.signature));
|
||||
}
|
||||
s.push(']');
|
||||
s.push('.');
|
||||
s.push_str(self.algorithm.as_str());
|
||||
s.push('.');
|
||||
s.push_str(&self.time);
|
||||
s.push('.');
|
||||
s.push_str(&standard_base64.encode(&self.signature));
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user