Lab 06: Signed Envelopes with Replay Protection
Goal
Implement cryptographic signatures and replay attack prevention.
Requirements
1. Signed Envelope
pub struct SignedEnvelope {
pub payload: Vec<u8>,
pub sender_did: String,
pub sequence: u64, // For replay protection
pub signature: Vec<u8>, // Ed25519 signature
}
impl SignedEnvelope {
pub fn sign(payload: Vec<u8>, keypair: &Keypair, sequence: u64) -> Self {
let message = [&payload[..], &sequence.to_le_bytes()].concat();
let signature = keypair.sign(&message);
SignedEnvelope {
payload,
sender_did: did_from_pubkey(keypair.public()),
sequence,
signature: signature.to_bytes().to_vec(),
}
}
pub fn verify(&self, public_key: &PublicKey) -> Result<(), VerifyError> {
let message = [&self.payload[..], &self.sequence.to_le_bytes()].concat();
let signature = Signature::from_bytes(&self.signature)?;
public_key.verify(&message, &signature)
.map_err(|_| VerifyError::InvalidSignature)
}
}
2. Replay Guard
pub struct ReplayGuard {
last_seen: HashMap<String, u64>, // DID → highest sequence
}
impl ReplayGuard {
pub fn check(&mut self, envelope: &SignedEnvelope) -> Result<(), ReplayError> {
let last = self.last_seen.get(&envelope.sender_did).unwrap_or(&0);
if envelope.sequence <= *last {
return Err(ReplayError::StaleSequence {
sender: envelope.sender_did.clone(),
received: envelope.sequence,
expected: *last + 1,
});
}
self.last_seen.insert(envelope.sender_did.clone(), envelope.sequence);
Ok(())
}
}
Tests to Write
Test 1: Valid Signature Verifies
#[test]
fn test_valid_signature() {
let keypair = Keypair::generate();
let payload = b"hello world".to_vec();
let envelope = SignedEnvelope::sign(payload, &keypair, 1);
assert!(envelope.verify(keypair.public()).is_ok());
}
Test 2: Tampered Payload Fails
#[test]
fn test_tampered_payload_fails() {
let keypair = Keypair::generate();
let payload = b"hello world".to_vec();
let mut envelope = SignedEnvelope::sign(payload, &keypair, 1);
envelope.payload = b"goodbye world".to_vec(); // Tamper!
assert!(envelope.verify(keypair.public()).is_err());
}
Test 3: Wrong Key Fails
#[test]
fn test_wrong_key_fails() {
let keypair1 = Keypair::generate();
let keypair2 = Keypair::generate();
let envelope = SignedEnvelope::sign(b"test".to_vec(), &keypair1, 1);
assert!(envelope.verify(keypair2.public()).is_err());
}
Test 4: Replay Attack Caught
#[test]
fn test_replay_attack_caught() {
let keypair = Keypair::generate();
let mut guard = ReplayGuard::new();
let env1 = SignedEnvelope::sign(b"msg1".to_vec(), &keypair, 1);
let env2 = SignedEnvelope::sign(b"msg2".to_vec(), &keypair, 2);
let env1_replay = env1.clone();
assert!(guard.check(&env1).is_ok());
assert!(guard.check(&env2).is_ok());
assert!(guard.check(&env1_replay).is_err()); // Replay caught!
}
Test 5: Out-of-Order Rejected
#[test]
fn test_out_of_order_rejected() {
let keypair = Keypair::generate();
let mut guard = ReplayGuard::new();
let env1 = SignedEnvelope::sign(b"msg1".to_vec(), &keypair, 1);
let env3 = SignedEnvelope::sign(b"msg3".to_vec(), &keypair, 3);
let env2 = SignedEnvelope::sign(b"msg2".to_vec(), &keypair, 2);
assert!(guard.check(&env1).is_ok());
assert!(guard.check(&env3).is_ok()); // Gap allowed (may arrive later)
assert!(guard.check(&env2).is_err()); // Old sequence rejected
}
Done When
- Valid signatures verify correctly
- Tampered messages fail verification
- Wrong key fails verification
- Replay attacks caught by ReplayGuard
- Out-of-order messages rejected
- All tests pass
Resources
icn-net/src/envelope.rs(SignedEnvelope)icn-identity/(Ed25519 keypairs)ed25519-dalekcrate docs