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;