/// 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> { 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(()) }