flux_verify_api/
verify_middleware.rs1use std::collections::HashSet;
2use tracing::{info, warn, error};
3use crate::signing::{self, Signature, SigningError};
4
5pub struct VerificationMiddleware {
7 trusted_keys: HashSet<[u8; 32]>,
9 strict: bool,
11}
12
13#[derive(Debug)]
15pub struct VerificationResult {
16 pub allowed: bool,
17 pub reason: String,
18 pub key_index: Option<usize>,
19}
20
21impl VerificationMiddleware {
22 pub fn new(trusted_keys: Vec<[u8; 32]>) -> Self {
24 assert!(!trusted_keys.is_empty(), "at least one trusted key required");
25 Self {
26 trusted_keys: trusted_keys.into_iter().collect(),
27 strict: true,
28 }
29 }
30
31 pub fn with_strict(mut self, strict: bool) -> Self {
33 self.strict = strict;
34 self
35 }
36
37 pub fn add_trusted_key(&mut self, public_key: [u8; 32]) {
39 info!("adding trusted public key: {:?}", hex::encode(public_key));
40 self.trusted_keys.insert(public_key);
41 }
42
43 pub fn remove_trusted_key(&mut self, public_key: &[u8; 32]) {
45 info!("removing trusted public key: {:?}", hex::encode(public_key));
46 self.trusted_keys.remove(public_key);
47 }
48
49 pub fn trusted_key_count(&self) -> usize {
51 self.trusted_keys.len()
52 }
53
54 pub fn verify(&self, bytecode: &[u8], signature: &Signature) -> VerificationResult {
59 let fingerprint_hex = hex::encode(signature.fingerprint);
60 info!(
61 fingerprint = %fingerprint_hex,
62 timestamp = signature.timestamp,
63 bytecode_len = bytecode.len(),
64 "verification attempt started"
65 );
66
67 for (idx, pk) in self.trusted_keys.iter().enumerate() {
69 match signing::verify_bytecode(bytecode, signature, pk) {
70 Ok(()) => {
71 info!(
72 fingerprint = %fingerprint_hex,
73 key_index = idx,
74 "bytecode signature verified"
75 );
76 return VerificationResult {
77 allowed: true,
78 reason: "signature verified".into(),
79 key_index: Some(idx),
80 };
81 }
82 Err(SigningError::FingerprintMismatch) => {
83 error!(
84 fingerprint = %fingerprint_hex,
85 "fingerprint mismatch — bytecode was tampered with"
86 );
87 return VerificationResult {
88 allowed: false,
89 reason: "fingerprint mismatch: bytecode was tampered with".into(),
90 key_index: None,
91 };
92 }
93 Err(SigningError::VerificationFailed) => {
94 continue;
96 }
97 Err(e) => {
98 warn!(error = %e, "verification error");
99 continue;
100 }
101 }
102 }
103
104 warn!(
105 fingerprint = %fingerprint_hex,
106 "no trusted key matched the signature"
107 );
108 VerificationResult {
109 allowed: false,
110 reason: "no trusted key matched the signature".into(),
111 key_index: None,
112 }
113 }
114
115 pub fn verify_or_reject(&self, bytecode: &[u8], signature: &Signature) -> Result<usize, String> {
117 let result = self.verify(bytecode, signature);
118 if result.allowed {
119 Ok(result.key_index.unwrap())
120 } else {
121 Err(result.reason)
122 }
123 }
124
125 pub fn allow_unsigned(&self) -> bool {
127 if self.strict {
128 warn!("unsigned bytecode rejected (strict mode)");
129 false
130 } else {
131 warn!("unsigned bytecode allowed (non-strict mode — not recommended for production)");
132 true
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use ed25519_dalek::SigningKey;
141
142 fn random_keypair() -> ([u8; 32], [u8; 32]) {
143 let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
144 let pk = signing_key.verifying_key().to_bytes();
145 let sk = signing_key.to_bytes();
146 (sk, pk)
147 }
148
149 #[test]
150 fn verify_with_trusted_key() {
151 let (sk, pk) = random_keypair();
152 let mw = VerificationMiddleware::new(vec![pk]);
153 let bytecode = b"LOAD x 42.0;";
154 let sig = signing::sign_bytecode(bytecode, &sk, Some(9999));
155 let result = mw.verify(bytecode, &sig);
156 assert!(result.allowed);
157 }
158
159 #[test]
160 fn reject_untrusted_key() {
161 let (sk, _) = random_keypair();
162 let (_, untrusted_pk) = random_keypair();
163 let mw = VerificationMiddleware::new(vec![untrusted_pk]);
164 let bytecode = b"LOAD x 42.0;";
165 let sig = signing::sign_bytecode(bytecode, &sk, Some(9999));
166 let result = mw.verify(bytecode, &sig);
167 assert!(!result.allowed);
168 }
169
170 #[test]
171 fn key_rotation_old_key_still_works() {
172 let (sk_old, pk_old) = random_keypair();
173 let (_sk_new, pk_new) = random_keypair();
174 let mut mw = VerificationMiddleware::new(vec![pk_old]);
175 mw.add_trusted_key(pk_new);
176 assert_eq!(mw.trusted_key_count(), 2);
177
178 let bytecode = b"LOAD x 42.0;";
180 let sig = signing::sign_bytecode(bytecode, &sk_old, Some(9999));
181 let result = mw.verify(bytecode, &sig);
182 assert!(result.allowed);
183 }
184
185 #[test]
186 fn strict_mode_rejects_unsigned() {
187 let (_, pk) = random_keypair();
188 let mw = VerificationMiddleware::new(vec![pk]).with_strict(true);
189 assert!(!mw.allow_unsigned());
190 }
191
192 #[test]
193 fn non_strict_mode_allows_unsigned() {
194 let (_, pk) = random_keypair();
195 let mw = VerificationMiddleware::new(vec![pk]).with_strict(false);
196 assert!(mw.allow_unsigned());
197 }
198}