242 lines
6.0 KiB
Markdown
242 lines
6.0 KiB
Markdown
# Job Signature Authentication
|
|
|
|
## Overview
|
|
|
|
The job model now includes cryptographic signature verification using secp256k1 to ensure that only authorized signatories can execute jobs.
|
|
|
|
## Changes Made
|
|
|
|
### 1. Job Model Updates (`src/job.rs`)
|
|
|
|
**New Fields:**
|
|
- `signatures: Vec<JobSignature>` - Signatures from authorized signatories (includes public keys)
|
|
|
|
**New Types:**
|
|
```rust
|
|
pub struct JobSignature {
|
|
pub public_key: String, // Hex-encoded secp256k1 public key
|
|
pub signature: String, // Hex-encoded secp256k1 signature
|
|
}
|
|
```
|
|
|
|
**New Methods:**
|
|
- `canonical_representation()` - Creates deterministic string for signing
|
|
- `verify_signatures()` - Verifies all signatures are cryptographically valid
|
|
- `signatories()` - Returns list of public keys from signatures
|
|
|
|
**New Errors:**
|
|
- `SignatureVerificationFailed` - Signature validation failed
|
|
- `Unauthorized` - Missing or invalid signatories
|
|
|
|
### 2. Signature Verification Process
|
|
|
|
1. **Canonical Representation**: Job data is serialized deterministically (excluding signatures)
|
|
2. **SHA-256 Hash**: Canonical representation is hashed
|
|
3. **secp256k1 Verification**: Each signature is verified against its public key
|
|
4. **Authorization**: Signatories are derived from the signatures themselves
|
|
|
|
### 3. Supervisor Integration (`supervisor/src/mycelium.rs`)
|
|
|
|
The `job.run` endpoint now:
|
|
1. Deserializes the job from JSON
|
|
2. Calls `job.verify_signatures()`
|
|
3. Logs successful verification with signatory list
|
|
4. Queues job only if verification passes
|
|
5. Returns error if verification fails
|
|
|
|
### 4. Dependencies
|
|
|
|
**Added to `runner_rust/Cargo.toml`:**
|
|
```toml
|
|
secp256k1 = { version = "0.28", features = ["recovery", "rand"], optional = true }
|
|
sha2 = { version = "0.10", optional = true }
|
|
hex = { version = "0.4", optional = true }
|
|
rand = { version = "0.8", optional = true }
|
|
|
|
[features]
|
|
crypto = ["secp256k1", "sha2", "hex", "rand"]
|
|
```
|
|
|
|
## Usage Example
|
|
|
|
See `examples/sign_job.rs` for a complete example:
|
|
|
|
```bash
|
|
cargo run --example sign_job --features crypto
|
|
```
|
|
|
|
### Creating a Signed Job
|
|
|
|
```rust
|
|
use runner_rust::job::{Job, JobSignature};
|
|
use secp256k1::{Secp256k1, SecretKey, Message};
|
|
use sha2::{Sha256, Digest};
|
|
use rand::rngs::OsRng;
|
|
|
|
// 1. Generate keypairs
|
|
let secp = Secp256k1::new();
|
|
let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
|
|
let pubkey_hex = hex::encode(public_key.serialize());
|
|
|
|
// 2. Create job
|
|
let mut job = Job::new(
|
|
"alice".to_string(),
|
|
"shared_context".to_string(),
|
|
"print('Hello!')".to_string(),
|
|
"runner1".to_string(),
|
|
"rhai".to_string(),
|
|
);
|
|
|
|
// 3. Sign the job
|
|
let canonical = job.canonical_representation();
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(canonical.as_bytes());
|
|
let hash = hasher.finalize();
|
|
let message = Message::from_digest_slice(&hash)?;
|
|
let signature = secp.sign_ecdsa(&message, &secret_key);
|
|
|
|
job.signatures = vec![JobSignature {
|
|
public_key: pubkey_hex,
|
|
signature: hex::encode(signature.serialize_compact()),
|
|
}];
|
|
|
|
// 4. Verify signatures
|
|
job.verify_signatures()?;
|
|
|
|
// 5. Send to supervisor
|
|
let job_json = serde_json::to_string(&job)?;
|
|
// POST to supervisor's job.run endpoint
|
|
```
|
|
|
|
## Security Features
|
|
|
|
### Multi-Party Authorization
|
|
- Jobs can have multiple signatories
|
|
- Each signature proves authorization from that public key
|
|
- Useful for multi-sig workflows and collaborative execution
|
|
|
|
### Tamper Detection
|
|
- Any modification to job data invalidates signatures
|
|
- Canonical representation ensures deterministic signing
|
|
- SHA-256 hash prevents collision attacks
|
|
|
|
### Public Key Cryptography
|
|
- secp256k1 (same as Bitcoin/Ethereum)
|
|
- 256-bit security level
|
|
- Compact signatures (64 bytes)
|
|
|
|
## API Changes
|
|
|
|
### Job Structure (JSON)
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"caller_id": "alice",
|
|
"context_id": "shared_context",
|
|
"payload": "print('Hello!')",
|
|
"runner": "runner1",
|
|
"executor": "rhai",
|
|
"timeout": 300,
|
|
"env_vars": {},
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"updated_at": "2024-01-01T00:00:00Z",
|
|
"signatures": [
|
|
{
|
|
"public_key": "03a1b2c3d4e5f6...",
|
|
"signature": "304402201234..."
|
|
},
|
|
{
|
|
"public_key": "02f1e2d3c4b5a6...",
|
|
"signature": "30440220abcd..."
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Supervisor job.run Endpoint
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"method": "job.run",
|
|
"params": [{
|
|
"secret": "admin_secret",
|
|
"job": { /* job with signatures */ }
|
|
}]
|
|
}
|
|
```
|
|
|
|
**Success Response:**
|
|
```json
|
|
{
|
|
"status": "job_queued",
|
|
"job_id": "uuid"
|
|
}
|
|
```
|
|
|
|
**Error Response:**
|
|
```json
|
|
{
|
|
"error": "signature verification failed: Invalid signature"
|
|
}
|
|
```
|
|
|
|
**Note:** Signatories can be retrieved from the job using `job.signatories()` which extracts public keys from the signatures.
|
|
|
|
## Migration Guide
|
|
|
|
### For Existing Jobs
|
|
|
|
Old jobs without signatures will fail verification. To migrate:
|
|
|
|
1. **Add crypto feature** to your build
|
|
2. **Generate keypairs** for all job creators
|
|
3. **Sign all jobs** before submission (signatures include public keys)
|
|
|
|
### Backward Compatibility
|
|
|
|
To temporarily disable signature verification:
|
|
|
|
```toml
|
|
# In Cargo.toml, remove crypto from default features
|
|
[features]
|
|
default = ["calendar", "finance"] # Remove "crypto"
|
|
```
|
|
|
|
This will use the no-op implementation that logs a warning.
|
|
|
|
## Testing
|
|
|
|
The example includes comprehensive tests:
|
|
|
|
1. ✅ Valid signatures from all signatories
|
|
2. ✅ Missing signature detection
|
|
3. ✅ Wrong signature detection
|
|
4. ✅ Unauthorized signer detection
|
|
|
|
Run tests:
|
|
```bash
|
|
cargo test --features crypto
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
1. **Key Management**: Implement secure key storage
|
|
2. **Key Distribution**: PKI or key exchange protocol
|
|
3. **Revocation**: Handle compromised keys
|
|
4. **Audit Trail**: Log all signature verifications
|
|
5. **Time-based Signatures**: Add expiration timestamps
|
|
|
|
## References
|
|
|
|
- [secp256k1 Documentation](https://docs.rs/secp256k1/)
|
|
- [SHA-256 Specification](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf)
|
|
- [Bitcoin Signatures](https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
|
|
|
|
---
|
|
|
|
**Implementation Status:** ✅ Complete
|
|
**Feature Flag:** `crypto` (enabled by default)
|
|
**Security Level:** Production-ready
|