1mod chain;
2
3use std::sync::{Arc, RwLock};
4
5use dashmap::DashMap;
6use ndn_packet::{Data, Name};
7
8use crate::cert_cache::Certificate;
9use crate::cert_fetcher::CertFetcher;
10use crate::trust_schema::SchemaRule;
11use crate::verifier::Verifier;
12use crate::{CertCache, Ed25519Verifier, SafeData, TrustError, TrustSchema, VerifyOutcome};
13
14#[derive(Debug)]
16pub enum ValidationResult {
17 Valid(Box<SafeData>),
19 Invalid(TrustError),
21 Pending,
23}
24
25pub struct Validator {
32 pub(super) schema: Arc<RwLock<TrustSchema>>,
33 pub(super) cert_cache: Arc<CertCache>,
34 pub(super) verifier: Ed25519Verifier,
35 pub(super) max_chain: usize,
36 pub(super) trust_anchors: Arc<DashMap<Arc<Name>, Certificate>>,
38 pub(super) cert_fetcher: Option<Arc<CertFetcher>>,
40}
41
42impl Validator {
43 pub fn new(schema: TrustSchema) -> Self {
45 Self {
46 schema: Arc::new(RwLock::new(schema)),
47 cert_cache: Arc::new(CertCache::new()),
48 verifier: Ed25519Verifier,
49 max_chain: 5,
50 trust_anchors: Arc::new(DashMap::new()),
51 cert_fetcher: None,
52 }
53 }
54
55 pub fn with_chain(
57 schema: TrustSchema,
58 cert_cache: Arc<CertCache>,
59 trust_anchors: Arc<DashMap<Arc<Name>, Certificate>>,
60 cert_fetcher: Option<Arc<CertFetcher>>,
61 max_chain: usize,
62 ) -> Self {
63 Self {
64 schema: Arc::new(RwLock::new(schema)),
65 cert_cache,
66 verifier: Ed25519Verifier,
67 max_chain,
68 trust_anchors,
69 cert_fetcher,
70 }
71 }
72
73 pub fn cert_cache(&self) -> &CertCache {
75 &self.cert_cache
76 }
77
78 pub fn add_trust_anchor(&self, cert: Certificate) {
80 self.cert_cache.insert(cert.clone());
81 self.trust_anchors.insert(Arc::clone(&cert.name), cert);
82 }
83
84 pub fn is_trust_anchor(&self, name: &Name) -> bool {
86 self.trust_anchors.iter().any(|r| r.key().as_ref() == name)
87 }
88
89 pub fn set_schema(&self, schema: TrustSchema) {
95 *self.schema.write().expect("schema RwLock poisoned") = schema;
96 }
97
98 pub fn add_schema_rule(&self, rule: SchemaRule) {
100 self.schema
101 .write()
102 .expect("schema RwLock poisoned")
103 .add_rule(rule);
104 }
105
106 pub fn remove_schema_rule(&self, index: usize) -> Option<SchemaRule> {
110 let mut guard = self.schema.write().expect("schema RwLock poisoned");
111 if index < guard.rules().len() {
112 Some(guard.remove_rule(index))
113 } else {
114 None
115 }
116 }
117
118 pub fn schema_rules_text(&self) -> Vec<(String, String)> {
120 self.schema
121 .read()
122 .expect("schema RwLock poisoned")
123 .rules()
124 .iter()
125 .map(|r| (r.data_pattern.to_string(), r.key_pattern.to_string()))
126 .collect()
127 }
128
129 pub fn schema_snapshot(&self) -> TrustSchema {
131 self.schema.read().expect("schema RwLock poisoned").clone()
132 }
133
134 pub async fn validate(&self, data: &Data) -> ValidationResult {
140 let Some(sig_info) = data.sig_info() else {
141 return ValidationResult::Invalid(TrustError::InvalidSignature);
142 };
143 let Some(key_locator) = &sig_info.key_locator else {
144 return ValidationResult::Invalid(TrustError::InvalidSignature);
145 };
146
147 if !self
148 .schema
149 .read()
150 .expect("schema RwLock poisoned")
151 .allows(&data.name, key_locator)
152 {
153 return ValidationResult::Invalid(TrustError::SchemaMismatch);
154 }
155
156 let Some(cert) = self.cert_cache.get(key_locator) else {
157 return ValidationResult::Pending;
158 };
159
160 if !cert.is_valid_at(now_ns()) {
161 return ValidationResult::Invalid(TrustError::CertNotFound {
162 name: format!("expired or not-yet-valid: {}", key_locator),
163 });
164 }
165
166 match self
167 .verifier
168 .verify(data.signed_region(), data.sig_value(), &cert.public_key)
169 .await
170 {
171 Ok(VerifyOutcome::Valid) => {
172 let safe = SafeData {
173 inner: Data::decode(data.raw().clone()).unwrap(),
174 trust_path: crate::safe_data::TrustPath::CertChain(vec![
175 key_locator.as_ref().clone(),
176 ]),
177 verified_at: now_ns(),
178 };
179 ValidationResult::Valid(Box::new(safe))
180 }
181 Ok(VerifyOutcome::Invalid) => ValidationResult::Invalid(TrustError::InvalidSignature),
182 Err(e) => ValidationResult::Invalid(e),
183 }
184 }
185}
186
187pub(crate) fn now_ns() -> u64 {
188 use std::time::{SystemTime, UNIX_EPOCH};
189 SystemTime::now()
190 .duration_since(UNIX_EPOCH)
191 .map(|d| d.as_nanos() as u64)
192 .unwrap_or(0)
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use crate::cert_cache::Certificate;
199 use crate::signer::{Ed25519Signer, Signer};
200 use crate::trust_schema::{NamePattern, PatternComponent, SchemaRule};
201 use bytes::Bytes;
202 use ndn_packet::{Name, NameComponent};
203 use std::sync::Arc;
204
205 fn comp(s: &'static str) -> NameComponent {
206 NameComponent::generic(Bytes::from_static(s.as_bytes()))
207 }
208 fn name1(c: &'static str) -> Name {
209 Name::from_components([comp(c)])
210 }
211
212 async fn make_signed_data(
217 signer: &Ed25519Signer,
218 data_comp: &'static str,
219 key_comp: &'static str,
220 ) -> Bytes {
221 use ndn_tlv::TlvWriter;
222
223 let nc = {
224 let mut w = TlvWriter::new();
225 w.write_tlv(0x08, data_comp.as_bytes());
226 w.finish()
227 };
228 let name_tlv = {
229 let mut w = TlvWriter::new();
230 w.write_tlv(0x07, &nc);
231 w.finish()
232 };
233
234 let knc = {
235 let mut w = TlvWriter::new();
236 w.write_tlv(0x08, key_comp.as_bytes());
237 w.finish()
238 };
239 let kname_tlv = {
240 let mut w = TlvWriter::new();
241 w.write_tlv(0x07, &knc);
242 w.finish()
243 };
244 let kloc_tlv = {
245 let mut w = TlvWriter::new();
246 w.write_tlv(0x1c, &kname_tlv);
247 w.finish()
248 };
249 let stype_tlv = {
250 let mut w = TlvWriter::new();
251 w.write_tlv(0x1b, &[7u8]);
252 w.finish()
253 };
254 let sinfo_inner: Vec<u8> = stype_tlv.iter().chain(kloc_tlv.iter()).copied().collect();
255 let sinfo_tlv = {
256 let mut w = TlvWriter::new();
257 w.write_tlv(0x16, &sinfo_inner);
258 w.finish()
259 };
260
261 let signed_region: Vec<u8> = name_tlv.iter().chain(sinfo_tlv.iter()).copied().collect();
262 let sig = signer.sign(&signed_region).await.unwrap();
263
264 let sval_tlv = {
265 let mut w = TlvWriter::new();
266 w.write_tlv(0x17, &sig);
267 w.finish()
268 };
269 let inner: Vec<u8> = signed_region
270 .iter()
271 .chain(sval_tlv.iter())
272 .copied()
273 .collect();
274 let mut w = TlvWriter::new();
275 w.write_tlv(0x06, &inner);
276 w.finish()
277 }
278
279 fn open_schema(data_comp: &'static str, key_comp: &'static str) -> TrustSchema {
280 let mut schema = TrustSchema::new();
281 schema.add_rule(SchemaRule {
282 data_pattern: NamePattern(vec![PatternComponent::Literal(comp(data_comp))]),
283 key_pattern: NamePattern(vec![PatternComponent::Literal(comp(key_comp))]),
284 });
285 schema
286 }
287
288 #[tokio::test]
289 async fn no_sig_info_returns_invalid() {
290 use ndn_tlv::TlvWriter;
292 let nc = {
293 let mut w = TlvWriter::new();
294 w.write_tlv(0x08, b"test");
295 w.finish()
296 };
297 let name_tlv = {
298 let mut w = TlvWriter::new();
299 w.write_tlv(0x07, &nc);
300 w.finish()
301 };
302 let inner: Vec<u8> = name_tlv.to_vec();
303 let data_bytes = {
304 let mut w = TlvWriter::new();
305 w.write_tlv(0x06, &inner);
306 w.finish()
307 };
308 let data = Data::decode(data_bytes).unwrap();
309
310 let validator = Validator::new(TrustSchema::new());
311 assert!(matches!(
312 validator.validate(&data).await,
313 ValidationResult::Invalid(_)
314 ));
315 }
316
317 #[tokio::test]
318 async fn schema_mismatch_returns_invalid() {
319 let seed = [10u8; 32];
320 let key_name = name1("key");
321 let signer = Ed25519Signer::from_seed(&seed, key_name.clone());
322 let data_bytes = make_signed_data(&signer, "data", "key").await;
323 let data = Data::decode(data_bytes).unwrap();
324
325 let mut schema = TrustSchema::new();
327 schema.add_rule(SchemaRule {
328 data_pattern: NamePattern(vec![PatternComponent::Literal(comp("other"))]),
329 key_pattern: NamePattern(vec![PatternComponent::Literal(comp("key"))]),
330 });
331
332 let validator = Validator::new(schema);
333 assert!(matches!(
334 validator.validate(&data).await,
335 ValidationResult::Invalid(_)
336 ));
337 }
338
339 #[tokio::test]
340 async fn no_cert_returns_pending() {
341 let seed = [11u8; 32];
342 let key_name = name1("key");
343 let signer = Ed25519Signer::from_seed(&seed, key_name);
344 let data_bytes = make_signed_data(&signer, "data", "key").await;
345 let data = Data::decode(data_bytes).unwrap();
346
347 let validator = Validator::new(open_schema("data", "key"));
348 assert!(matches!(
349 validator.validate(&data).await,
350 ValidationResult::Pending
351 ));
352 }
353
354 #[tokio::test]
355 async fn valid_signature_returns_valid() {
356 let seed = [12u8; 32];
357 let key_name = name1("key");
358 let signer = Ed25519Signer::from_seed(&seed, key_name.clone());
359 let data_bytes = make_signed_data(&signer, "data", "key").await;
360 let data = Data::decode(data_bytes).unwrap();
361
362 let vk_bytes = ed25519_dalek::SigningKey::from_bytes(&seed)
363 .verifying_key()
364 .to_bytes();
365 let cert = Certificate {
366 name: Arc::new(key_name),
367 public_key: Bytes::copy_from_slice(&vk_bytes),
368 valid_from: 0,
369 valid_until: u64::MAX,
370 issuer: None,
371 signed_region: None,
372 sig_value: None,
373 };
374 let validator = Validator::new(open_schema("data", "key"));
375 validator.cert_cache().insert(cert);
376
377 assert!(matches!(
378 validator.validate(&data).await,
379 ValidationResult::Valid(_)
380 ));
381 }
382
383 #[tokio::test]
384 async fn expired_cert_returns_invalid() {
385 let seed = [15u8; 32];
386 let key_name = name1("key");
387 let signer = Ed25519Signer::from_seed(&seed, key_name.clone());
388 let data_bytes = make_signed_data(&signer, "data", "key").await;
389 let data = Data::decode(data_bytes).unwrap();
390
391 let vk_bytes = ed25519_dalek::SigningKey::from_bytes(&seed)
392 .verifying_key()
393 .to_bytes();
394 let cert = Certificate {
395 name: Arc::new(key_name),
396 public_key: Bytes::copy_from_slice(&vk_bytes),
397 valid_from: 0,
398 valid_until: 1, issuer: None,
400 signed_region: None,
401 sig_value: None,
402 };
403 let validator = Validator::new(open_schema("data", "key"));
404 validator.cert_cache().insert(cert);
405
406 assert!(matches!(
407 validator.validate(&data).await,
408 ValidationResult::Invalid(_)
409 ));
410 }
411
412 #[tokio::test]
413 async fn invalid_signature_returns_invalid() {
414 let seed_a = [13u8; 32];
416 let seed_b = [14u8; 32];
417 let key_name = name1("key");
418 let signer = Ed25519Signer::from_seed(&seed_a, key_name.clone());
419 let data_bytes = make_signed_data(&signer, "data", "key").await;
420 let data = Data::decode(data_bytes).unwrap();
421
422 let wrong_pk = ed25519_dalek::SigningKey::from_bytes(&seed_b)
423 .verifying_key()
424 .to_bytes();
425 let cert = Certificate {
426 name: Arc::new(key_name),
427 public_key: Bytes::copy_from_slice(&wrong_pk),
428 valid_from: 0,
429 valid_until: u64::MAX,
430 issuer: None,
431 signed_region: None,
432 sig_value: None,
433 };
434 let validator = Validator::new(open_schema("data", "key"));
435 validator.cert_cache().insert(cert);
436
437 assert!(matches!(
438 validator.validate(&data).await,
439 ValidationResult::Invalid(_)
440 ));
441 }
442}