Skip to main content

ndn_app/
verifier.rs

1//! Composable acceptance policies for Interests and Data packets.
2//!
3//! A verifier is anything that can say yes or no to a packet before it
4//! reaches application code: [`InterestVerifier`] gates producer route
5//! handlers (see [`crate::app::App::route`]), and [`DataVerifier`] gates
6//! responses received by [`crate::app::AppHandler::express_interest`].
7//! Both traits are deliberately minimal (one `verify` method each) so
8//! that policy is built by composing small, independently testable
9//! pieces with [`VerifierEx::and`] and [`VerifierEx::or`], rather than by
10//! writing one large verifier per use case.
11//!
12//! ```rust,no_run
13//! use ndn_app::verifier::{ForbidUnsigned, ForbidDigestSignature, VerifierEx};
14//!
15//! // Require a signature, but don't accept a bare digest as proof of
16//! // authenticity.
17//! let verifier = ForbidUnsigned.and(ForbidDigestSignature);
18//! ```
19//!
20//! [`simple_verifier`] and [`simple_signed`] package up the combination
21//! most applications want as a starting point.
22
23use std::{collections::HashMap, sync::Arc};
24
25use async_trait::async_trait;
26use bytes::{Buf, Bytes};
27use derive_more::Constructor;
28use futures::{future::BoxFuture, FutureExt};
29use ndn_protocol::{
30    signature::{KeyLocatorData, SignMethodType as _, ToVerifier},
31    Certificate, Data, DigestSha256, Interest,
32};
33use tokio::sync::RwLock;
34use type_map::concurrent::TypeMap;
35
36use crate::app::AppHandler;
37
38/// Accepts unsigned packets and packets with a valid signature, but
39/// rejects a `DigestSha256` signature whose digest doesn't actually
40/// match the packet.
41///
42/// A bare digest only proves the packet wasn't corrupted in transit, not
43/// who sent it, so this is a baseline integrity check rather than
44/// authentication. Reach for [`simple_signed`] when a real signature
45/// should be required.
46pub fn simple_verifier() -> OrVerifier<ForbidDigestSignature, RequireValidSignature> {
47    OrVerifier(
48        ForbidDigestSignature,
49        RequireValidSignature(DigestSha256::certificate()),
50    )
51}
52
53/// Same as [`simple_verifier`], but also rejects unsigned packets, so
54/// every accepted packet carries a real signature.
55pub fn simple_signed(
56) -> AndVerifier<ForbidUnsigned, OrVerifier<ForbidDigestSignature, RequireValidSignature>> {
57    AndVerifier(ForbidUnsigned, simple_verifier())
58}
59
60/// Decides whether an incoming Interest should reach a route handler.
61///
62/// Implementations receive the same [`AppHandler`] passed to route
63/// handlers, so a verifier can itself express Interests, e.g. to fetch a
64/// certificate needed to validate a signature (see
65/// [`RequireValidSignature`]). `context` is a per-app [`TypeMap`] shared
66/// across all verifier invocations, letting stateful verifiers (like
67/// replay-protection ones) keep data between calls without threading
68/// their own storage through `App`.
69#[async_trait]
70pub trait InterestVerifier {
71    async fn verify(
72        &self,
73        interest: &Interest<Bytes>,
74        context: Arc<RwLock<TypeMap>>,
75        app_handler: AppHandler,
76        signature_verifiers: &(dyn ToVerifier + Sync),
77    ) -> bool;
78}
79
80/// Decides whether a Data packet received in response to an expressed
81/// Interest should be handed back to the caller of
82/// [`AppHandler::express_interest`], mirroring [`InterestVerifier`] for
83/// the consumer side.
84#[async_trait]
85pub trait DataVerifier {
86    async fn verify(
87        &self,
88        data: &Data<Bytes>,
89        context: Arc<RwLock<TypeMap>>,
90        mut app_handler: AppHandler,
91        signature_verifiers: &(dyn ToVerifier + Sync),
92    ) -> bool;
93}
94
95/// Adds `.and` / `.or` combinators to any verifier, so policies can be
96/// built up from smaller pieces instead of writing one verifier that
97/// checks everything.
98pub trait VerifierEx: Sized {
99    /// Accepts a packet if either `self` or `other` accepts it.
100    fn or<T: Sized>(self, other: T) -> OrVerifier<Self, T> {
101        OrVerifier(self, other)
102    }
103
104    /// Accepts a packet only if both `self` and `other` accept it.
105    fn and<T: Sized>(self, other: T) -> AndVerifier<Self, T> {
106        AndVerifier(self, other)
107    }
108}
109
110/// Accepts every packet unconditionally. Useful as a placeholder while a
111/// route is being developed, or as one side of an [`OrVerifier`] where
112/// the other side is expected to do the real work.
113#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
114pub struct AllowAll;
115
116impl VerifierEx for AllowAll {}
117
118#[async_trait]
119impl DataVerifier for AllowAll {
120    async fn verify(
121        &self,
122        _data: &Data<Bytes>,
123        _context: Arc<RwLock<TypeMap>>,
124        _app_handler: AppHandler,
125        _signature_verifiers: &(dyn ToVerifier + Sync),
126    ) -> bool {
127        true
128    }
129}
130
131#[async_trait]
132impl InterestVerifier for AllowAll {
133    async fn verify(
134        &self,
135        _data: &Interest<Bytes>,
136        _context: Arc<RwLock<TypeMap>>,
137        _app_handler: AppHandler,
138        _signature_verifiers: &(dyn ToVerifier + Sync),
139    ) -> bool {
140        true
141    }
142}
143
144/// Rejects every packet unconditionally. The identity element for
145/// [`AndVerifier`]: `AndVerifier(ForbidAll, x)` always rejects,
146/// regardless of `x`, which is occasionally useful for disabling a route
147/// without removing it.
148#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
149pub struct ForbidAll;
150
151#[async_trait]
152impl DataVerifier for ForbidAll {
153    async fn verify(
154        &self,
155        _data: &Data<Bytes>,
156        _context: Arc<RwLock<TypeMap>>,
157        _app_handler: AppHandler,
158        _signature_verifiers: &(dyn ToVerifier + Sync),
159    ) -> bool {
160        false
161    }
162}
163
164#[async_trait]
165impl InterestVerifier for ForbidAll {
166    async fn verify(
167        &self,
168        _data: &Interest<Bytes>,
169        _context: Arc<RwLock<TypeMap>>,
170        _app_handler: AppHandler,
171        _signature_verifiers: &(dyn ToVerifier + Sync),
172    ) -> bool {
173        false
174    }
175}
176
177impl VerifierEx for ForbidAll {}
178
179/// Accepts a packet if either wrapped verifier accepts it. Built by
180/// [`VerifierEx::or`] rather than constructed directly.
181///
182/// Both branches always run, even once one has already accepted the
183/// packet, since a verifier's side effects (e.g. recording a nonce in
184/// [`RequireValidNonce`]) may matter even when its boolean result
185/// doesn't end up deciding the outcome.
186#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
187pub struct OrVerifier<T, U>(T, U);
188
189#[async_trait]
190impl<T, U> DataVerifier for OrVerifier<T, U>
191where
192    T: DataVerifier + Sync,
193    U: DataVerifier + Sync,
194{
195    async fn verify(
196        &self,
197        data: &Data<Bytes>,
198        context: Arc<RwLock<TypeMap>>,
199        app_handler: AppHandler,
200        signature_verifiers: &(dyn ToVerifier + Sync),
201    ) -> bool {
202        let res1 = self
203            .0
204            .verify(
205                data,
206                Arc::clone(&context),
207                app_handler.clone(),
208                signature_verifiers,
209            )
210            .await;
211        let res2 = self
212            .1
213            .verify(data, context, app_handler.clone(), signature_verifiers)
214            .await;
215        res1 || res2
216    }
217}
218
219#[async_trait]
220impl<T, U> InterestVerifier for OrVerifier<T, U>
221where
222    T: InterestVerifier + Sync,
223    U: InterestVerifier + Sync,
224{
225    async fn verify(
226        &self,
227        interest: &Interest<Bytes>,
228        context: Arc<RwLock<TypeMap>>,
229        app_handler: AppHandler,
230        signature_verifiers: &(dyn ToVerifier + Sync),
231    ) -> bool {
232        let res1 = self
233            .0
234            .verify(
235                interest,
236                Arc::clone(&context),
237                app_handler.clone(),
238                signature_verifiers,
239            )
240            .await;
241        let res2 = self
242            .1
243            .verify(interest, context, app_handler, signature_verifiers)
244            .await;
245        res1 || res2
246    }
247}
248
249impl<T, U> VerifierEx for OrVerifier<T, U> {}
250
251/// Accepts a packet only if both wrapped verifiers accept it. Built by
252/// [`VerifierEx::and`] rather than constructed directly.
253///
254/// Like [`OrVerifier`], both branches always run rather than
255/// short-circuiting, so stateful verifiers on either side still observe
256/// every packet regardless of the other branch's result.
257#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
258pub struct AndVerifier<T, U>(T, U);
259
260#[async_trait]
261impl<T, U> DataVerifier for AndVerifier<T, U>
262where
263    T: DataVerifier + Sync,
264    U: DataVerifier + Sync,
265{
266    async fn verify(
267        &self,
268        data: &Data<Bytes>,
269        context: Arc<RwLock<TypeMap>>,
270        app_handler: AppHandler,
271        signature_verifiers: &(dyn ToVerifier + Sync),
272    ) -> bool {
273        let res1 = self
274            .0
275            .verify(
276                data,
277                Arc::clone(&context),
278                app_handler.clone(),
279                signature_verifiers,
280            )
281            .await;
282        let res2 = self
283            .1
284            .verify(data, context, app_handler.clone(), signature_verifiers)
285            .await;
286        res1 && res2
287    }
288}
289
290#[async_trait]
291impl<T, U> InterestVerifier for AndVerifier<T, U>
292where
293    T: InterestVerifier + Sync,
294    U: InterestVerifier + Sync,
295{
296    async fn verify(
297        &self,
298        interest: &Interest<Bytes>,
299        context: Arc<RwLock<TypeMap>>,
300        app_handler: AppHandler,
301        signature_verifiers: &(dyn ToVerifier + Sync),
302    ) -> bool {
303        let res1 = self
304            .0
305            .verify(
306                interest,
307                Arc::clone(&context),
308                app_handler.clone(),
309                signature_verifiers,
310            )
311            .await;
312        let res2 = self
313            .1
314            .verify(interest, context, app_handler, signature_verifiers)
315            .await;
316        res1 && res2
317    }
318}
319
320impl<T, U> VerifierEx for AndVerifier<T, U> {}
321
322/// Requires a valid cryptographic signature, chaining up through
323/// certificates to the trust anchor held in the tuple field.
324///
325/// A packet's signature is checked directly against the anchor first;
326/// if the signing key isn't the anchor itself, the verifier fetches the
327/// signer's certificate (by expressing an Interest for it through
328/// [`AppHandler`]) and recurses, up to a fixed depth, until it either
329/// reaches the anchor or gives up. This lets an application trust one
330/// root key while still accepting packets signed by keys the root has
331/// certified, rather than needing every valid signer's key listed
332/// up front.
333#[derive(Debug, Clone, Hash)]
334pub struct RequireValidSignature(pub Certificate);
335
336impl RequireValidSignature {
337    fn verify_signature<'a>(
338        &'a self,
339        cert: &'a Certificate,
340        mut app_handler: AppHandler,
341        signature_verifiers: &'a (dyn ToVerifier + Sync),
342        max_depth: usize,
343    ) -> BoxFuture<'a, bool> {
344        async move {
345            if max_depth == 0 {
346                return false;
347            }
348
349            let Some(info) = cert.signature_info() else {
350                return false;
351            };
352
353            let Some(KeyLocatorData::Name(locator)) = info.key_locator() else {
354                return false;
355            };
356
357            if self.0.name().has_prefix(locator) {
358                // Signed by anchor
359                let Some(verifier) = signature_verifiers.from_data(self.0 .0.clone()) else {
360                    return false;
361                };
362                return cert.as_data().verify(&*verifier).is_ok();
363            }
364
365            let Ok(signer) = app_handler
366                .express_interest_unsigned(
367                    Interest::<()>::new(locator.clone()),
368                    AllowAll, // SECURITY: We do custom verification
369                )
370                .await
371            else {
372                return false;
373            };
374            println!("{:#?}", signer);
375
376            let signer_cert = Certificate(signer);
377
378            if !self
379                .verify_signature(
380                    &signer_cert,
381                    app_handler.clone(),
382                    signature_verifiers,
383                    max_depth - 1,
384                )
385                .await
386            {
387                return false;
388            }
389
390            let Some(verifier) = signature_verifiers.from_data(signer_cert.0) else {
391                return false;
392            };
393
394            cert.as_data().verify(&*verifier).is_ok()
395        }
396        .boxed()
397    }
398}
399
400#[async_trait]
401impl InterestVerifier for RequireValidSignature {
402    async fn verify(
403        &self,
404        interest: &Interest<Bytes>,
405        _context: Arc<RwLock<TypeMap>>,
406        mut app_handler: AppHandler,
407        signature_verifiers: &(dyn ToVerifier + Sync),
408    ) -> bool {
409        const CERT_CHAIN_MAX_DEPTH: usize = 16;
410
411        if let Some(verifier) = signature_verifiers.from_data(self.0 .0.clone()) {
412            if interest.verify(&*verifier).is_ok() {
413                return true;
414            }
415        }
416
417        let Some(info) = interest.signature_info() else {
418            return false;
419        };
420        let Some(locator) = info.key_locator() else {
421            return false;
422        };
423
424        let Some(locator_name) = locator.as_name() else {
425            return false;
426        };
427        let Ok(signed_by) = app_handler
428            .express_interest_unsigned(
429                Interest::<()>::new(locator_name.clone()),
430                AllowAll, // SECURITY: We do custom verification
431            )
432            .await
433        else {
434            return false;
435        };
436        return self
437            .verify_signature(
438                &Certificate(signed_by),
439                app_handler.clone(),
440                signature_verifiers,
441                CERT_CHAIN_MAX_DEPTH,
442            )
443            .await;
444    }
445}
446
447#[async_trait]
448impl DataVerifier for RequireValidSignature {
449    async fn verify(
450        &self,
451        data: &Data<Bytes>,
452        _context: Arc<RwLock<TypeMap>>,
453        mut app_handler: AppHandler,
454        signature_verifiers: &(dyn ToVerifier + Sync),
455    ) -> bool {
456        const CERT_CHAIN_MAX_DEPTH: usize = 16;
457
458        if let Some(verifier) = signature_verifiers.from_data(self.0 .0.clone()) {
459            if data.verify(&*verifier).is_ok() {
460                return true;
461            }
462        }
463
464        let Some(info) = data.signature_info() else {
465            return false;
466        };
467        let Some(locator) = info.key_locator() else {
468            return false;
469        };
470
471        let Some(locator_name) = locator.as_name() else {
472            return false;
473        };
474
475        let Ok(signed_by) = app_handler
476            .express_interest_unsigned(
477                Interest::<()>::new(locator_name.clone()),
478                AllowAll, // SECURITY: We do custom verification
479            )
480            .await
481        else {
482            return false;
483        };
484
485        return self
486            .verify_signature(
487                &Certificate(signed_by),
488                app_handler.clone(),
489                signature_verifiers,
490                CERT_CHAIN_MAX_DEPTH,
491            )
492            .await;
493    }
494}
495
496impl VerifierEx for RequireValidSignature {}
497
498/// Rejects packets signed with a bare `DigestSha256` signature, while
499/// still allowing unsigned packets and packets signed with other
500/// signature types through.
501///
502/// A `DigestSha256` signature only proves the packet's bytes weren't
503/// altered; it doesn't identify a signer, since anyone can compute a
504/// digest. This verifier exists to be combined with a real signature
505/// check (see [`simple_verifier`]) so digest-only packets don't
506/// masquerade as authenticated ones.
507#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
508pub struct ForbidDigestSignature;
509
510#[async_trait]
511impl InterestVerifier for ForbidDigestSignature {
512    async fn verify(
513        &self,
514        interest: &Interest<Bytes>,
515        _context: Arc<RwLock<TypeMap>>,
516        _app_handler: AppHandler,
517        _signature_verifiers: &(dyn ToVerifier + Sync),
518    ) -> bool {
519        if let Some(info) = interest.signature_info() {
520            info.signature_type().value() != ndn_protocol::DigestSha256::SIGNATURE_TYPE
521        } else {
522            true
523        }
524    }
525}
526
527#[async_trait]
528impl DataVerifier for ForbidDigestSignature {
529    async fn verify(
530        &self,
531        data: &Data<Bytes>,
532        _context: Arc<RwLock<TypeMap>>,
533        _app_handler: AppHandler,
534        _signature_verifiers: &(dyn ToVerifier + Sync),
535    ) -> bool {
536        if let Some(info) = data.signature_info() {
537            info.signature_type().value() != ndn_protocol::DigestSha256::SIGNATURE_TYPE
538        } else {
539            true
540        }
541    }
542}
543
544impl VerifierEx for ForbidDigestSignature {}
545
546/// Rejects packets with no signature at all, regardless of what
547/// signature type would otherwise be present.
548///
549/// This only checks that a `SignatureInfo` field exists; it says nothing
550/// about whether the signature is valid. Combine with
551/// [`RequireValidSignature`] to actually verify it.
552#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
553pub struct ForbidUnsigned;
554
555#[async_trait]
556impl InterestVerifier for ForbidUnsigned {
557    async fn verify(
558        &self,
559        interest: &Interest<Bytes>,
560        _context: Arc<RwLock<TypeMap>>,
561        _app_handler: AppHandler,
562        _signature_verifiers: &(dyn ToVerifier + Sync),
563    ) -> bool {
564        interest.signature_info().is_some()
565    }
566}
567
568#[async_trait]
569impl DataVerifier for ForbidUnsigned {
570    async fn verify(
571        &self,
572        data: &Data<Bytes>,
573        _context: Arc<RwLock<TypeMap>>,
574        _app_handler: AppHandler,
575        _signature_verifiers: &(dyn ToVerifier + Sync),
576    ) -> bool {
577        data.signature_info().is_some()
578    }
579}
580
581impl VerifierEx for ForbidUnsigned {}
582
583/// Rejects a signed Interest if its signature nonce has been seen before
584/// from the same signer, and rejects signed Interests with no nonce or
585/// an implausibly long one. Unsigned Interests are always accepted,
586/// since there's no signer identity to track replay against.
587///
588/// Nonces are tracked per `(signature type, key locator)` pair in a
589/// fixed-size ring buffer, so this only catches replay within a recent
590/// window rather than for the lifetime of the app; a longer memory would
591/// mean unbounded growth per signer.
592#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
593pub struct RequireValidNonce;
594
595struct NonceList<const N: usize> {
596    nonces: [Option<Bytes>; N],
597    buffer_pos: usize,
598}
599
600/// Per-signer nonce history, stored in the shared verifier [`TypeMap`]
601/// so it survives across calls to [`RequireValidNonce::verify`].
602struct ValidNonceContext {
603    used_nonces: HashMap<(u64, Option<KeyLocatorData>), NonceList<{ Self::BUFFER_SIZE }>>,
604}
605
606impl ValidNonceContext {
607    const BUFFER_SIZE: usize = 16;
608}
609
610#[async_trait]
611impl InterestVerifier for RequireValidNonce {
612    async fn verify(
613        &self,
614        interest: &Interest<Bytes>,
615        context: Arc<RwLock<TypeMap>>,
616        _app_handler: AppHandler,
617        _signature_verifiers: &(dyn ToVerifier + Sync),
618    ) -> bool {
619        let Some(signature_info) = interest.signature_info() else {
620            // Unsigned - might be allowed
621            return true;
622        };
623
624        let Some(nonce) = signature_info.nonce() else {
625            // No nonce present
626            return false;
627        };
628
629        if nonce.remaining() > 16 {
630            // Nonce too long - limit maximimum nonce length to prevent potential out of memory
631            // attacks
632            return false;
633        }
634
635        let key = (
636            signature_info.signature_type().value(),
637            signature_info.key_locator().map(Clone::clone),
638        );
639
640        let mut context = context.write().await;
641        let verifier_context = {
642            if !context.contains::<ValidNonceContext>() {
643                let verifier_context = ValidNonceContext {
644                    used_nonces: HashMap::new(),
645                };
646                context.insert(verifier_context);
647            }
648            context.get_mut::<ValidNonceContext>().unwrap()
649        };
650
651        let used_nonces = if let Some(used_nonces) = verifier_context.used_nonces.get_mut(&key) {
652            used_nonces
653        } else {
654            const NONE: Option<Bytes> = None;
655            let used_nonces = NonceList {
656                nonces: [NONE; ValidNonceContext::BUFFER_SIZE],
657                buffer_pos: 0,
658            };
659            verifier_context
660                .used_nonces
661                .insert(key.clone(), used_nonces);
662            verifier_context.used_nonces.get_mut(&key).unwrap()
663        };
664
665        if used_nonces.nonces.contains(&Some(nonce.clone())) {
666            return false;
667        }
668        used_nonces.nonces[used_nonces.buffer_pos] = Some(nonce.clone());
669        used_nonces.buffer_pos = (used_nonces.buffer_pos + 1) % ValidNonceContext::BUFFER_SIZE;
670        true
671    }
672}
673
674impl VerifierEx for RequireValidNonce {}
675
676/// Rejects a signed Interest whose signature timestamp isn't strictly
677/// greater than the last one seen from the same signer, and rejects
678/// signed Interests with no timestamp. Unsigned Interests are always
679/// accepted.
680///
681/// This is a lighter-weight alternative to [`RequireValidNonce`]: it
682/// only needs one timestamp per signer rather than a window of recent
683/// nonces, at the cost of requiring signers to send strictly increasing
684/// timestamps (fine for a live clock, but unlike nonces it can't
685/// tolerate reordering). The first Interest seen from a signer is
686/// compared against `now - GRACE_PERIOD` rather than accepted
687/// unconditionally, so a timestamp more than a minute old is still
688/// rejected even on the first sighting.
689#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
690pub struct RequireValidTime;
691
692/// Per-signer last-seen timestamp, stored in the shared verifier
693/// [`TypeMap`] so it survives across calls to
694/// [`RequireValidTime::verify`].
695struct ValidTimeContext {
696    last_seen: HashMap<(u64, Option<KeyLocatorData>), u64>,
697}
698
699impl ValidTimeContext {
700    const GRACE_PERIOD: u64 = 60_000;
701}
702
703#[async_trait]
704impl InterestVerifier for RequireValidTime {
705    async fn verify(
706        &self,
707        interest: &Interest<Bytes>,
708        context: Arc<RwLock<TypeMap>>,
709        _app_handler: AppHandler,
710        _signature_verifiers: &(dyn ToVerifier + Sync),
711    ) -> bool {
712        let Some(signature_info) = interest.signature_info() else {
713            // Unsigned - might be allowed
714            return true;
715        };
716
717        let Some(timestamp) = signature_info.time().map(|x| x.as_u64()) else {
718            // No timestamp present
719            return false;
720        };
721
722        let key = (
723            signature_info.signature_type().value(),
724            signature_info.key_locator().map(Clone::clone),
725        );
726
727        let mut context = context.write().await;
728        let verifier_context = {
729            if !context.contains::<ValidTimeContext>() {
730                let verifier_context = ValidTimeContext {
731                    last_seen: HashMap::new(),
732                };
733                context.insert(verifier_context);
734            }
735            context.get_mut::<ValidTimeContext>().unwrap()
736        };
737
738        if !verifier_context.last_seen.contains_key(&key) {
739            verifier_context.last_seen.insert(
740                key.clone(),
741                std::time::SystemTime::now()
742                    .duration_since(std::time::UNIX_EPOCH)
743                    .unwrap()
744                    .as_millis() as u64
745                    - ValidTimeContext::GRACE_PERIOD,
746            );
747        }
748        let last_seen = verifier_context.last_seen.get_mut(&key).unwrap();
749        if timestamp > *last_seen {
750            *last_seen = timestamp;
751            return true;
752        }
753        false
754    }
755}
756
757/// Rejects a signed Interest whose signature sequence number isn't
758/// strictly greater than the last one seen from the same signer, and
759/// rejects signed Interests with no sequence number. Unsigned Interests
760/// are always accepted.
761///
762/// Unlike [`RequireValidTime`], the first Interest seen from a signer is
763/// accepted unconditionally and simply establishes the baseline, since a
764/// sequence number (unlike a timestamp) carries no external notion of
765/// "too old" to check it against.
766#[derive(Debug, Clone, Copy, Hash, Constructor, Default)]
767pub struct RequireValidSeqNum;
768
769/// Per-signer last-seen sequence number, stored in the shared verifier
770/// [`TypeMap`] so it survives across calls to
771/// [`RequireValidSeqNum::verify`].
772struct ValidSeqNumContext {
773    last_seq_num: HashMap<(u64, Option<KeyLocatorData>), u64>,
774}
775
776#[async_trait]
777impl InterestVerifier for RequireValidSeqNum {
778    async fn verify(
779        &self,
780        interest: &Interest<Bytes>,
781        context: Arc<RwLock<TypeMap>>,
782        _app_handler: AppHandler,
783        _signature_verifiers: &(dyn ToVerifier + Sync),
784    ) -> bool {
785        let Some(signature_info) = interest.signature_info() else {
786            // Unsigned - might be allowed
787            return true;
788        };
789
790        let Some(seq_num) = signature_info.seq_num().map(|x| x.as_u64()) else {
791            // No timestamp present
792            return false;
793        };
794
795        let key = (
796            signature_info.signature_type().value(),
797            signature_info.key_locator().map(Clone::clone),
798        );
799
800        let mut context = context.write().await;
801        let verifier_context = {
802            if !context.contains::<ValidSeqNumContext>() {
803                let verifier_context = ValidSeqNumContext {
804                    last_seq_num: HashMap::new(),
805                };
806                context.insert(verifier_context);
807            }
808            context.get_mut::<ValidSeqNumContext>().unwrap()
809        };
810
811        if !verifier_context.last_seq_num.contains_key(&key) {
812            verifier_context.last_seq_num.insert(key.clone(), seq_num);
813            return true;
814        }
815        let last_seq_num = verifier_context.last_seq_num.get_mut(&key).unwrap();
816        if seq_num > *last_seq_num {
817            *last_seq_num = seq_num;
818            return true;
819        }
820        false
821    }
822}