Skip to main content

tor_keymgr/key_specifier/
derive.rs

1//! [`KeySpecifier`] derive-deftly macro and its support code
2//!
3//! # STABILITY - NOTHING IN THIS MODULE IS PART OF THE STABLE PUBLIC API
4//!
5//! The `pub` items in this module are accessible as `$crate::key_specifier_derive`,
6//! but `#[doc(hidden)]` is applied at the top level.
7//!
8//! (Recall that the actual derive-deftly macro
9//! `KeySpecifier` ends up in the crate toplevel,
10//! so that *does* form part of our public API.)
11
12use std::iter;
13
14use derive_deftly::define_derive_deftly;
15use itertools::{EitherOrBoth, Itertools, izip};
16
17use super::*;
18use crate::DENOTATOR_SEP;
19
20pub use crate::KeyPathInfoBuilder;
21pub use tor_error::{Bug, internal, into_internal};
22
23/// Trait for (only) formatting as a [`KeySpecifierComponent`]
24///
25/// Like the formatting part of `KeySpecifierComponent`
26/// but implemented for Option and &str too.
27pub trait RawKeySpecifierComponent {
28    /// Append `self`s `KeySpecifierComponent` string representation to `s`
29    //
30    // This is not quite like `KeySpecifierComponent::to_slug`,
31    // since that *returns* a String (effectively) and we *append*.
32    // At some future point we may change KeySpecifierComponent,
33    // although the current API has the nice feature that
34    // the syntax of the appended string is checked before we receive it here.
35    fn append_to(&self, s: &mut String) -> Result<(), Bug>;
36}
37impl<T: KeySpecifierComponent> RawKeySpecifierComponent for T {
38    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
39        self.to_slug()?.as_str().append_to(s)
40    }
41}
42impl<T: KeySpecifierComponent> RawKeySpecifierComponent for Option<T> {
43    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
44        let v: &dyn RawKeySpecifierComponent = match self.as_ref() {
45            Some(v) => v,
46            None => &"*",
47        };
48        v.append_to(s)
49    }
50}
51impl<'s> RawKeySpecifierComponent for &'s str {
52    fn append_to(&self, s: &mut String) -> Result<(), Bug> {
53        s.push_str(self);
54        Ok(())
55    }
56}
57
58/// Make a string like `pc/pc/pc/lc_lc_lc`
59fn arti_path_string_from_components(
60    path_comps: &[&dyn RawKeySpecifierComponent],
61    leaf_comps: &[&dyn RawKeySpecifierComponent],
62) -> Result<String, Bug> {
63    let mut path = String::new();
64
65    for comp in path_comps {
66        comp.append_to(&mut path)?;
67        path.push('/');
68    }
69    for (delim, comp) in izip!(
70        iter::once(None).chain(iter::repeat(Some(DENOTATOR_SEP))),
71        leaf_comps,
72    ) {
73        if let Some(delim) = delim {
74            path.push(delim);
75        }
76        comp.append_to(&mut path)?;
77    }
78
79    Ok(path)
80}
81
82/// Make a string like `pc/pc/pc/pd+pd+pd@cd+cd+cd+cd`
83fn cert_arti_path_string_from_components(
84    subj_comp: &str,
85    leaf_comps: &[&dyn RawKeySpecifierComponent],
86) -> Result<String, Bug> {
87    if leaf_comps.is_empty() {
88        return Ok(subj_comp.to_string());
89    }
90
91    let mut path = if subj_comp.contains('+') {
92        format!("{subj_comp}@")
93    } else {
94        format!("{subj_comp}+@")
95    };
96
97    for (delim, comp) in izip!(
98        iter::once(None).chain(iter::repeat(Some(DENOTATOR_SEP))),
99        leaf_comps,
100    ) {
101        if let Some(delim) = delim {
102            path.push(delim);
103        }
104        comp.append_to(&mut path)?;
105    }
106
107    Ok(path)
108}
109
110/// Make an `ArtiPath` like `pc/pc/pc/lc_lc_lc`
111///
112/// This is the engine for the `KeySpecifier` macro's `arti_path()` impls.
113///
114/// The macro-generated code sets up couple of vectors.
115/// Each vector entry is a pointer to the field in the original struct,
116/// plus a vtable pointer saying what to do with it.
117///
118/// For fixed elements in the path,
119/// the vtable entry's data pointer is a pointer to a constant &str.
120///
121/// In the macro, this is done by the user-defined expansion `ARTI_FROM_COMPONENTS_ARGS`.
122///
123/// Doing it this way minimises the amount of macro-generated machine code.
124pub fn arti_path_from_components(
125    path_comps: &[&dyn RawKeySpecifierComponent],
126    leaf_comps: &[&dyn RawKeySpecifierComponent],
127) -> Result<ArtiPath, ArtiPathUnavailableError> {
128    Ok(arti_path_string_from_components(path_comps, leaf_comps)?
129        .try_into()
130        .map_err(into_internal!("bad ArtiPath from good components"))?)
131}
132
133/// Make a `KeyPathPattern::Arti` like `pc/pc/pc/lc_lc_lc`
134pub fn arti_pattern_from_components(
135    path_comps: &[&dyn RawKeySpecifierComponent],
136    leaf_comps: &[&dyn RawKeySpecifierComponent],
137) -> Result<KeyPathPattern, Bug> {
138    Ok(KeyPathPattern::Arti(arti_path_string_from_components(
139        path_comps, leaf_comps,
140    )?))
141}
142
143/// Make a `KeyPathPattern::Arti` for a certificate specifier
144pub fn cert_arti_pattern_from_components(
145    subj_path: &str,
146    leaf_comps: &[&dyn RawKeySpecifierComponent],
147) -> Result<KeyPathPattern, Bug> {
148    Ok(KeyPathPattern::Arti(cert_arti_path_string_from_components(
149        subj_path, leaf_comps,
150    )?))
151}
152
153/// Error returned from [`RawKeySpecifierComponentParser::parse`]
154#[derive(Debug)]
155#[allow(clippy::exhaustive_enums)] // Not part of public API
156pub enum RawComponentParseResult {
157    /// This was a field
158    ///
159    /// The `Option` has been filled with the actual value.
160    /// It has an entry in the `keys` argument to [`parse_arti_path`].
161    ParsedField,
162    /// This was a literal, and it matched
163    MatchedLiteral,
164    /// Becomes [`ArtiPathError::PatternNotMatched`]
165    PatternNotMatched,
166    /// `InvalidKeyPathComponentValue`
167    Invalid(InvalidKeyPathComponentValue),
168}
169
170use RawComponentParseResult as RCPR;
171
172/// Trait for parsing a path component, used by [`parse_arti_path`]
173///
174/// Implemented for `Option<impl KeySpecifierComponent>`,
175/// and guarantees to fill in the Option if it succeeds.
176///
177/// Also implemented for `&str`: just checks that the string is right,
178/// (and, doesn't modify `*self`).
179pub trait RawKeySpecifierComponentParser {
180    /// Check that `comp` is as expected, and store any results in `self`.
181    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult;
182}
183
184impl<T: KeySpecifierComponent> RawKeySpecifierComponentParser for Option<T> {
185    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
186        let v = match T::from_slug(comp) {
187            Ok(v) => v,
188            Err(e) => return RCPR::Invalid(e),
189        };
190        *self = Some(v);
191        RCPR::ParsedField
192    }
193}
194impl<'s> RawKeySpecifierComponentParser for &'s str {
195    fn parse(&mut self, comp: &Slug) -> RawComponentParseResult {
196        if comp.as_str() == *self {
197            RCPR::MatchedLiteral
198        } else {
199            RCPR::PatternNotMatched
200        }
201    }
202}
203
204/// List of parsers for fields
205type Parsers<'p> = [&'p mut dyn RawKeySpecifierComponentParser];
206
207/// Split a string into components and parse each one
208fn extract(
209    input: Option<&str>,
210    delim: char,
211    parsers: &mut Parsers,
212    keys: &mut &[&str],
213) -> Result<(), ArtiPathError> {
214    for ent in Itertools::zip_longest(
215        input.map(|input| input.split(delim)).into_iter().flatten(),
216        parsers,
217    ) {
218        let EitherOrBoth::Both(comp, parser) = ent else {
219            // wrong number of components
220            return Err(ArtiPathError::PatternNotMatched);
221        };
222
223        // TODO would be nice to avoid allocating again here,
224        // but I think that needs an `SlugRef`.
225        let comp = Slug::new(comp.to_owned())
226            .map_err(ArtiPathSyntaxError::Slug)
227            .map_err(ArtiPathError::InvalidArtiPath)?;
228
229        let missing_keys = || internal!("keys list too short, bad args to parse_arti_path");
230
231        match parser.parse(&comp) {
232            RCPR::PatternNotMatched => Err(ArtiPathError::PatternNotMatched),
233            RCPR::Invalid(error) => Err(ArtiPathError::InvalidKeyPathComponentValue {
234                error,
235                key: keys.first().ok_or_else(missing_keys)?.to_string(),
236                value: comp,
237            }),
238            RCPR::ParsedField => {
239                *keys = keys.split_first().ok_or_else(missing_keys)?.1;
240                Ok(())
241            }
242            RCPR::MatchedLiteral => Ok(()),
243        }?;
244    }
245    Ok(())
246}
247
248/// Parse a `KeyPath` as an `ArtiPath` like pc/pc/pc/lc_lc_lc
249///
250/// `keys` is the field names for each of the path_parsers and leaf_parsers,
251/// *but* only the ones which will return `RCPR::ParsedField` (or `::Invalid`).
252///
253/// As with `arti_path_string_components` etc., we try to minimise
254/// the amount of macro-generated machine code.
255///
256/// The macro-generated impl again assembles two vectors,
257/// one for the path components and one for the leaf components.
258///
259/// For a field, the vector entry is a pointer to `&mut Option<...>`
260/// for the field, along with a `RawKeySpecifierComponentParser` vtable entry.
261/// (The macro-generated impl must unwrap each of these Options,
262/// to assemble the final struct.  In principle this could be avoided with
263/// use of `MaybeUninit` and unsafe.)
264///
265/// For a fixed string component, the vector entry data pointer points to its `&str`.
266/// "Parsing" consists of checking that the string is as expected.
267///
268/// We also need the key names for error reporting.
269/// We pass this as a *single* array, and a double-reference to the slice,
270/// since that resolves to one pointer to a static structure.
271pub fn parse_arti_path(
272    arti_path: &ArtiPath,
273    keys: &&[&str],
274    path_parsers: &mut Parsers,
275    leaf_parsers: &mut Parsers,
276) -> Result<(), ArtiPathError> {
277    let path = arti_path.as_str();
278
279    let (path, leaf) = match path.rsplit_once('/') {
280        Some((path, leaf)) => (Some(path), leaf),
281        None => (None, path),
282    };
283
284    let mut keys: &[&str] = keys;
285
286    extract(path, '/', path_parsers, &mut keys)?;
287    extract(Some(leaf), DENOTATOR_SEP, leaf_parsers, &mut keys)?;
288    Ok(())
289}
290
291/// Parse the denotators from the `ArtiPath` of a certificate.
292///
293/// The specified `cert_denos` should be the substring of an `ArtiPath`
294/// containing the certificate denotator group.
295///
296/// The `leaf_parses` and `keys` arguments serve the same purpose as in
297/// [`parse_arti_path`].
298pub fn parse_cert_denotators(
299    cert_denos: &str,
300    keys: &&[&str],
301    leaf_parsers: &mut Parsers,
302) -> Result<(), ArtiPathError> {
303    let mut keys: &[&str] = keys;
304
305    extract(Some(cert_denos), DENOTATOR_SEP, leaf_parsers, &mut keys)?;
306
307    Ok(())
308}
309
310/// Build a `KeyPathInfo` given the information about a key specifier
311///
312/// Calling pattern, to minimise macro-generated machine code,
313/// is similar `arti_path_from_components`.
314///
315/// The macro-generated code parses the path into its KeySpecifier impl
316/// (as an owned value) and then feeds references to the various fields
317/// to `describe_via_components`.
318pub fn describe_via_components(
319    summary: &&str,
320    role: &dyn RawKeySpecifierComponent,
321    extra_keys: &&[&str],
322    extra_info: &[&dyn KeySpecifierComponent],
323) -> Result<KeyPathInfo, Bug> {
324    let mut info = KeyPathInfoBuilder::default();
325    info.summary(summary.to_string());
326    info.role({
327        let mut s = String::new();
328        role.append_to(&mut s)?;
329        s
330    });
331    for (key, value) in izip!(*extra_keys, extra_info) {
332        let value = KeySpecifierComponentPrettyHelper(*value).to_string();
333        info.extra_info(*key, value);
334    }
335    info.build()
336        .map_err(into_internal!("failed to build KeyPathInfo"))
337}
338
339define_derive_deftly! {
340    /// A helper for implementing [`KeySpecifier`]s.
341    ///
342    /// Applies to a struct that has some static components (`prefix`, `role`),
343    /// and a number of variable components represented by its fields.
344    ///
345    /// Implements `KeySpecifier` etc.
346    ///
347    /// Each field is either a path field (which becomes a component in the `ArtiPath`),
348    /// or a denotator (which becomes *part* of the final component in the `ArtiPath`).
349    ///
350    /// The `prefix` is the first component of the [`ArtiPath`] of the [`KeySpecifier`].
351    ///
352    /// The role should be the name of the key in the Tor Specifications.
353    /// The **lowercased** `role` is used as the _prefix of the last component_
354    /// of the [`ArtiPath`] of the specifier.
355    /// The `role` is followed by the denotators of the key.
356    ///
357    /// The denotator fields, if there are any,
358    /// should be annotated with `#[denotator]`.
359    ///
360    /// The declaration order of the fields is important.
361    /// The inner components of the [`ArtiPath`] of the specifier are built
362    /// from the string representation of its path fields, taken in declaration order,
363    /// followed by the encoding of its denotators, also taken in the order they were declared.
364    /// As such, all path fields, must implement [`KeySpecifierComponent`].
365    /// and all denotators must implement [`KeySpecifierComponent`].
366    /// The denotators are separated from the rest of the path, and from each other,
367    /// by `+` characters.
368    ///
369    /// For example, a key specifier with `prefix` `"foo"` and `role` `"bar"`
370    /// will have an [`ArtiPath`] of the form
371    /// `"foo/<field1_str>/<field2_str>/../bar[+<denotators>]"`.
372    ///
373    /// A key specifier of this form, with denotators that encode to "d1" and "d2",
374    /// would look like this: `"foo/<field1_str>/<field2_str>/../bar+d1+d2"`.
375    ///
376    /// ### Results of applying this macro
377    ///
378    /// `#[derive(Deftly)] #[derive_deftly(KeySpecifier)] struct SomeKeySpec ...`
379    /// generates:
380    ///
381    ///  * `impl `[`KeySpecifier`]` for SomeKeySpec`
382    ///  * `struct SomeKeySpecPattern`,
383    ///    a derived struct which contains an `Option` for each field.
384    ///    `None` in the pattern means "any".
385    ///  * `impl `[`KeySpecifierPattern`]` for SomeKeySpecPattern`
386    ///  * `impl TryFrom<`[`KeyPath`]> for SomeKeySpec`
387    ///  * Registration of an impl of [`KeyPathInfoExtractor`]
388    ///    (on a private unit struct `SomeKeySpecInfoExtractor`)
389    ///
390    /// ### Custom attributes
391    ///
392    ///  * **`#[deftly(prefix)]`** (toplevel):
393    ///    Specifies the fixed prefix (the first path component).
394    ///    Must be a literal string.
395    ///
396    ///  * **`#[deftly(role = "...")]`** (toplevel):
397    ///    Specifies the role - the initial portion of the leafname.
398    ///    This should be the name of the key in the Tor Specifications.
399    ///    Must be a literal string.
400    ///    This or the field-level `#[deftly(role)]` must be specified.
401    ///
402    ///  * **`[deftly(role)]` (field):
403    ///    Specifies that the role is determined at runtime.
404    ///    The field type must implement [`KeyDenotator`].
405    ///
406    ///  * **`#[deftly(summary = "...")]`** (summary, mandatory):
407    ///    Specifies the summary; ends up as the `summary` field in [`KeyPathInfo`].
408    ///    (See [`KeyPathInfoBuilder::summary()`].)
409    ///    Must be a literal string.
410    ///
411    ///  * **`#[deftly(denotator)]`** (field):
412    ///    Designates a field that should be represented
413    ///    in the key file leafname, after the role.
414    ///
415    ///  * **`#[deftly(ctor_path = VARIANT)]`** (toplevel):
416    ///    Specifies that this kind of key has a representation in C Tor keystores,
417    ///    and provides the appropriate [`CTorPath`] variant in `VARIANT`.
418    ///
419    ///    Used for implementing [`CTorKeySpecifier`].
420    ///
421    ///    If specified, the generated [`KeySpecifier::ctor_path`] implementation
422    ///    will return [`CTorPath`]::`VARIANT` populated with the fields extracted
423    ///    from this type. Therefore, your type **must** have exactly the same fields
424    ///    as the specified `CTorPath` variant.
425    ///
426    ///    If not specified, the generated [`KeySpecifier::ctor_path`]
427    ///    implementation will always return `None`.
428    ///
429    ///  * **`#[deftly(fixed_path_component = STR)]`** (field):
430    ///    Before this field insert a fixed path component `STR`.
431    ///    (Can be even used before a denotator component,
432    ///    to add a final fixed path component.)
433    ///
434    ///  * **`#[deftly(keypair_specifier = TYPE)]`** (field):
435    ///    If this is the specifier for a public key, the specifier for
436    ///    the corresponding keypair type.
437    ///
438    ///    If not specified, the generated [`KeySpecifier::keypair_specifier`]
439    ///    implementation will always return `None`.
440    //
441    //     NOTE: The `KeySpecifier::keypair_specifier` implementation
442    //     of the `ArtiPath` of a public key will always return `None`,
443    //     even if the public key specifier it represents has a keypair specifier.
444    //
445    ///
446    export KeySpecifier for struct, beta_deftly, meta_quoted rigorous:
447
448    // A condition that evaluates to `true` for path fields.
449    ${defcond F_IS_PATH not(any(fmeta(denotator), fmeta(role)))}
450    ${defcond F_IS_ROLE all(fmeta(role), not(tmeta(role)))}
451
452    #[doc = concat!("Pattern matching some or all [`", stringify!($tname), "`]")]
453    #[allow(dead_code)] // Not everyone will need the pattern feature
454    #[non_exhaustive]
455    $tvis struct $<$tname Pattern><$tdefgens>
456    where $twheres
457    ${vdefbody $vname $(
458        ${fattrs doc}
459        ///
460        /// `None` to match keys with any value for this field.
461        $fvis $fname: Option<$ftype>,
462    ) }
463
464    // ** MAIN KNOWLEDGE OF HOW THE PATH IS CONSTRUCTED **
465    //
466    // These two user-defined expansions,
467    //   $ARTI_PATH_COMPONENTS
468    //   $ARTI_LEAF_COMPONENTS
469    // expand to code for handling each path and leaf component,
470    // in the order in which they appear in the ArtiPath.
471    //
472    // The "code for handling", by default, is:
473    //   - for a field, take a reference to the field in `self`
474    //   - for a fixed component, take a reference to a &'static str
475    // in each case with a comma appended.
476    // So this is suitable for including in a &[&dyn ...].
477    //
478    // The call site can override the behaviour by locally redefining,
479    // the two user-defined expansions DO_FIELD and DO_LITERAL.
480    //
481    // DO_FIELD should expand to the code necessary to handle a field.
482    // It probably wants to refer to $fname.
483    //
484    // DO_LITERAL should expand to the code necessary to handle a literal value.
485    // When DO_LITERAL is called the user-defined expansion LIT will expand to
486    // something like `${fmeta(...) as str}`, which will in turn expand to
487    // a string literal.
488    //
489    // For use sites which want to distinguish the role from other fields:
490    // DO_ROLE_FIELD and DO_ROLE_LITERAL are used for the role.
491    // They default to expanding $DO_FIELD and $DO_LITERAL respectively.
492    //
493    // This is the *only* place that knows how ArtiPaths are constructed,
494    // when the path syntax is defined using the KeySpecifier d-a macro.
495    //
496    // The actual code here is necessarily rather abstract.
497    ${define ARTI_PATH_COMPONENTS {
498        // #[deftly(prefix = ...)]
499        ${define LIT ${tmeta(prefix) as str}}
500        $DO_LITERAL
501
502        ${for fields {
503            // #[deftly(fixed_path_component = ...)]
504            ${if fmeta(fixed_path_component) {
505                // IWVNI d-a allowed arguments to use-defined expansions, but this will do
506                ${define LIT ${fmeta(fixed_path_component) as str}}
507                $DO_LITERAL
508            }}
509            // Path fields
510            ${if F_IS_PATH { $DO_FIELD }}
511        }}
512    }}
513    ${define ARTI_LEAF_COMPONENTS {
514        ${if tmeta(role) {
515            // #[deftly(role = ...)] on the toplevel
516            ${define LIT { stringify!(${snake_case ${tmeta(role)}}) }}
517            $DO_ROLE_LITERAL
518        }}
519        ${for fields {
520            // #[deftly(role)] on a field
521            ${if F_IS_ROLE { $DO_ROLE_FIELD }}
522        }}
523        ${for fields {
524            // #[deftly(denotator)]
525            ${if fmeta(denotator) { $DO_FIELD }}
526        }}
527    }}
528
529    ${define DO_FIELD { &self.$fname, }}
530    ${define DO_LITERAL { &$LIT, }}
531    ${define DO_ROLE_FIELD { $DO_FIELD }}
532    ${define DO_ROLE_LITERAL { $DO_LITERAL }}
533
534    impl<$tgens> $crate::KeySpecifier for $ttype
535    where $twheres
536    {
537        fn arti_path(
538            &self,
539        ) -> std::result::Result<$crate::ArtiPath, $crate::ArtiPathUnavailableError> {
540            use $crate::key_specifier_derive::*;
541
542            arti_path_from_components(
543                &[ $ARTI_PATH_COMPONENTS ],
544                &[ $ARTI_LEAF_COMPONENTS ],
545            )
546        }
547
548        fn ctor_path(&self) -> Option<$crate::CTorPath> {
549            <Self as $crate::CTorKeySpecifier>::ctor_path(self)
550        }
551
552        fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
553            ${if tmeta(keypair_specifier) {
554                Some(Box::new(std::convert::Into::<
555                    ${tmeta(keypair_specifier) as ty}
556                >::into(self)))
557            } else {
558                None
559            }}
560        }
561    }
562
563    impl<$tgens> $crate::KeySpecifierPattern for $<$tname Pattern><$tdefgens>
564    where $twheres
565    {
566        fn arti_pattern(
567            &self,
568        ) -> std::result::Result<$crate::KeyPathPattern, $crate::key_specifier_derive::Bug> {
569            use $crate::key_specifier_derive::*;
570
571            arti_pattern_from_components(
572                &[ $ARTI_PATH_COMPONENTS ],
573                &[ $ARTI_LEAF_COMPONENTS ],
574            )
575        }
576
577        fn new_any() -> Self {
578            $< $tname Pattern > {
579                $( $fname: None, )
580            }
581        }
582    }
583
584    struct $< $tname InfoExtractor >;
585
586    impl<$tgens> $crate::KeyPathInfoExtractor for $< $tname InfoExtractor >
587    where $twheres
588    {
589        fn describe(
590            &self,
591            path: &$crate::KeyPath,
592        ) -> std::result::Result<$crate::KeyPathInfo, $crate::KeyPathError> {
593            use $crate::key_specifier_derive::*;
594
595            // Parse this path
596            #[allow(unused_variables)] // Unused if no fields
597            let spec = $ttype::try_from(path)?;
598
599            // none of this cares about non-role literals
600            // all the others three be explicitly defined each time
601            ${define DO_LITERAL {}}
602
603            static NON_ROLE_FIELD_KEYS: &[&str] = &[
604                ${define DO_FIELD { stringify!($fname), }}
605                ${define DO_ROLE_FIELD {}}
606                ${define DO_ROLE_LITERAL {}}
607                $ARTI_PATH_COMPONENTS
608                $ARTI_LEAF_COMPONENTS
609            ];
610
611            describe_via_components(
612                &${tmeta(summary) as str},
613
614                // role
615                ${define DO_FIELD {}}
616                ${define DO_ROLE_FIELD { &spec.$fname, }}
617                ${define DO_ROLE_LITERAL { &$LIT, }}
618                $ARTI_LEAF_COMPONENTS
619
620                &NON_ROLE_FIELD_KEYS,
621
622                &[
623                    ${define DO_FIELD { &spec.$fname, }}
624                    ${define DO_ROLE_FIELD {}}
625                    ${define DO_ROLE_LITERAL {}}
626                    $ARTI_PATH_COMPONENTS
627                    $ARTI_LEAF_COMPONENTS
628                ],
629            ).map_err($crate::KeyPathError::Bug)
630        }
631    }
632
633    impl<$tgens> TryFrom<&$crate::KeyPath> for $tname
634    where $twheres
635    {
636        type Error = $crate::KeyPathError;
637
638        fn try_from(path: &$crate::KeyPath) -> std::result::Result<$tname, Self::Error> {
639            use $crate::key_specifier_derive::*;
640
641            static FIELD_KEYS: &[&str] = &[
642                ${define DO_LITERAL {}}
643                ${define DO_FIELD { stringify!($fname), }}
644                $ARTI_PATH_COMPONENTS
645                $ARTI_LEAF_COMPONENTS
646            ];
647
648            #[allow(unused_mut)] // not needed if there are no fields
649            #[allow(unused_variables)] // not needed if there are no fields
650            let mut builder =
651                <$<$tname Pattern>::<$tgens> as $crate::KeySpecifierPattern>::new_any();
652
653            ${define DO_FIELD { &mut builder.$fname, }}
654            ${define DO_LITERAL { &mut $LIT, }}
655
656            #[allow(unused_variables)] // CTorPath is only used with ctor_path(..)
657            match path {
658                $crate::KeyPath::Arti(path) => {
659                    parse_arti_path(
660                        path,
661                        &FIELD_KEYS,
662                        &mut [ $ARTI_PATH_COMPONENTS ],
663                        &mut [ $ARTI_LEAF_COMPONENTS ],
664                    ).map_err(|err| $crate::KeyPathError::Arti { path: path.clone(), err })?;
665                },
666                $crate::KeyPath::CTor(path) => {
667                    return <Self as $crate::CTorKeySpecifier>::from_ctor_path(path.clone())
668                        .map_err(|err| $crate::KeyPathError::CTor { path: path.clone(), err });
669                },
670                #[allow(unreachable_patterns)] // This is reachable if used outside of tor-keymgr
671                &_ => {
672                    return Err(internal!("unrecognized key path?!").into());
673                }
674            };
675
676            #[allow(unused_variables)] // not needed if there are no fields
677            let handle_none = || internal!("bad RawKeySpecifierComponentParser impl");
678
679            Ok($tname { $(
680                $fname: builder.$fname.ok_or_else(handle_none)?,
681            ) })
682        }
683    }
684
685    ${if tmeta(ctor_path) {
686
687    ${define CTOR_PATH_VARIANT ${tmeta(ctor_path) as path}}
688
689    impl<$tgens> $crate::CTorKeySpecifier for $ttype
690    where $twheres
691    {
692        fn ctor_path(&self) -> Option<$crate::CTorPath> {
693            Some($crate::CTorPath :: $CTOR_PATH_VARIANT {
694                $( $fname: self.$fname.clone(), )
695            })
696        }
697
698        fn from_ctor_path(
699            path: $crate::CTorPath
700        ) -> std::result::Result<Self, $crate::CTorPathError> {
701
702            match path {
703                $crate::CTorPath :: $CTOR_PATH_VARIANT { $( $fname, )} => {
704                    Ok( Self { $( $fname, ) })
705                },
706                _ => Err($crate::CTorPathError::KeySpecifierMismatch(stringify!($tname).into())),
707            }
708        }
709    }
710
711    } else {
712    impl<$tgens> $crate::CTorKeySpecifier for $ttype
713    where $twheres
714    {
715        fn ctor_path(&self) -> Option<$crate::CTorPath> {
716            None
717        }
718
719        fn from_ctor_path(
720            _: $crate::CTorPath
721        ) -> std::result::Result<Self, $crate::CTorPathError> {
722            Err($crate::CTorPathError::MissingCTorPath(stringify!($tname).to_string()))
723        }
724    }
725
726    }}
727
728    // Register the info extractor with `KeyMgr`.
729    $crate::inventory::submit!(&$< $tname InfoExtractor > as &dyn $crate::KeyPathInfoExtractor);
730}
731
732#[cfg(feature = "experimental-api")]
733define_derive_deftly! {
734    /// A helper for implementing [`KeyCertificateSpecifier`]s.
735    ///
736    /// ### Results of applying this macro
737    ///
738    /// `#[derive(Deftly)] #[derive_deftly(CertSpecifier)] struct SomeCertSpec ...`
739    /// generates:
740    ///
741    ///  * `impl `[`KeyCertificateSpecifier`]` for SomeCertSpec`
742    ///  * `struct SomeCertSpecPattern`,
743    ///    a derived struct which contains an `Option` for each denotator field,
744    ///    and a non-optional field for the subject key `KeyPathPattern`.
745    ///    `None` in the pattern means "any".
746    ///  * `impl `[`CertSpecifierPattern`]` for SomeCertSpecPattern`
747    ///  * `impl TryFrom<`[`KeyPath`]> for SomeCertSpec`
748    ///
749    ///
750    /// ### Custom attributes
751    ///
752    ///  * **`#[deftly(subject)]`** (mandatory, field):
753    ///    Designates a field that represents the subject key specifier.
754    ///    This should only be applied to **one** field.
755    ///
756    ///  * **`#[deftly(denotator)]`** (field):
757    ///    Designates a field that should be represented
758    ///    in the key file leafname.
759    ///    The `ArtiPath` of the certificate is derived from the `ArtiPath`
760    ///    of the subject key,  by concatenating the `ArtiPath` of the subject
761    ///    key with provided denotators provided.
762    ///    If no there are no denotators, the `ArtiPath` of the certificate
763    ///    is the same as the `ArtiPath` of the subject key.
764    export CertSpecifier beta_deftly, for struct:
765
766    // Ensure exactly one field annotated with #[deftly(subject)]
767    ${if not(approx_equal(${for fields { ${when fmeta(subject)} 1 }}, 1))
768        { ${error "Exactly one field must be #[deftly(subject)]"} }
769    }
770
771    // All fields must be either #[deftly(subject)] or #[deftly(denotator)]
772    $(
773        ${when not(any(
774            fmeta(subject),
775            fmeta(denotator),
776        ))}
777
778        ${error
779            message=${concat $fname " must be #[deftly(subject)] or #[deftly(denotator)]"}
780        }
781    )
782
783    ${define SUBJ_FNAME
784        ${for fields {
785            ${if fmeta(subject) {
786                &self.$fname
787            }}
788        }}
789    }
790
791    ${define SUBJ_FTYPE
792        ${for fields {
793            ${if fmeta(subject) {
794                $ftype
795            }}
796        }}
797    }
798
799    ${define SUBJ_PATTERN_FTYPE
800        ${for fields {
801            ${if fmeta(subject) {
802                $<$ftype Pattern>
803            }}
804        }}
805    }
806
807    impl<$tgens> $crate::KeyCertificateSpecifier for $tname<$tdefgens>
808    where $twheres
809    {
810        fn cert_denotators(&self) -> Vec<&dyn $crate::KeySpecifierComponent> {
811            vec![
812                ${for fields {
813                    // #[deftly(denotator)]
814                    ${if fmeta(denotator) { &self.$fname, }}
815                }}
816            ]
817        }
818
819        fn subject_key_specifier(&self) -> &dyn $crate::KeySpecifier {
820            ${SUBJ_FNAME}
821        }
822    }
823
824    #[doc = concat!("Pattern matching some or all [`", stringify!($tname), "`]")]
825    #[allow(dead_code)] // Not everyone will need the pattern feature
826    #[non_exhaustive]
827    $tvis struct $<$tname Pattern><$tdefgens>
828    where $twheres
829    {
830        ${for fields {
831            // The subject key specifier pattern is non-optional,
832            // because we derive the fixed part of the pattern from it
833            ${if fmeta(subject) {
834                ${fattrs doc}
835                $fvis $fname: $<$ftype Pattern>,
836            }}
837            // The denotators are optional
838            ${if fmeta(denotator) {
839                ${fattrs doc}
840                $fvis $fname: Option<$ftype>,
841            }}
842        }}
843    }
844
845    ${define DO_FIELD { &self.$fname, }}
846
847    ${define ARTI_LEAF_COMPONENTS {
848        ${for fields {
849            // #[deftly(denotator)]
850            ${if fmeta(denotator) { $DO_FIELD }}
851        }}
852    }}
853
854    impl<$tgens> $crate::CertSpecifierPattern for $<$tname Pattern><$tdefgens>
855    where $twheres
856    {
857        type SubjectKeySpecifierPattern = ${SUBJ_PATTERN_FTYPE};
858
859        fn arti_pattern(
860            &self,
861        ) -> std::result::Result<$crate::KeyPathPattern, $crate::key_specifier_derive::Bug> {
862            use $crate::key_specifier_derive::*;
863            use $crate::KeyPathPattern::*;
864            use $crate::KeySpecifierPattern as _;
865
866            let subj_path_pat = ${SUBJ_FNAME}.arti_pattern()?;
867            let subj_path = match subj_path_pat {
868                Arti(path) => path,
869                _ => {
870                    return Err(
871                        tor_error::internal!("subject key pattern is not an Arti pattern?!").into()
872                    );
873                }
874            };
875
876            cert_arti_pattern_from_components(
877                &subj_path,
878                &[ $ARTI_LEAF_COMPONENTS ],
879            )
880        }
881
882        fn new_any() -> Self {
883            // Build the "any" pattern of the subject key.
884            let spec =
885                < <$<$tname Pattern>::<$tgens> as $crate::CertSpecifierPattern>::SubjectKeySpecifierPattern
886                    as $crate::KeySpecifierPattern>::new_any();
887
888            $< $tname Pattern > {
889                $(
890                    ${if fmeta(subject) { $fname: spec, }}
891                )
892                $(
893                    ${if fmeta(denotator) { $fname: None, }}
894                )
895            }
896        }
897    }
898
899    impl<$tgens> TryFrom<&$crate::KeyPath> for $tname
900    where $twheres
901    {
902        type Error = $crate::KeyPathError;
903
904        fn try_from(path: &$crate::KeyPath) -> std::result::Result<$tname, Self::Error> {
905            use $crate::key_specifier_derive::*;
906
907            let arti_path = match path {
908                $crate::KeyPath::Arti(path) => path,
909                &_ => {
910                    return Err(tor_error::bad_api_usage!("Cert specifiers never have non-ArtiPaths").into());
911                }
912            };
913
914            #[allow(unused_mut)] // not needed if there are no fields
915            #[allow(unused_variables)] // not needed if there are no fields
916            let mut builder =
917                <$<$tname Pattern>::<$tgens> as $crate::CertSpecifierPattern>::new_any();
918
919            let subj_key = if let Some((key_path, cert_denos)) = arti_path.split_once($crate::DENOTATOR_GROUP_SEP) {
920
921                // Handle the special case where the subject key ArtiPath has no denotators,
922                // but the cert ArtiPath *does*. This translates to paths that contain
923                // the slightly odd +@ construction, where + designates the beginning
924                // of the denotator section, followed by an empty denotator group.
925                let key_path = match key_path.strip_suffix('+') {
926                    Some(p) => p,
927                    None => key_path,
928                };
929
930                let key_arti_path = $crate::ArtiPath::new(key_path.to_string())
931                    .map_err(tor_error::into_internal!("cert path contains invalid key ArtiPath?!"))?;
932
933                static FIELD_KEYS: &[&str] = &[
934                    ${for fields {
935                        // #[deftly(denotator)]
936                        ${if fmeta(denotator) { stringify!($fname), }}
937                    }}
938                ];
939
940                parse_cert_denotators(
941                    cert_denos,
942                    &FIELD_KEYS,
943                    ${define DO_FIELD { &mut builder.$fname, }}
944                    &mut [ $ARTI_LEAF_COMPONENTS ],
945                ).map_err(|err| $crate::KeyPathError::Arti { path: arti_path.clone(), err })?;
946
947                let key_spec = $crate::KeyPath::Arti(key_arti_path);
948
949                $SUBJ_FTYPE::try_from(&key_spec)?
950            } else {
951                // Cert has no denotators, so the whole path is the path
952                // of the subject key specifier
953                $SUBJ_FTYPE::try_from(path)?
954            };
955
956            #[allow(unused_variables)] // not needed if there are no fields
957            let handle_none = || internal!("bad RawKeySpecifierComponentParser impl");
958
959            Ok($tname {
960                ${for fields {
961                    // The denotators are optional...
962                    ${if fmeta(denotator) {
963                        $fname: builder.$fname.ok_or_else(handle_none)?,
964                    }}
965                    // ...but subject key specifier pattern is not
966                    ${if fmeta(subject) {
967                        $fname: subj_key,
968                    }}
969                }}
970            })
971        }
972    }
973
974    // TODO: generate and register a KeyPathInfoExtractor impl for cert specifiers
975    // (so that KeyMgr::describe() can describe them)
976}
977
978pub use derive_deftly_template_KeySpecifier;
979
980#[cfg(feature = "experimental-api")]
981pub use derive_deftly_template_CertSpecifier;