feat: script-sign.rs now works
This commit is contained in:
@@ -11,9 +11,9 @@ use std::ops::Add;
|
||||
use std::process::exit;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
/// Simple program to greet a person
|
||||
/// Current time
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
#[command(version, about, long_about = None, bin_name = "current-time.rs")]
|
||||
struct Args {
|
||||
/// New line
|
||||
#[arg(long)]
|
||||
|
||||
@@ -8,4 +8,5 @@ myip-rs
|
||||
osssendfile-rs
|
||||
post-rs
|
||||
rpm-read-rs
|
||||
script-sign-rs
|
||||
sync-rs
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
},
|
||||
"current-time-rs": {
|
||||
"script_name": "current-time-rs",
|
||||
"script_length": 3291,
|
||||
"script_sha256": "77e3ccc10157807d83357d0b537f8fc08cb4f468e1f2f06ecc250f10ab8e254a"
|
||||
"script_length": 3301,
|
||||
"script_sha256": "51536795cafedf593184a2690f08ee8fc2be9b5dccdd8a52fafa025d66c26e96"
|
||||
},
|
||||
"decrypt-rs": {
|
||||
"script_name": "decrypt-rs",
|
||||
@@ -49,6 +49,11 @@
|
||||
"script_length": 637,
|
||||
"script_sha256": "2338fb99c2803fc36ecdb09ec04eeb9aefb3524f8a68db4875562fdf3c8ba3f8"
|
||||
},
|
||||
"script-sign-rs": {
|
||||
"script_name": "script-sign-rs",
|
||||
"script_length": 16506,
|
||||
"script_sha256": "d8dc82553a5300ecee3364ed04596163295139f8b18e0d1a95b89ea6540ebfe5"
|
||||
},
|
||||
"sync-rs": {
|
||||
"script_name": "sync-rs",
|
||||
"script_length": 1818,
|
||||
|
||||
215
script-sign-rs/Cargo.lock
generated
215
script-sign-rs/Cargo.lock
generated
@@ -11,6 +11,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
@@ -60,6 +75,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
@@ -93,12 +114,41 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
@@ -106,6 +156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -120,6 +171,18 @@ dependencies = [
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
@@ -138,6 +201,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.16"
|
||||
@@ -291,6 +360,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
@@ -315,6 +390,29 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
@@ -327,6 +425,16 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -349,12 +457,33 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
@@ -416,6 +545,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
@@ -524,6 +659,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"simpledateformat",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -583,6 +719,12 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -593,6 +735,16 @@ dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simpledateformat"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d99ace355f41f2391428701c164540ab9b0b644c0aa935f3eefe773dc0072a3e"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
@@ -697,6 +849,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -719,6 +925,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
clap = "4.5.23"
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
digest = "0.10.7"
|
||||
ecdsa = "0.16.9"
|
||||
hex = "0.4.3"
|
||||
@@ -16,3 +16,4 @@ rust_util = "0.6.47"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.134"
|
||||
sha2 = "0.10.8"
|
||||
simpledateformat = "0.1.4"
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
Signature format (last line):
|
||||
|
||||
```plain
|
||||
// @SCRIPT-SIGNATURE-V1: <key-id>.<algotirhm>.<signature-value-in-base64>
|
||||
// @SCRIPT-SIGNATURE-V1: <key-id>.<algotirhm>.<time>.<signature-value-in-base64>
|
||||
```
|
||||
@@ -1,35 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
274
script-sign-rs/src/main.rs
Normal file → Executable file
274
script-sign-rs/src/main.rs
Normal file → Executable file
@@ -1,38 +1,163 @@
|
||||
use crate::ecdsautil::EcdsaAlgorithm;
|
||||
#!/usr/bin/env runrs
|
||||
|
||||
//! ```cargo
|
||||
//! [dependencies]
|
||||
//! base64 = "0.22.1"
|
||||
//! clap = { version = "4.5.23", features = ["derive"] }
|
||||
//! digest = "0.10.7"
|
||||
//! ecdsa = "0.16.9"
|
||||
//! hex = "0.4.3"
|
||||
//! p256 = "0.13.2"
|
||||
//! p384 = "0.13.0"
|
||||
//! regex = "1.11.1"
|
||||
//! rust_util = "0.6.47"
|
||||
//! serde = { version = "1.0.217", features = ["derive"] }
|
||||
//! serde_json = "1.0.134"
|
||||
//! sha2 = "0.10.8"
|
||||
//! simpledateformat = "0.1.4"
|
||||
//! ```
|
||||
|
||||
use base64::engine::general_purpose::STANDARD as standard_base64;
|
||||
use base64::Engine;
|
||||
use clap::Parser;
|
||||
use digest::Digest;
|
||||
use ecdsa::Signature;
|
||||
use ecdsa::VerifyingKey;
|
||||
use p256::ecdsa::signature::hazmat::PrehashVerifier;
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use regex::Regex;
|
||||
use rust_util::{debugging, opt_result, opt_value_result, simple_error, util_cmd, XResult};
|
||||
use rust_util::{
|
||||
debugging, failure, failure_and_exit, information, opt_result, opt_value_result, simple_error,
|
||||
success, util_cmd, warning, XResult,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
mod ecdsautil;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const SIGNATURE_PREFIX: &str = "// @SCRIPT-SIGNATURE-";
|
||||
|
||||
fn main() {
|
||||
let test_file = "test.js";
|
||||
let content = fs::read_to_string(test_file).unwrap();
|
||||
|
||||
let mut script = Script::parse(&content).unwrap();
|
||||
let digest_sha256 = script.normalize_content_lines_and_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 signing tool
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None, bin_name = "script-sign.rs")]
|
||||
struct Args {
|
||||
/// Sign script
|
||||
#[arg(long)]
|
||||
sign: bool,
|
||||
/// Verify script
|
||||
#[arg(long)]
|
||||
verify: bool,
|
||||
/// Force sign script
|
||||
#[arg(long)]
|
||||
force: bool,
|
||||
/// Script file path
|
||||
scripts: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
script.sign().unwrap();
|
||||
fs::write(test_file, script.as_string()).unwrap();
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
if !args.verify && !args.sign {
|
||||
failure_and_exit!("Argument --verify or --sign must assigned.");
|
||||
}
|
||||
|
||||
// println!("{}", script.as_string());
|
||||
let key_map = KeyMap::default().unwrap();
|
||||
let total_scripts = args.scripts.len();
|
||||
for (i, script_path) in args.scripts.iter().enumerate() {
|
||||
information!(
|
||||
"Processing {}/{}: {}",
|
||||
(i + 1),
|
||||
total_scripts,
|
||||
script_path.display()
|
||||
);
|
||||
|
||||
if !script_path.is_file() {
|
||||
warning!("Not a file: {}", script_path.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
let script_content = match fs::read_to_string(script_path) {
|
||||
Ok(script_content) => script_content,
|
||||
Err(e) => {
|
||||
warning!("Read script: {} failed: {}", script_path.display(), e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut script = match Script::parse(&script_content) {
|
||||
Ok(script) => script,
|
||||
Err(e) => {
|
||||
warning!("Read script: {} failed: {}", script_path.display(), e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if args.verify {
|
||||
// VERIFY SCRIPT
|
||||
if let Some(signature) = &script.signature {
|
||||
match script.verify(&key_map) {
|
||||
Ok(true) => {
|
||||
success!(
|
||||
"Verify script success: {}, key ID: {}, sign date: {}",
|
||||
script_path.display(),
|
||||
signature.key_id,
|
||||
signature.time
|
||||
);
|
||||
}
|
||||
Ok(false) => {
|
||||
failure!("Verify script failed: {}", script_path.display());
|
||||
}
|
||||
Err(e) => {
|
||||
warning!("Verify script: {} failed: {}", script_path.display(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warning!("Script is not signed: {}", script_path.display());
|
||||
}
|
||||
} else if args.sign {
|
||||
// SIGN SCRIPT
|
||||
let mut continue_sign = false;
|
||||
if script.signature.is_some() {
|
||||
match script.verify(&key_map) {
|
||||
Ok(true) => {
|
||||
if args.force {
|
||||
continue_sign = true;
|
||||
} else {
|
||||
warning!("Script is singed, force sign script need --force flag.");
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
continue_sign = true;
|
||||
}
|
||||
Err(e) => {
|
||||
warning!("Verify script: {} failed: {}", script_path.display(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue_sign = true;
|
||||
}
|
||||
if continue_sign {
|
||||
information!("Prepare sign script: {}", script_path.display());
|
||||
match script.sign() {
|
||||
Ok(_) => match fs::write(script_path, &script.as_string()) {
|
||||
Ok(_) => {
|
||||
success!("Sign script success: {}", script_path.display());
|
||||
}
|
||||
Err(e) => {
|
||||
failure!("Sign script {} failed: {}", script_path.display(), e);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
failure!("Sign script {} failed: {}", script_path.display(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SHOULD REACH HERE
|
||||
failure_and_exit!("Argument --verify or --sign flag must assigned.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
@@ -47,6 +172,7 @@ enum ScriptSignatureAlgorithm {
|
||||
struct ScriptSignature {
|
||||
key_id: String,
|
||||
algorithm: ScriptSignatureAlgorithm,
|
||||
time: String,
|
||||
signature: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -61,6 +187,7 @@ struct KeyMap {
|
||||
key_map: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct CardEcSignResult {
|
||||
pub algorithm: String,
|
||||
@@ -72,12 +199,12 @@ struct CardEcSignResult {
|
||||
|
||||
impl KeyMap {
|
||||
fn default() -> XResult<Self> {
|
||||
let mut key_map = HashMap::new();
|
||||
key_map.insert(
|
||||
"yk-r1".to_string(),
|
||||
"04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0".to_string()
|
||||
);
|
||||
|
||||
let signing_keys = r##"
|
||||
{
|
||||
"yk-r1": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0"
|
||||
}
|
||||
"##;
|
||||
let key_map: HashMap<String, String> = serde_json::from_str(signing_keys).unwrap();
|
||||
Ok(KeyMap { key_map })
|
||||
}
|
||||
|
||||
@@ -110,14 +237,15 @@ 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*//\s*@SCRIPT-SIGNATURE-V1:\s*([a-zA-Z0-9-_]+)\.([a-zA-Z0-9]+)\.([a-zA-Z0-9-+_/=]+)\s*$"##).unwrap();
|
||||
// e.g. // @SCRIPT-SIGNATURE-V1: <key-id>.<algotirhm>.<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: {}",
|
||||
script_signature_line
|
||||
);
|
||||
let (_, [key_id, algorithm, signature]) = script_signature_v1_captures.extract();
|
||||
let (_, [key_id, algorithm, time, signature]) = script_signature_v1_captures.extract();
|
||||
|
||||
let signature = opt_result!(
|
||||
standard_base64.decode(signature),
|
||||
@@ -126,6 +254,7 @@ impl ScriptSignature {
|
||||
Ok(ScriptSignature {
|
||||
key_id: key_id.to_string(),
|
||||
algorithm: ScriptSignatureAlgorithm::try_from(algorithm)?,
|
||||
time: time.to_string(),
|
||||
signature,
|
||||
})
|
||||
}
|
||||
@@ -138,6 +267,8 @@ impl ScriptSignature {
|
||||
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
|
||||
}
|
||||
@@ -200,10 +331,10 @@ impl Script {
|
||||
Some(key) => key,
|
||||
};
|
||||
let key_bytes = hex::decode(key)?;
|
||||
let digest_sha256 = self.normalize_content_lines_and_sha256();
|
||||
let digest_sha256 = self.normalize_content_lines_and_sha256(&signature.time);
|
||||
match signature.algorithm {
|
||||
ScriptSignatureAlgorithm::ES256 => {
|
||||
match ecdsautil::ecdsaverify(
|
||||
match ecdsaverify(
|
||||
EcdsaAlgorithm::P256,
|
||||
&key_bytes,
|
||||
&digest_sha256,
|
||||
@@ -221,7 +352,8 @@ impl Script {
|
||||
}
|
||||
|
||||
fn sign(&mut self) -> XResult<()> {
|
||||
let digest_sha256_hex = hex::encode(&self.normalize_content_lines_and_sha256());
|
||||
let (time, digest_sha256) = self.normalize_content_lines_and_sha256_with_current_time();
|
||||
let digest_sha256_hex = hex::encode(&digest_sha256);
|
||||
let output = util_cmd::run_command_or_exit(
|
||||
"card-cli",
|
||||
&["piv-ecsign", "--json", "-s", "r1", "-x", &digest_sha256_hex],
|
||||
@@ -234,6 +366,7 @@ impl Script {
|
||||
self.signature = Some(ScriptSignature {
|
||||
key_id: "yk-r1".to_string(),
|
||||
algorithm: ScriptSignatureAlgorithm::ES256,
|
||||
time,
|
||||
signature: hex::decode(&ecsign_result.signed_data_hex)?,
|
||||
});
|
||||
} else {
|
||||
@@ -253,21 +386,81 @@ impl Script {
|
||||
normalized_content_lines
|
||||
}
|
||||
|
||||
fn normalize_content_lines_and_sha256(&self) -> Vec<u8> {
|
||||
fn normalize_content_lines_and_sha256_with_current_time(&self) -> (String, Vec<u8>) {
|
||||
let current_time = current_time();
|
||||
(
|
||||
current_time.clone(),
|
||||
self.normalize_content_lines_and_sha256(¤t_time),
|
||||
)
|
||||
}
|
||||
|
||||
fn normalize_content_lines_and_sha256(&self, current_time: &str) -> Vec<u8> {
|
||||
let normalized_content_lines = self.normalize_content_lines();
|
||||
let joined_normalized_content_lines = normalized_content_lines.join("\n");
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&joined_normalized_content_lines);
|
||||
hasher.update(current_time.as_bytes());
|
||||
hasher.update(joined_normalized_content_lines.as_bytes());
|
||||
hasher.finalize().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn current_time() -> String {
|
||||
simpledateformat::fmt("yyyyMMdd'T'HHmmssz")
|
||||
.unwrap()
|
||||
.format_local_now()
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_script_signature_parse() {
|
||||
let v1 = "// @SCRIPT-SIGNATURE-V1: key-id.RS256.aGVsbG93b3JsZA==";
|
||||
let v1 = "// @SCRIPT-SIGNATURE-V1: key-id.RS256.2025-01-05T20:57:14+08:00.aGVsbG93b3JsZA==";
|
||||
let s = ScriptSignature::parse(v1).unwrap();
|
||||
assert_eq!("key-id", s.key_id);
|
||||
assert_eq!(ScriptSignatureAlgorithm::RS256, s.algorithm);
|
||||
assert_eq!("2025-01-05T20:57:14+08:00", s.time);
|
||||
assert_eq!(b"helloworld".to_vec(), s.signature);
|
||||
|
||||
assert_eq!(v1, s.as_string());
|
||||
@@ -283,18 +476,21 @@ fn test_script_parse_01() {
|
||||
#[test]
|
||||
fn test_script_parse_02() {
|
||||
let script =
|
||||
Script::parse("test script\n\n// @SCRIPT-SIGNATURE-V1: key-id.RS256.aGVsbG93b3JsZA==\n")
|
||||
Script::parse("test script\n\n// @SCRIPT-SIGNATURE-V1: key-id.RS256.2025-01-05T20:57:14+08:00.aGVsbG93b3JsZA==\n")
|
||||
.unwrap();
|
||||
assert_eq!(2, script.content_lines.len());
|
||||
assert_eq!("test script", script.content_lines[0]);
|
||||
assert_eq!("", script.content_lines[1]);
|
||||
let current_time = "2025-01-05T20:57:14+08:00";
|
||||
let digest_sha256 = script.normalize_content_lines_and_sha256(¤t_time);
|
||||
assert_eq!(
|
||||
"Gfvd4W/64/WaSMaztA1nlq81vBLam8gcJokoOlGuGpc=",
|
||||
standard_base64.encode(&script.normalize_content_lines_and_sha256())
|
||||
"sybQ8O5TgRlkQ0i8pNIA6huHvAd5XbVZF+U60WMrdco=",
|
||||
standard_base64.encode(&digest_sha256)
|
||||
);
|
||||
assert!(script.signature.is_some());
|
||||
let s = script.signature.unwrap();
|
||||
assert_eq!("key-id", s.key_id);
|
||||
assert_eq!(ScriptSignatureAlgorithm::RS256, s.algorithm);
|
||||
assert_eq!("2025-01-05T20:57:14+08:00", s.time);
|
||||
assert_eq!(b"helloworld".to_vec(), s.signature);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
console.log('hello world');
|
||||
|
||||
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.MEUCIQDiGI8XkLHLcslt/663ZAIIBVItcRLMRZOJ7kBlP96QfgIgQtOeDbj5Z1+o4UkuXV1Knq0X8DRIOTNkVGmyBsJQ6f4=
|
||||
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20250105T213419+08:00.MEUCIDGpmzkPcGE0yFO/ARKc0DwRD64Qay7vB9k2jQ7xSL8UAiEAynSg+OWp3oSC06GRQFUg/znGY3aGiSq5TztTykAqxKs=
|
||||
|
||||
Reference in New Issue
Block a user