feat: v0.1.1

This commit is contained in:
2025-01-23 00:13:00 +08:00
parent ebcf7b83d6
commit 84f66ae736
4 changed files with 138 additions and 32 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "script-sign"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
description = "Script Sign"

View File

@@ -1,25 +1,35 @@
use rust_util::XResult;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyMeta {
pub public_key_point_hex: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KeyMap {
key_map: HashMap<String, String>,
pub key_map: HashMap<String, KeyMeta>,
}
impl KeyMap {
pub fn system() -> XResult<Self> {
pub fn system() -> Self {
let signing_keys = r##"
{
"yk-r1": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0"
"yk-r1": {
"public_key_point_hex": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0"
}
}
"##;
// unwrap should not happen
let key_map: HashMap<String, String> = serde_json::from_str(signing_keys).unwrap();
Ok(KeyMap { key_map })
let key_map: HashMap<String, KeyMeta> = serde_json::from_str(signing_keys).unwrap();
KeyMap { key_map }
}
pub fn find(&self, key_id: &str) -> Option<&String> {
pub fn from(key_map: HashMap<String, KeyMeta>) -> Self {
Self { key_map }
}
pub fn find(&self, key_id: &str) -> Option<&KeyMeta> {
self.key_map.get(key_id)
}
}

View File

@@ -22,7 +22,7 @@ pub struct Script {
impl Script {
pub fn verify_script_file_with_system_key_map(script_file: &str) -> XResult<bool> {
Self::verify_script_file(script_file, &KeyMap::system()?)
Self::verify_script_file(script_file, &KeyMap::system())
}
pub fn verify_script_file(script_file: &str, key_map: &KeyMap) -> XResult<bool> {
@@ -42,29 +42,60 @@ impl Script {
pub fn parse(script: &str) -> XResult<Script> {
let lines = script.lines().collect::<Vec<_>>();
let last_non_empty_line = lines.iter().rev().find(|ln| !ln.is_empty());
match last_non_empty_line {
Some(last_non_empty_line) if last_non_empty_line.starts_with(SIGNATURE_PREFIX) => {
let script_signature = ScriptSignature::parse(last_non_empty_line)?;
let final_lines = lines
.iter()
.rev()
.skip_while(|ln| ln.is_empty())
.skip(1)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect::<Vec<_>>();
let mut in_signature_section = false;
let mut signature_lines = vec![];
let mut content_lines = vec![];
let mut signature_line = String::new();
let mut push_signature_line = |signature_line: &mut String| {
if !signature_line.is_empty() {
signature_lines.push(signature_line.clone());
signature_line.clear();
}
};
for line in &lines {
if in_signature_section {
if line.starts_with(SIGNATURE_PREFIX) {
push_signature_line(&mut signature_line);
signature_line.push_str(line);
} else if line.starts_with("//") {
signature_line.push_str(line.chars().skip(2).collect::<String>().trim());
} else if !line.trim().is_empty() {
return simple_error!("Bad signature section line, find: '{line}'");
}
} else {
if line.starts_with(SIGNATURE_PREFIX) {
in_signature_section = true;
push_signature_line(&mut signature_line);
signature_line.push_str(line);
} else {
content_lines.push(line.to_string());
}
}
}
push_signature_line(&mut signature_line);
if signature_lines.len() > 1 {
return simple_error!(
"Found {} signatures, only supports one signature.",
signature_lines.len()
);
}
if signature_lines.is_empty() {
Ok(Script {
content_lines: final_lines.iter().map(ToString::to_string).collect(),
content_lines,
signature: None,
})
} else {
let script_signature = ScriptSignature::parse(&signature_lines[0])?;
Ok(Script {
content_lines,
signature: Some(script_signature),
})
}
_ => Ok(Script {
content_lines: lines.iter().map(ToString::to_string).collect(),
signature: None,
}),
}
}
pub fn as_string(&self) -> String {
@@ -79,8 +110,13 @@ impl Script {
} else {
joined_content_liens.push_str("\n\n");
}
joined_content_liens.push_str(&signature.as_string());
let signature_lines = signature.as_string_lines_default_width();
for signature_line in &signature_lines {
joined_content_liens.push_str(&signature_line);
joined_content_liens.push('\n');
}
// joined_content_liens.push_str(&signature.as_string());
// joined_content_liens.push('\n');
joined_content_liens
}
}
@@ -99,7 +135,7 @@ impl Script {
None => return simple_error!("Sign key id: {} not found", &signature.key_id),
Some(key) => key,
};
let key_bytes = hex::decode(key)?;
let key_bytes = hex::decode(&key.public_key_point_hex)?;
let digest_sha256 = self.normalize_content_lines_and_sha256(&signature.time);
match signature.algorithm {
ScriptSignatureAlgorithm::ES256 => {
@@ -203,3 +239,48 @@ fn test_script_parse_02() {
assert_eq!("2025-01-05T20:57:14+08:00", s.time);
assert_eq!(b"helloworld".to_vec(), s.signature);
}
#[test]
fn test_script_parse_03() {
let script =
Script::parse(r##"#!/usr/bin/env -S deno run --allow-env
console.log("Hello world.");
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8Wn6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A=="##)
.unwrap();
assert!(script.verify(&KeyMap::system()).unwrap());
}
#[test]
fn test_script_parse_04() {
let script =
Script::parse(r##"#!/usr/bin/env -S deno run --allow-env
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
console.log("Hello world.");
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu8W
// n6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A==
"##, script_str);
}
#[test]
fn test_script_parse_05() {
let script = Script::parse(
r##"#!/usr/bin/env -S deno run --allow-env
console.log("Hello world.");
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250122T233410+08:00.MEQCIGogDudoVpCVfGiNPu
// 8Wn6YPDtFX5OXC4bKtsN1nw414AiAq+5EVdvOuKAlXdVeeE1d91mKX9TaSTR25jliUx0km6A=="##,
)
.unwrap();
assert!(script.verify(&KeyMap::system()).unwrap());
}

View File

@@ -79,6 +79,21 @@ impl ScriptSignature {
})
}
pub fn as_string_lines_default_width(&self) -> Vec<String> {
self.as_string_lines(80)
}
pub fn as_string_lines(&self, width: usize) -> Vec<String> {
let mut lines = vec![];
let str = self.as_string();
let chars = str.chars().skip(3).collect::<Vec<_>>();
let chunks = chars.chunks(width);
for chunk in chunks {
lines.push(format!("// {}", chunk.iter().collect::<String>()));
}
lines
}
pub fn as_string(&self) -> String {
let mut s = String::with_capacity(245);
s.push_str(SIGNATURE_PREFIX);