feat: update script-sign-rs
This commit is contained in:
@@ -90,11 +90,11 @@
|
|||||||
},
|
},
|
||||||
"script-sign.rs": {
|
"script-sign.rs": {
|
||||||
"script_name": "script-sign.rs",
|
"script_name": "script-sign.rs",
|
||||||
"script_length": 16506,
|
"script_length": 4457,
|
||||||
"script_sha256": "d8dc82553a5300ecee3364ed04596163295139f8b18e0d1a95b89ea6540ebfe5",
|
"script_sha256": "9b688c4446a13d0d0013220a41984fdb4288c99bc5d8c80b641ec7ac04f7a6c0",
|
||||||
"script_full_url": "https://git.hatter.ink/rust-scripts/scriptbase/raw/branch/main/script-sign-rs/src/main.rs",
|
"script_full_url": "https://git.hatter.ink/rust-scripts/scriptbase/raw/branch/main/script-sign-rs/src/main.rs",
|
||||||
"publish_time": 1737272563311,
|
"publish_time": 1737272563311,
|
||||||
"update_time": 1737272563311
|
"update_time": 1737481447995
|
||||||
},
|
},
|
||||||
"sync.rs": {
|
"sync.rs": {
|
||||||
"script_name": "sync.rs",
|
"script_name": "sync.rs",
|
||||||
|
|||||||
@@ -51,8 +51,8 @@
|
|||||||
},
|
},
|
||||||
"script-sign-rs": {
|
"script-sign-rs": {
|
||||||
"script_name": "script-sign-rs",
|
"script_name": "script-sign-rs",
|
||||||
"script_length": 16506,
|
"script_length": 4457,
|
||||||
"script_sha256": "d8dc82553a5300ecee3364ed04596163295139f8b18e0d1a95b89ea6540ebfe5"
|
"script_sha256": "9b688c4446a13d0d0013220a41984fdb4288c99bc5d8c80b641ec7ac04f7a6c0"
|
||||||
},
|
},
|
||||||
"sync-rs": {
|
"sync-rs": {
|
||||||
"script_name": "sync-rs",
|
"script_name": "sync-rs",
|
||||||
|
|||||||
14
script-sign-rs/Cargo.lock
generated
14
script-sign-rs/Cargo.lock
generated
@@ -644,11 +644,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "script-sign-rs"
|
name = "script-sign"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "187af8146515de8a5cde0828910084742ebd4e8e5898a51aea83eeb3099d482e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"clap",
|
|
||||||
"digest",
|
"digest",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -662,6 +663,15 @@ dependencies = [
|
|||||||
"simpledateformat",
|
"simpledateformat",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "script-sign-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"rust_util",
|
||||||
|
"script-sign",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sec1"
|
name = "sec1"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
|||||||
@@ -4,16 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.1"
|
|
||||||
clap = { version = "4.5.23", features = ["derive"] }
|
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"
|
rust_util = "0.6.47"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
script-sign = "0.1"
|
||||||
serde_json = "1.0.134"
|
|
||||||
sha2 = "0.10.8"
|
|
||||||
simpledateformat = "0.1.4"
|
|
||||||
|
|||||||
@@ -2,43 +2,17 @@
|
|||||||
|
|
||||||
//! ```cargo
|
//! ```cargo
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! base64 = "0.22.1"
|
|
||||||
//! clap = { version = "4.5.23", features = ["derive"] }
|
//! 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"
|
//! rust_util = "0.6.47"
|
||||||
//! serde = { version = "1.0.217", features = ["derive"] }
|
//! script-sign = "0.1"
|
||||||
//! 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 clap::Parser;
|
||||||
use digest::Digest;
|
use rust_util::{failure, failure_and_exit, information, success, warning};
|
||||||
use ecdsa::Signature;
|
use script_sign::{KeyMap, Script};
|
||||||
use ecdsa::VerifyingKey;
|
|
||||||
use p256::ecdsa::signature::hazmat::PrehashVerifier;
|
|
||||||
use p256::NistP256;
|
|
||||||
use p384::NistP384;
|
|
||||||
use regex::Regex;
|
|
||||||
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;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const SIGNATURE_PREFIX: &str = "// @SCRIPT-SIGNATURE-";
|
|
||||||
|
|
||||||
/// Script signing tool
|
/// Script signing tool
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None, bin_name = "script-sign.rs")]
|
#[command(version, about, long_about = None, bin_name = "script-sign.rs")]
|
||||||
@@ -62,7 +36,7 @@ fn main() {
|
|||||||
failure_and_exit!("Argument --verify or --sign must assigned.");
|
failure_and_exit!("Argument --verify or --sign must assigned.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_map = KeyMap::default().unwrap();
|
let key_map = KeyMap::system().unwrap();
|
||||||
let total_scripts = args.scripts.len();
|
let total_scripts = args.scripts.len();
|
||||||
for (i, script_path) in args.scripts.iter().enumerate() {
|
for (i, script_path) in args.scripts.iter().enumerate() {
|
||||||
information!(
|
information!(
|
||||||
@@ -159,338 +133,3 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
enum ScriptSignatureAlgorithm {
|
|
||||||
RS256,
|
|
||||||
ES256,
|
|
||||||
ES384,
|
|
||||||
ES521,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct ScriptSignature {
|
|
||||||
key_id: String,
|
|
||||||
algorithm: ScriptSignatureAlgorithm,
|
|
||||||
time: String,
|
|
||||||
signature: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Script {
|
|
||||||
content_lines: Vec<String>,
|
|
||||||
signature: Option<ScriptSignature>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct KeyMap {
|
|
||||||
key_map: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[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 signing_keys = r##"
|
|
||||||
{
|
|
||||||
"yk-r1": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0"
|
|
||||||
}
|
|
||||||
"##;
|
|
||||||
let key_map: HashMap<String, String> = serde_json::from_str(signing_keys).unwrap();
|
|
||||||
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();
|
|
||||||
Ok(match upper_algo.as_str() {
|
|
||||||
"RS256" => Self::RS256,
|
|
||||||
"ES256" => Self::ES256,
|
|
||||||
"ES384" => Self::ES384,
|
|
||||||
"ES521" => Self::ES521,
|
|
||||||
_ => return simple_error!("Not valid algorithm: {}", algo),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
ScriptSignatureAlgorithm::RS256 => "RS256",
|
|
||||||
ScriptSignatureAlgorithm::ES256 => "ES256",
|
|
||||||
ScriptSignatureAlgorithm::ES384 => "ES384",
|
|
||||||
ScriptSignatureAlgorithm::ES521 => "ES521",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScriptSignature {
|
|
||||||
fn parse(script_signature_line: &str) -> XResult<ScriptSignature> {
|
|
||||||
// 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, time, signature]) = script_signature_v1_captures.extract();
|
|
||||||
|
|
||||||
let signature = opt_result!(
|
|
||||||
standard_base64.decode(signature),
|
|
||||||
"Parse script signature failed, decode signature failed: {}"
|
|
||||||
);
|
|
||||||
Ok(ScriptSignature {
|
|
||||||
key_id: key_id.to_string(),
|
|
||||||
algorithm: ScriptSignatureAlgorithm::try_from(algorithm)?,
|
|
||||||
time: time.to_string(),
|
|
||||||
signature,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_string(&self) -> String {
|
|
||||||
let mut s = String::with_capacity(245);
|
|
||||||
s.push_str(SIGNATURE_PREFIX);
|
|
||||||
s.push_str("V1: ");
|
|
||||||
s.push_str(&self.key_id);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Script {
|
|
||||||
fn parse(script: &str) -> XResult<Script> {
|
|
||||||
let lines = script.lines().collect::<Vec<_>>();
|
|
||||||
let last_non_empty_line = lines.iter().rev().skip_while(|ln| ln.is_empty()).next();
|
|
||||||
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<_>>();
|
|
||||||
Ok(Script {
|
|
||||||
content_lines: final_lines.iter().map(ToString::to_string).collect(),
|
|
||||||
signature: Some(script_signature),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Ok(Script {
|
|
||||||
content_lines: lines.iter().map(ToString::to_string).collect(),
|
|
||||||
signature: None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_string(&self) -> String {
|
|
||||||
let mut joined_content_liens = self.content_lines.join("\n");
|
|
||||||
match &self.signature {
|
|
||||||
None => joined_content_liens,
|
|
||||||
Some(signature) => {
|
|
||||||
if joined_content_liens.ends_with("\n\n") {
|
|
||||||
// SKIP add \n
|
|
||||||
} else if joined_content_liens.ends_with("\n") {
|
|
||||||
joined_content_liens.push('\n');
|
|
||||||
} else {
|
|
||||||
joined_content_liens.push_str("\n\n");
|
|
||||||
}
|
|
||||||
joined_content_liens.push_str(&signature.as_string());
|
|
||||||
joined_content_liens.push('\n');
|
|
||||||
joined_content_liens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(&signature.time);
|
|
||||||
match signature.algorithm {
|
|
||||||
ScriptSignatureAlgorithm::ES256 => {
|
|
||||||
match 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 (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],
|
|
||||||
);
|
|
||||||
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,
|
|
||||||
time,
|
|
||||||
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 {
|
|
||||||
let trimed_ln = ln.trim();
|
|
||||||
if !trimed_ln.is_empty() {
|
|
||||||
normalized_content_lines.push(trimed_ln.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
normalized_content_lines
|
|
||||||
}
|
|
||||||
|
|
||||||
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(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.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());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_script_parse_01() {
|
|
||||||
let script = Script::parse("test script").unwrap();
|
|
||||||
assert_eq!(1, script.content_lines.len());
|
|
||||||
assert!(script.signature.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_script_parse_02() {
|
|
||||||
let script =
|
|
||||||
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!(
|
|
||||||
"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);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user