154 lines
4.9 KiB
Rust
154 lines
4.9 KiB
Rust
/// Example: Creating and Signing a Job
|
|
///
|
|
/// This example demonstrates how to:
|
|
/// 1. Create a job with signatories
|
|
/// 2. Sign the job with secp256k1 private keys
|
|
/// 3. Verify the signatures
|
|
///
|
|
/// Usage:
|
|
/// ```bash
|
|
/// cargo run --example sign_job --features crypto
|
|
/// ```
|
|
|
|
use runner_rust::job::{Job, JobSignature};
|
|
use secp256k1::{Secp256k1, SecretKey, Message, PublicKey};
|
|
use sha2::{Sha256, Digest};
|
|
use rand::rngs::OsRng;
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
env_logger::init();
|
|
|
|
println!("🔐 Job Signing Example\n");
|
|
println!("======================\n");
|
|
|
|
// Step 1: Generate secp256k1 keypairs for signatories
|
|
println!("Step 1: Generating keypairs for signatories...");
|
|
let secp = Secp256k1::new();
|
|
|
|
let (secret_key1, public_key1) = secp.generate_keypair(&mut OsRng);
|
|
let (secret_key2, public_key2) = secp.generate_keypair(&mut OsRng);
|
|
|
|
let pubkey1_hex = hex::encode(public_key1.serialize());
|
|
let pubkey2_hex = hex::encode(public_key2.serialize());
|
|
|
|
println!(" Signatory 1 public key: {}", pubkey1_hex);
|
|
println!(" Signatory 2 public key: {}\n", pubkey2_hex);
|
|
|
|
// Step 2: Create a job
|
|
println!("Step 2: Creating job...");
|
|
let mut job = Job::new(
|
|
"alice".to_string(),
|
|
"shared_context".to_string(),
|
|
"print('Hello from signed job!')".to_string(),
|
|
"runner1".to_string(),
|
|
"rhai".to_string(),
|
|
);
|
|
|
|
println!(" Job ID: {}\n", job.id);
|
|
|
|
// Step 3: Get canonical representation and hash it
|
|
println!("Step 3: Creating canonical representation...");
|
|
let canonical = job.canonical_representation();
|
|
println!(" Canonical: {}", canonical);
|
|
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(canonical.as_bytes());
|
|
let hash = hasher.finalize();
|
|
let message = Message::from_digest_slice(&hash)?;
|
|
|
|
println!(" Hash: {}\n", hex::encode(hash));
|
|
|
|
// Step 4: Sign with both private keys
|
|
println!("Step 4: Signing job with both signatories...");
|
|
let sig1 = secp.sign_ecdsa(&message, &secret_key1);
|
|
let sig2 = secp.sign_ecdsa(&message, &secret_key2);
|
|
|
|
job.signatures = vec![
|
|
JobSignature {
|
|
public_key: pubkey1_hex.clone(),
|
|
signature: hex::encode(sig1.serialize_compact()),
|
|
},
|
|
JobSignature {
|
|
public_key: pubkey2_hex.clone(),
|
|
signature: hex::encode(sig2.serialize_compact()),
|
|
},
|
|
];
|
|
|
|
println!(" ✓ Signed by signatory 1");
|
|
println!(" ✓ Signed by signatory 2");
|
|
println!(" Signatories: {:?}\n", job.signatories());
|
|
|
|
// Step 5: Verify signatures
|
|
println!("Step 5: Verifying signatures...");
|
|
match job.verify_signatures() {
|
|
Ok(()) => {
|
|
println!(" ✅ All signatures verified successfully!\n");
|
|
}
|
|
Err(e) => {
|
|
println!(" ❌ Signature verification failed: {}\n", e);
|
|
return Err(e.into());
|
|
}
|
|
}
|
|
|
|
// Step 6: Serialize to JSON
|
|
println!("Step 6: Serializing job to JSON...");
|
|
let job_json = serde_json::to_string_pretty(&job)?;
|
|
println!("{}\n", job_json);
|
|
|
|
// Step 7: Test with missing signature
|
|
println!("Step 7: Testing with missing signature...");
|
|
let mut incomplete_job = job.clone();
|
|
incomplete_job.signatures.pop(); // Remove one signature
|
|
|
|
match incomplete_job.verify_signatures() {
|
|
Ok(()) => {
|
|
println!(" ❌ Should have failed!\n");
|
|
}
|
|
Err(e) => {
|
|
println!(" ✅ Correctly rejected: {}\n", e);
|
|
}
|
|
}
|
|
|
|
// Step 8: Test with wrong signature
|
|
println!("Step 8: Testing with wrong signature...");
|
|
let (wrong_secret, _) = secp.generate_keypair(&mut OsRng);
|
|
let wrong_sig = secp.sign_ecdsa(&message, &wrong_secret);
|
|
|
|
let mut tampered_job = job.clone();
|
|
tampered_job.signatures[0].signature = hex::encode(wrong_sig.serialize_compact());
|
|
|
|
match tampered_job.verify_signatures() {
|
|
Ok(()) => {
|
|
println!(" ❌ Should have failed!\n");
|
|
}
|
|
Err(e) => {
|
|
println!(" ✅ Correctly rejected: {}\n", e);
|
|
}
|
|
}
|
|
|
|
// Step 9: Test with unauthorized signer
|
|
println!("Step 9: Testing with unauthorized signer...");
|
|
let (unauthorized_secret, unauthorized_public) = secp.generate_keypair(&mut OsRng);
|
|
let unauthorized_sig = secp.sign_ecdsa(&message, &unauthorized_secret);
|
|
|
|
let mut unauthorized_job = job.clone();
|
|
unauthorized_job.signatures.push(JobSignature {
|
|
public_key: hex::encode(unauthorized_public.serialize()),
|
|
signature: hex::encode(unauthorized_sig.serialize_compact()),
|
|
});
|
|
|
|
match unauthorized_job.verify_signatures() {
|
|
Ok(()) => {
|
|
println!(" ❌ Should have failed!\n");
|
|
}
|
|
Err(e) => {
|
|
println!(" ✅ Correctly rejected: {}\n", e);
|
|
}
|
|
}
|
|
|
|
println!("======================");
|
|
println!("✅ All tests passed!");
|
|
|
|
Ok(())
|
|
}
|