Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5a80ff9870
|
53
src/lib.rs
53
src/lib.rs
@@ -6,7 +6,8 @@ mod util;
|
|||||||
pub use crate::keymap::KeyMap;
|
pub use crate::keymap::KeyMap;
|
||||||
use crate::sign::{ecdsaverify, EcdsaAlgorithm};
|
use crate::sign::{ecdsaverify, EcdsaAlgorithm};
|
||||||
use crate::signature::{
|
use crate::signature::{
|
||||||
CardEcSignResult, ScriptSignature, ScriptSignatureAlgorithm, SIGNATURE_PREFIX,
|
CardEcSignResult, ScriptSignature, ScriptSignatureAlgorithm, ScriptSignatureVersion,
|
||||||
|
SIGNATURE_PREFIX,
|
||||||
};
|
};
|
||||||
use crate::util::current_time;
|
use crate::util::current_time;
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
@@ -135,7 +136,48 @@ impl Script {
|
|||||||
None => return simple_error!("Sign key id: {} not found", &signature.key_id),
|
None => return simple_error!("Sign key id: {} not found", &signature.key_id),
|
||||||
Some(key) => key,
|
Some(key) => key,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)?;
|
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);
|
let digest_sha256 = self.normalize_content_lines_and_sha256(&signature.time);
|
||||||
match signature.algorithm {
|
match signature.algorithm {
|
||||||
ScriptSignatureAlgorithm::ES256 => {
|
ScriptSignatureAlgorithm::ES256 => {
|
||||||
@@ -169,7 +211,9 @@ impl Script {
|
|||||||
);
|
);
|
||||||
if ecsign_result.algorithm == "ecdsa_p256_with_sha256" {
|
if ecsign_result.algorithm == "ecdsa_p256_with_sha256" {
|
||||||
self.signature = Some(ScriptSignature {
|
self.signature = Some(ScriptSignature {
|
||||||
|
ver: ScriptSignatureVersion::V1,
|
||||||
key_id: "yk-r1".to_string(),
|
key_id: "yk-r1".to_string(),
|
||||||
|
embed_signing_key: None,
|
||||||
algorithm: ScriptSignatureAlgorithm::ES256,
|
algorithm: ScriptSignatureAlgorithm::ES256,
|
||||||
time,
|
time,
|
||||||
signature: hex::decode(&ecsign_result.signed_data_hex)?,
|
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=="##)
|
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8Wn6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A=="##)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let script_str = script.as_string();
|
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.");
|
console.log("Hello world.");
|
||||||
|
|
||||||
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8W
|
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8W
|
||||||
// n6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A==
|
// n6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A==
|
||||||
"##, script_str);
|
"##,
|
||||||
|
script_str
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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_PREFIX: &str = "// @SCRIPT-SIGNATURE-";
|
||||||
pub const SIGNATURE_V1: &str = "V1";
|
pub const SIGNATURE_V1: &str = "V1";
|
||||||
|
pub const SIGNATURE_V2: &str = "V2";
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum ScriptSignatureAlgorithm {
|
pub enum ScriptSignatureAlgorithm {
|
||||||
@@ -15,6 +16,12 @@ pub enum ScriptSignatureAlgorithm {
|
|||||||
ES521,
|
ES521,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum ScriptSignatureVersion {
|
||||||
|
V1,
|
||||||
|
V2,
|
||||||
|
}
|
||||||
|
|
||||||
impl ScriptSignatureAlgorithm {
|
impl ScriptSignatureAlgorithm {
|
||||||
pub fn try_from(algo: &str) -> XResult<Self> {
|
pub fn try_from(algo: &str) -> XResult<Self> {
|
||||||
let upper_algo = algo.to_uppercase();
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct ScriptSignature {
|
pub struct ScriptSignature {
|
||||||
|
pub ver: ScriptSignatureVersion,
|
||||||
pub key_id: String,
|
pub key_id: String,
|
||||||
|
pub embed_signing_key: Option<EmbedSigningKey>,
|
||||||
pub algorithm: ScriptSignatureAlgorithm,
|
pub algorithm: ScriptSignatureAlgorithm,
|
||||||
pub time: String,
|
pub time: String,
|
||||||
pub signature: Vec<u8>,
|
pub signature: Vec<u8>,
|
||||||
@@ -57,22 +74,75 @@ pub struct CardEcSignResult {
|
|||||||
|
|
||||||
impl ScriptSignature {
|
impl ScriptSignature {
|
||||||
pub fn parse(script_signature_line: &str) -> XResult<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(
|
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();
|
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!(
|
let script_signature_v1_captures = opt_value_result!(
|
||||||
script_signature_v1_regex.captures(script_signature_line),
|
script_signature_v1_regex.captures(script_signature_line),
|
||||||
"Parse script signature failed: {}",
|
"Parse script signature v1 failed: {}",
|
||||||
script_signature_line
|
script_signature_line
|
||||||
);
|
);
|
||||||
let (_, [key_id, algorithm, time, signature]) = script_signature_v1_captures.extract();
|
let (_, [key_id, algorithm, time, signature]) = script_signature_v1_captures.extract();
|
||||||
|
|
||||||
let signature = opt_result!(
|
let signature = opt_result!(
|
||||||
standard_base64.decode(signature),
|
standard_base64.decode(signature),
|
||||||
"Parse script signature failed, decode signature failed: {}"
|
"Parse script signature v1 failed, decode signature failed: {}"
|
||||||
);
|
);
|
||||||
Ok(ScriptSignature {
|
Ok(ScriptSignature {
|
||||||
|
ver: ScriptSignatureVersion::V1,
|
||||||
key_id: key_id.to_string(),
|
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)?,
|
algorithm: ScriptSignatureAlgorithm::try_from(algorithm)?,
|
||||||
time: time.to_string(),
|
time: time.to_string(),
|
||||||
signature,
|
signature,
|
||||||
@@ -95,6 +165,13 @@ impl ScriptSignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> String {
|
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);
|
let mut s = String::with_capacity(245);
|
||||||
s.push_str(SIGNATURE_PREFIX);
|
s.push_str(SIGNATURE_PREFIX);
|
||||||
s.push_str(SIGNATURE_V1);
|
s.push_str(SIGNATURE_V1);
|
||||||
@@ -108,6 +185,33 @@ impl ScriptSignature {
|
|||||||
s.push_str(&standard_base64.encode(&self.signature));
|
s.push_str(&standard_base64.encode(&self.signature));
|
||||||
s
|
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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user