ndn_security/validator/
chain.rs

1use std::collections::HashSet;
2use std::sync::Arc;
3
4use ndn_packet::{Data, Name, SignatureType};
5
6use crate::cert_cache::Certificate;
7use crate::safe_data::TrustPath;
8use crate::verifier::Verifier;
9use crate::{SafeData, TrustError, VerifyOutcome};
10
11use super::{ValidationResult, Validator, now_ns};
12
13impl Validator {
14    /// Validate a Data packet by walking the full certificate chain.
15    ///
16    /// Verifies the Data's signature, then walks up the chain — each
17    /// certificate's signature is verified using the next certificate's
18    /// public key — until a trust anchor is reached. Missing certificates
19    /// are fetched via the `CertFetcher` if configured.
20    pub async fn validate_chain(&self, data: &Data) -> ValidationResult {
21        let Some(sig_info) = data.sig_info() else {
22            return ValidationResult::Invalid(TrustError::InvalidSignature);
23        };
24
25        // DigestSha256 is self-contained: verify SHA-256(signed_region) == sig_value.
26        // No key locator or trust chain is involved.
27        if sig_info.sig_type == SignatureType::DigestSha256 {
28            use sha2::{Digest, Sha256};
29            let hash = Sha256::digest(data.signed_region());
30            if hash.as_slice() == data.sig_value() {
31                let safe = SafeData {
32                    inner: Data::decode(data.raw().clone())
33                        .expect("already decoded, re-decode cannot fail"),
34                    trust_path: TrustPath::DigestSha256,
35                    verified_at: now_ns(),
36                };
37                return ValidationResult::Valid(Box::new(safe));
38            }
39            return ValidationResult::Invalid(TrustError::InvalidSignature);
40        }
41
42        // Resolve the KeyLocator. NDN allows two forms: a Name pointing at
43        // the signer's certificate, or a KeyDigest carrying SHA-256 of the
44        // public key. The Name form is the common path; for the KeyDigest
45        // form we look the cert up in the local cache by its key digest
46        // (the cert must be pre-loaded — there's no way to fetch a cert
47        // over the network when only its key digest is known).
48        let first_key: Arc<Name> = if let Some(name) = &sig_info.key_locator {
49            Arc::clone(name)
50        } else if let Some(digest) = &sig_info.key_digest {
51            match self.cert_cache.get_by_key_digest(digest) {
52                Some(cert) => Arc::clone(&cert.name),
53                None => return ValidationResult::Invalid(TrustError::InvalidSignature),
54            }
55        } else {
56            return ValidationResult::Invalid(TrustError::InvalidSignature);
57        };
58
59        if !self
60            .schema
61            .read()
62            .expect("schema RwLock poisoned")
63            .allows(&data.name, &first_key)
64        {
65            return ValidationResult::Invalid(TrustError::SchemaMismatch);
66        }
67
68        let now = now_ns();
69        let mut chain_names: Vec<Name> = Vec::new();
70        let mut seen: HashSet<Arc<Name>> = HashSet::new();
71
72        // Current entity to verify: starts with the Data packet itself.
73        let mut current_signed_region: &[u8] = data.signed_region();
74        let mut current_sig_value: &[u8] = data.sig_value();
75        let mut current_key_name: Arc<Name> = first_key;
76
77        // Owned buffers for intermediate cert signed regions / sig values.
78        let mut owned_signed_region: bytes::Bytes;
79        let mut owned_sig_value: bytes::Bytes;
80
81        for _depth in 0..self.max_chain {
82            if !seen.insert(Arc::clone(&current_key_name)) {
83                return ValidationResult::Invalid(TrustError::ChainCycle {
84                    name: current_key_name.to_string(),
85                });
86            }
87
88            // Trust anchor terminates the chain.
89            if let Some(anchor) = self.trust_anchors.get(&current_key_name) {
90                if !anchor.is_valid_at(now) {
91                    return ValidationResult::Invalid(TrustError::CertNotFound {
92                        name: format!("expired trust anchor: {}", current_key_name),
93                    });
94                }
95                return match self
96                    .verifier
97                    .verify(current_signed_region, current_sig_value, &anchor.public_key)
98                    .await
99                {
100                    Ok(VerifyOutcome::Valid) => {
101                        chain_names.push(current_key_name.as_ref().clone());
102                        let safe = SafeData {
103                            inner: Data::decode(data.raw().clone()).unwrap(),
104                            trust_path: crate::safe_data::TrustPath::CertChain(chain_names),
105                            verified_at: now,
106                        };
107                        ValidationResult::Valid(Box::new(safe))
108                    }
109                    Ok(VerifyOutcome::Invalid) => {
110                        ValidationResult::Invalid(TrustError::InvalidSignature)
111                    }
112                    Err(e) => ValidationResult::Invalid(e),
113                };
114            }
115
116            // Fetch or look up the certificate.
117            let cert = match self.resolve_cert(&current_key_name).await {
118                Some(c) => c,
119                None => return ValidationResult::Pending,
120            };
121
122            if !cert.is_valid_at(now) {
123                return ValidationResult::Invalid(TrustError::CertNotFound {
124                    name: format!("expired or not-yet-valid: {}", current_key_name),
125                });
126            }
127
128            // Verify the current entity's signature with this cert's public key.
129            match self
130                .verifier
131                .verify(current_signed_region, current_sig_value, &cert.public_key)
132                .await
133            {
134                Ok(VerifyOutcome::Valid) => {}
135                Ok(VerifyOutcome::Invalid) => {
136                    return ValidationResult::Invalid(TrustError::InvalidSignature);
137                }
138                Err(e) => return ValidationResult::Invalid(e),
139            }
140
141            chain_names.push(current_key_name.as_ref().clone());
142
143            // Move up: verify this cert's own signature next.
144            let Some(issuer) = &cert.issuer else {
145                return ValidationResult::Invalid(TrustError::CertNotFound {
146                    name: format!("cert has no issuer: {}", cert.name),
147                });
148            };
149            let Some(sr) = &cert.signed_region else {
150                return ValidationResult::Invalid(TrustError::CertNotFound {
151                    name: format!("cert missing signed region: {}", cert.name),
152                });
153            };
154            let Some(sv) = &cert.sig_value else {
155                return ValidationResult::Invalid(TrustError::CertNotFound {
156                    name: format!("cert missing sig value: {}", cert.name),
157                });
158            };
159
160            owned_signed_region = sr.clone();
161            owned_sig_value = sv.clone();
162            current_signed_region = &owned_signed_region;
163            current_sig_value = &owned_sig_value;
164            current_key_name = Arc::clone(issuer);
165        }
166
167        ValidationResult::Invalid(TrustError::ChainTooDeep {
168            limit: self.max_chain,
169        })
170    }
171
172    /// Try to resolve a certificate from cache or by fetching.
173    async fn resolve_cert(&self, name: &Arc<Name>) -> Option<Certificate> {
174        if let Some(cert) = self.cert_cache.get(name) {
175            return Some(cert);
176        }
177        if let Some(fetcher) = &self.cert_fetcher {
178            fetcher.fetch(name).await.ok()
179        } else {
180            None
181        }
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use crate::TrustSchema;
189    use crate::cert_cache::Certificate;
190    use crate::signer::{Ed25519Signer, Signer};
191    use crate::trust_schema::{NamePattern, PatternComponent};
192    use bytes::Bytes;
193    use ndn_packet::{Name, NameComponent};
194    use std::sync::Arc;
195
196    fn comp(s: &'static str) -> NameComponent {
197        NameComponent::generic(Bytes::from_static(s.as_bytes()))
198    }
199    fn name1(c: &'static str) -> Name {
200        Name::from_components([comp(c)])
201    }
202
203    /// Build a Data TLV signed with `signer`.
204    async fn make_signed_data(
205        signer: &Ed25519Signer,
206        data_comp: &'static str,
207        key_comp: &'static str,
208    ) -> Bytes {
209        use ndn_tlv::TlvWriter;
210
211        let nc = {
212            let mut w = TlvWriter::new();
213            w.write_tlv(0x08, data_comp.as_bytes());
214            w.finish()
215        };
216        let name_tlv = {
217            let mut w = TlvWriter::new();
218            w.write_tlv(0x07, &nc);
219            w.finish()
220        };
221
222        let knc = {
223            let mut w = TlvWriter::new();
224            w.write_tlv(0x08, key_comp.as_bytes());
225            w.finish()
226        };
227        let kname_tlv = {
228            let mut w = TlvWriter::new();
229            w.write_tlv(0x07, &knc);
230            w.finish()
231        };
232        let kloc_tlv = {
233            let mut w = TlvWriter::new();
234            w.write_tlv(0x1c, &kname_tlv);
235            w.finish()
236        };
237        let stype_tlv = {
238            let mut w = TlvWriter::new();
239            w.write_tlv(0x1b, &[7u8]);
240            w.finish()
241        };
242        let sinfo_inner: Vec<u8> = stype_tlv.iter().chain(kloc_tlv.iter()).copied().collect();
243        let sinfo_tlv = {
244            let mut w = TlvWriter::new();
245            w.write_tlv(0x16, &sinfo_inner);
246            w.finish()
247        };
248
249        let signed_region: Vec<u8> = name_tlv.iter().chain(sinfo_tlv.iter()).copied().collect();
250        let sig = signer.sign(&signed_region).await.unwrap();
251
252        let sval_tlv = {
253            let mut w = TlvWriter::new();
254            w.write_tlv(0x17, &sig);
255            w.finish()
256        };
257        let inner: Vec<u8> = signed_region
258            .iter()
259            .chain(sval_tlv.iter())
260            .copied()
261            .collect();
262        let mut w = TlvWriter::new();
263        w.write_tlv(0x06, &inner);
264        w.finish()
265    }
266
267    /// Build a certificate Data packet: a Data whose name is `cert_name`,
268    /// Content contains the subject's public key, signed by `issuer_signer`.
269    async fn make_cert_data_packet(
270        cert_name: &Name,
271        subject_pk: &[u8],
272        issuer_signer: &Ed25519Signer,
273    ) -> Bytes {
274        use ndn_tlv::TlvWriter;
275
276        let name_inner = {
277            let mut w = TlvWriter::new();
278            for c in cert_name.components() {
279                w.write_tlv(c.typ, &c.value);
280            }
281            w.finish()
282        };
283        let name_tlv = {
284            let mut w = TlvWriter::new();
285            w.write_tlv(0x07, &name_inner);
286            w.finish()
287        };
288
289        let content_tlv = {
290            let mut w = TlvWriter::new();
291            w.write_nested(0x15, |w| {
292                w.write_tlv(0x00, subject_pk);
293            });
294            w.finish()
295        };
296
297        let issuer_name_inner = {
298            let mut w = TlvWriter::new();
299            for c in issuer_signer.key_name().components() {
300                w.write_tlv(c.typ, &c.value);
301            }
302            w.finish()
303        };
304        let sinfo_tlv = {
305            let mut w = TlvWriter::new();
306            w.write_nested(0x16, |w| {
307                w.write_tlv(0x1b, &[7u8]);
308                w.write_nested(0x1c, |w| {
309                    w.write_tlv(0x07, &issuer_name_inner);
310                });
311            });
312            w.finish()
313        };
314
315        let signed_region: Vec<u8> = name_tlv
316            .iter()
317            .chain(content_tlv.iter())
318            .chain(sinfo_tlv.iter())
319            .copied()
320            .collect();
321        let sig = issuer_signer.sign(&signed_region).await.unwrap();
322
323        let sval_tlv = {
324            let mut w = TlvWriter::new();
325            w.write_tlv(0x17, &sig);
326            w.finish()
327        };
328        let inner: Vec<u8> = signed_region
329            .iter()
330            .chain(sval_tlv.iter())
331            .copied()
332            .collect();
333        let mut w = TlvWriter::new();
334        w.write_tlv(0x06, &inner);
335        w.finish()
336    }
337
338    /// Build a wildcard schema that allows any data -> any key.
339    fn wildcard_schema() -> TrustSchema {
340        use crate::trust_schema::SchemaRule;
341        let mut schema = TrustSchema::new();
342        schema.add_rule(SchemaRule {
343            data_pattern: NamePattern(vec![PatternComponent::MultiCapture("_".into())]),
344            key_pattern: NamePattern(vec![PatternComponent::MultiCapture("_".into())]),
345        });
346        schema
347    }
348
349    #[tokio::test]
350    async fn chain_walk_data_to_anchor() {
351        // Chain: Data(/data) -> cert(/key) -> anchor(/anchor)
352        let anchor_seed = [20u8; 32];
353        let anchor_name = name1("anchor");
354        let anchor_signer = Ed25519Signer::from_seed(&anchor_seed, anchor_name.clone());
355        let anchor_pk = ed25519_dalek::SigningKey::from_bytes(&anchor_seed)
356            .verifying_key()
357            .to_bytes();
358
359        let key_seed = [21u8; 32];
360        let key_name = name1("key");
361        let key_signer = Ed25519Signer::from_seed(&key_seed, key_name.clone());
362        let key_pk = ed25519_dalek::SigningKey::from_bytes(&key_seed)
363            .verifying_key()
364            .to_bytes();
365
366        let cert_wire = make_cert_data_packet(&key_name, &key_pk, &anchor_signer).await;
367        let cert_data = Data::decode(cert_wire).unwrap();
368        let cert = Certificate::decode(&cert_data).unwrap();
369
370        let data_bytes = make_signed_data(&key_signer, "data", "key").await;
371        let data = Data::decode(data_bytes).unwrap();
372
373        let validator = Validator::new(wildcard_schema());
374        validator.add_trust_anchor(Certificate {
375            name: Arc::new(anchor_name),
376            public_key: Bytes::copy_from_slice(&anchor_pk),
377            valid_from: 0,
378            valid_until: u64::MAX,
379            issuer: None,
380            signed_region: None,
381            sig_value: None,
382        });
383        validator.cert_cache().insert(cert);
384
385        match validator.validate_chain(&data).await {
386            ValidationResult::Valid(safe) => {
387                assert_eq!(safe.inner.name, data.name);
388            }
389            ValidationResult::Invalid(e) => panic!("expected Valid, got Invalid: {e}"),
390            ValidationResult::Pending => panic!("expected Valid, got Pending"),
391        }
392    }
393
394    #[tokio::test]
395    async fn chain_walk_missing_cert_returns_pending() {
396        let key_seed = [22u8; 32];
397        let key_name = name1("key");
398        let key_signer = Ed25519Signer::from_seed(&key_seed, key_name);
399
400        let data_bytes = make_signed_data(&key_signer, "data", "key").await;
401        let data = Data::decode(data_bytes).unwrap();
402
403        let validator = Validator::new(wildcard_schema());
404        assert!(matches!(
405            validator.validate_chain(&data).await,
406            ValidationResult::Pending
407        ));
408    }
409
410    /// Build a Data TLV signed with `signer` whose KeyLocator carries a
411    /// KeyDigest (SHA-256 of `signer`'s public key) instead of a Name.
412    /// Used to exercise the digest-form KeyLocator fallback in
413    /// `validate_chain`.
414    async fn make_signed_data_with_key_digest(
415        signer: &Ed25519Signer,
416        signer_pk: &[u8],
417        data_comp: &'static str,
418    ) -> Bytes {
419        use ndn_tlv::TlvWriter;
420
421        let nc = {
422            let mut w = TlvWriter::new();
423            w.write_tlv(0x08, data_comp.as_bytes());
424            w.finish()
425        };
426        let name_tlv = {
427            let mut w = TlvWriter::new();
428            w.write_tlv(0x07, &nc);
429            w.finish()
430        };
431
432        // KeyLocator carrying KeyDigest = SHA-256(public key).
433        let digest = {
434            use sha2::{Digest, Sha256};
435            Sha256::digest(signer_pk)
436        };
437        let kloc_tlv = {
438            let mut w = TlvWriter::new();
439            w.write_nested(0x1c, |w| {
440                w.write_tlv(0x1d, digest.as_slice());
441            });
442            w.finish()
443        };
444        let stype_tlv = {
445            let mut w = TlvWriter::new();
446            w.write_tlv(0x1b, &[7u8]); // Ed25519
447            w.finish()
448        };
449        let sinfo_inner: Vec<u8> = stype_tlv.iter().chain(kloc_tlv.iter()).copied().collect();
450        let sinfo_tlv = {
451            let mut w = TlvWriter::new();
452            w.write_tlv(0x16, &sinfo_inner);
453            w.finish()
454        };
455
456        let signed_region: Vec<u8> = name_tlv.iter().chain(sinfo_tlv.iter()).copied().collect();
457        let sig = signer.sign(&signed_region).await.unwrap();
458
459        let sval_tlv = {
460            let mut w = TlvWriter::new();
461            w.write_tlv(0x17, &sig);
462            w.finish()
463        };
464        let inner: Vec<u8> = signed_region
465            .iter()
466            .chain(sval_tlv.iter())
467            .copied()
468            .collect();
469        let mut w = TlvWriter::new();
470        w.write_tlv(0x06, &inner);
471        w.finish()
472    }
473
474    #[tokio::test]
475    async fn chain_walk_resolves_key_digest_via_cache() {
476        // Same shape as chain_walk_data_to_anchor, but the Data's
477        // KeyLocator is a KeyDigest, not a Name. The validator must
478        // resolve the digest against the cert cache before the chain
479        // walk proceeds.
480        let anchor_seed = [30u8; 32];
481        let anchor_name = name1("anchor");
482        let anchor_signer = Ed25519Signer::from_seed(&anchor_seed, anchor_name.clone());
483        let anchor_pk = ed25519_dalek::SigningKey::from_bytes(&anchor_seed)
484            .verifying_key()
485            .to_bytes();
486
487        let key_seed = [31u8; 32];
488        let key_name = name1("key");
489        let key_signer = Ed25519Signer::from_seed(&key_seed, key_name.clone());
490        let key_pk = ed25519_dalek::SigningKey::from_bytes(&key_seed)
491            .verifying_key()
492            .to_bytes();
493
494        let cert_wire = make_cert_data_packet(&key_name, &key_pk, &anchor_signer).await;
495        let cert_data = Data::decode(cert_wire).unwrap();
496        let cert = Certificate::decode(&cert_data).unwrap();
497
498        let data_bytes = make_signed_data_with_key_digest(&key_signer, &key_pk, "data").await;
499        let data = Data::decode(data_bytes).unwrap();
500
501        let validator = Validator::new(wildcard_schema());
502        validator.add_trust_anchor(Certificate {
503            name: Arc::new(anchor_name),
504            public_key: Bytes::copy_from_slice(&anchor_pk),
505            valid_from: 0,
506            valid_until: u64::MAX,
507            issuer: None,
508            signed_region: None,
509            sig_value: None,
510        });
511        validator.cert_cache().insert(cert);
512
513        match validator.validate_chain(&data).await {
514            ValidationResult::Valid(safe) => {
515                assert_eq!(safe.inner.name, data.name);
516            }
517            ValidationResult::Invalid(e) => {
518                panic!("expected Valid, got Invalid: {e}")
519            }
520            ValidationResult::Pending => panic!("expected Valid, got Pending"),
521        }
522    }
523
524    #[tokio::test]
525    async fn chain_walk_key_digest_uncached_returns_invalid() {
526        // KeyDigest cannot be fetched over the network — if the cert is
527        // not in the cache, validation must fail rather than hang.
528        let key_seed = [32u8; 32];
529        let key_name = name1("key");
530        let key_signer = Ed25519Signer::from_seed(&key_seed, key_name);
531        let key_pk = ed25519_dalek::SigningKey::from_bytes(&key_seed)
532            .verifying_key()
533            .to_bytes();
534
535        let data_bytes = make_signed_data_with_key_digest(&key_signer, &key_pk, "data").await;
536        let data = Data::decode(data_bytes).unwrap();
537
538        let validator = Validator::new(wildcard_schema());
539        match validator.validate_chain(&data).await {
540            ValidationResult::Invalid(TrustError::InvalidSignature) => {}
541            other => panic!("expected Invalid(InvalidSignature), got: {other:?}"),
542        }
543    }
544
545    #[tokio::test]
546    async fn chain_walk_depth_limit() {
547        let seed = [23u8; 32];
548        let key_name = name1("key");
549        let signer = Ed25519Signer::from_seed(&seed, key_name.clone());
550        let pk = ed25519_dalek::SigningKey::from_bytes(&seed)
551            .verifying_key()
552            .to_bytes();
553
554        let cert_wire = make_cert_data_packet(&key_name, &pk, &signer).await;
555        let cert_data = Data::decode(cert_wire).unwrap();
556        let cert = Certificate::decode(&cert_data).unwrap();
557
558        let data_bytes = make_signed_data(&signer, "data", "key").await;
559        let data = Data::decode(data_bytes).unwrap();
560
561        let validator = Validator::new(wildcard_schema());
562        validator.cert_cache().insert(cert);
563
564        match validator.validate_chain(&data).await {
565            ValidationResult::Invalid(TrustError::ChainCycle { .. }) => {}
566            other => panic!("expected ChainCycle, got: {other:?}"),
567        }
568    }
569}