sequoia_openpgp/regex/mod.rs
1//! OpenPGP regex parser.
2//!
3//! OpenPGP defines a [regular expression language]. It is used with
4//! [trust signatures] to scope the trust that they extend.
5//!
6//! [regular expression language]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
7//! [trust signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
8//!
9//! Compared with most regular expression languages, OpenPGP's is
10//! quite simple. In particular, it only includes the following
11//! features:
12//!
13//! - Alternations using `|`,
14//! - Grouping using `(` and `)`,
15//! - The `*`, `+`, and `?` glob operators,
16//! - The `^`, and `$` anchors,
17//! - The '.' operator, positive *non-empty* ranges
18//! (e.g. `[a-zA-Z]`) and negative *non-empty* ranges (`[^@]`), and
19//! - The backslash operator to escape special characters (except
20//! in ranges).
21//!
22//! The regular expression engine defined in this module implements
23//! that language with two differences. The first difference is that
24//! the compiler only works on UTF-8 strings (not bytes). The second
25//! difference is that ranges in character classes are between UTF-8
26//! characters, not just ASCII characters.
27//!
28//! # Data Structures
29//!
30//! This module defines two data structures. [`Regex`] encapsulates a
31//! valid regular expression, and provides methods to check whether
32//! the regular expression matches a string or a [`UserID`].
33//! [`RegexSet`] is similar, but encapsulates zero or more regular
34//! expressions, which may or may not be valid. Its match methods
35//! return `true` if there are no regular expressions, or, if there is
36//! at least one regular expression, they return whether at least one
37//! of the regular expressions matches it. `RegexSet`'s matcher
38//! handles invalid regular expressions by considering them to be
39//! regular expressions that don't match anything. These semantics
40//! are consistent with a trust signature's scoping rules. Further,
41//! strings that contain control characters never match. This
42//! behavior can be overridden using [`Regex::disable_sanitizations`]
43//! and [`RegexSet::disable_sanitizations`].
44//!
45//! [`UserID`]: crate::packet::UserID
46//! [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
47//! [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
48//!
49//! # Scoped Trust Signatures
50//!
51//! To create a trust signature, you create a signature whose [type]
52//! is either [GenericCertification], [PersonaCertification],
53//! [CasualCertification], [PositiveCertification], or [DirectKey],
54//! and add a [Trust Signature] subpacket using, for instance, the
55//! [`SignatureBuilder::set_trust_signature`] method.
56//!
57//! [type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.1
58//! [GenericCertification]: crate::types::SignatureType::GenericCertification
59//! [PersonaCertification]: crate::types::SignatureType::PersonaCertification
60//! [CasualCertification]: crate::types::SignatureType::CasualCertification
61//! [PositiveCertification]: crate::types::SignatureType::PositiveCertification
62//! [DirectKey]: crate::types::SignatureType::DirectKey
63//! [Trust Signature]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
64//! [`SignatureBuilder::set_trust_signature`]: crate::packet::signature::SignatureBuilder::set_trust_signature()
65//!
66//! To scope a trust signature, you add a [Regular Expression
67//! subpacket] to it using
68//! [`SignatureBuilder::set_regular_expression`] or
69//! [`SignatureBuilder::add_regular_expression`].
70//!
71//! To extract any regular expressions, you can use
72//! [`SubpacketAreas::regular_expressions`].
73//!
74//! [Regular Expression subpacket]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.22
75//! [`SignatureBuilder::set_regular_expression`]: crate::packet::signature::SignatureBuilder::set_regular_expression()
76//! [`SignatureBuilder::add_regular_expression`]: crate::packet::signature::SignatureBuilder::add_regular_expression()
77//! [`SubpacketAreas::regular_expressions`]: crate::packet::signature::subpacket::SubpacketAreas::regular_expressions()
78//!
79//! # Caveat Emptor
80//!
81//! Note: GnuPG has [very limited regular expression support]. In
82//! particular, it only recognizes regular expressions with the
83//! following form:
84//!
85//! [very limited regular expression support]: https://dev.gnupg.org/source/gnupg/browse/master/g10/trustdb.c;15e065dee891eef9545556f210b4199107999869$1558
86//!
87//! ```text
88//! <[^>]+[@.]example\.com>$
89//! ```
90//!
91//! Further, it escapes any operators between the `<[^>]+[@.]` and the
92//! `>$` except `.` and `\`. Otherwise, GnuPG treats the regular
93//! expression as a literal domain (e.g., `example.com`).
94//!
95//! Further, until [version 2.2.22] (released in August 2020), GnuPG
96//! did not support regular expressions on Windows, and other systems
97//! that don't include `regcomp`. On these systems, if a trust
98//! signature included a regular expression, GnuPG conservatively
99//! considered the whole trust signature to match nothing.
100//!
101//! [version 2.2.22]: https://dev.gnupg.org/T5030
102//!
103//! # Examples
104//!
105//! A CA signs two certificates, one for Alice, who works at
106//! `example.com`, and one for Bob, who is associated with `some.org`.
107//! Carol then creates a trust signature for the CA, which she scopes
108//! to `example.org` and `example.com`. We then confirm that Carol
109//! can use the CA to authenticate Alice, but not Bob.
110//!
111//! ```
112//! use sequoia_openpgp as openpgp;
113//! use openpgp::cert::prelude::*;
114//! use openpgp::packet::prelude::*;
115//! use openpgp::policy::StandardPolicy;
116//! use openpgp::regex::RegexSet;
117//! use openpgp::types::SignatureType;
118//!
119//! # fn main() -> openpgp::Result<()> {
120//! let p = &StandardPolicy::new();
121//!
122//! let (ca, _)
123//! = CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>"))
124//! .generate()?;
125//! let mut ca_signer = ca.primary_key().key().clone()
126//! .parts_into_secret()?.into_keypair()?;
127//! let ca_userid = ca.with_policy(p, None)?
128//! .userids().nth(0).expect("Added a User ID").userid();
129//!
130//! // The CA certifies "Alice <alice@example.com>".
131//! let (alice, _)
132//! = CertBuilder::general_purpose(Some("Alice <alice@example.com>"))
133//! .generate()?;
134//! let alice_userid = alice.with_policy(p, None)?
135//! .userids().nth(0).expect("Added a User ID").userid();
136//! let alice_certification = SignatureBuilder::new(SignatureType::GenericCertification)
137//! .sign_userid_binding(
138//! &mut ca_signer,
139//! alice.primary_key().component(),
140//! alice_userid)?;
141//! let alice = alice.insert_packets(alice_certification.clone())?.0;
142//! # assert!(alice.clone().into_packets().any(|p| {
143//! # match p {
144//! # Packet::Signature(sig) => sig == alice_certification,
145//! # _ => false,
146//! # }
147//! # }));
148//!
149//! // The CA certifies "Bob <bob@some.org>".
150//! let (bob, _)
151//! = CertBuilder::general_purpose(Some("Bob <bob@some.org>"))
152//! .generate()?;
153//! let bob_userid = bob.with_policy(p, None)?
154//! .userids().nth(0).expect("Added a User ID").userid();
155//! let bob_certification = SignatureBuilder::new(SignatureType::GenericCertification)
156//! .sign_userid_binding(
157//! &mut ca_signer,
158//! bob.primary_key().component(),
159//! bob_userid)?;
160//! let bob = bob.insert_packets(bob_certification.clone())?.0;
161//! # assert!(bob.clone().into_packets().any(|p| {
162//! # match p {
163//! # Packet::Signature(sig) => sig == bob_certification,
164//! # _ => false,
165//! # }
166//! # }));
167//!
168//!
169//! // Carol tsigns the CA's certificate.
170//! let (carol, _)
171//! = CertBuilder::general_purpose(Some("Carol <carol@another.net>"))
172//! .generate()?;
173//! let mut carol_signer = carol.primary_key().key().clone()
174//! .parts_into_secret()?.into_keypair()?;
175//!
176//! let ca_tsig = SignatureBuilder::new(SignatureType::GenericCertification)
177//! .set_trust_signature(2, 120)?
178//! .set_regular_expression("<[^>]+[@.]example\\.org>$")?
179//! .add_regular_expression("<[^>]+[@.]example\\.com>$")?
180//! .sign_userid_binding(
181//! &mut carol_signer,
182//! ca.primary_key().component(),
183//! ca_userid)?;
184//! let ca = ca.insert_packets(ca_tsig.clone())?.0;
185//! # assert!(ca.clone().into_packets().any(|p| {
186//! # match p {
187//! # Packet::Signature(sig) => sig == ca_tsig,
188//! # _ => false,
189//! # }
190//! # }));
191//!
192//!
193//! // Carol now tries to authenticate Alice and Bob's certificates
194//! // using the CA as a trusted introducer based on `ca_tsig`.
195//! let res = RegexSet::from_signature(&ca_tsig)?;
196//!
197//! // Should be able to authenticate Alice.
198//! let alice_ua = alice.with_policy(p, None)?
199//! .userids().nth(0).expect("Added a User ID");
200//! # assert!(res.matches_userid(alice_ua.userid()));
201//! let mut authenticated = false;
202//! for c in alice_ua.certifications() {
203//! if c.get_issuers().into_iter().any(|h| h.aliases(ca.key_handle())) {
204//! if c.clone().verify_userid_binding(
205//! ca.primary_key().key(),
206//! alice.primary_key().key(),
207//! alice_ua.userid()).is_ok()
208//! {
209//! authenticated |= res.matches_userid(alice_ua.userid());
210//! }
211//! }
212//! }
213//! assert!(authenticated);
214//!
215//! // But, although the CA has certified Bob's key, Carol doesn't rely
216//! // on it, because Bob's email address ("bob@some.org") is out of
217//! // scope (some.org, not example.com).
218//! let bob_ua = bob.with_policy(p, None)?
219//! .userids().nth(0).expect("Added a User ID");
220//! # assert!(! res.matches_userid(bob_ua.userid()));
221//! let mut have_certification = false;
222//! let mut authenticated = false;
223//! for c in bob_ua.certifications() {
224//! if c.get_issuers().into_iter().any(|h| h.aliases(ca.key_handle())) {
225//! if c.clone().verify_userid_binding(
226//! ca.primary_key().key(),
227//! bob.primary_key().key(),
228//! bob_ua.userid()).is_ok()
229//! {
230//! have_certification = true;
231//! authenticated |= res.matches_userid(bob_ua.userid());
232//! }
233//! }
234//! }
235//! assert!(have_certification);
236//! assert!(! authenticated);
237//! # Ok(()) }
238//! ```
239
240use std::borrow::Borrow;
241use std::fmt;
242
243use lalrpop_util::ParseError;
244use regex_syntax::hir::{self, Hir};
245
246use crate::Error;
247use crate::Result;
248use crate::packet::prelude::*;
249use crate::types::SignatureType;
250
251pub(crate) mod lexer;
252lalrpop_util::lalrpop_mod!(
253 #[allow(clippy::all)]
254 #[allow(unused_parens)]
255 grammar,
256 "/regex/grammar.rs"
257);
258
259pub(crate) use self::lexer::Token;
260pub(crate) use self::lexer::{Lexer, LexicalError};
261
262const TRACE: bool = false;
263
264// Convert tokens into strings.
265//
266// Unfortunately, we can't implement From, because we don't define
267// ParseError in this crate.
268pub(crate) fn parse_error_downcast(e: ParseError<usize, Token, LexicalError>)
269 -> ParseError<usize, String, LexicalError>
270{
271 match e {
272 ParseError::UnrecognizedToken {
273 token: (start, t, end),
274 expected,
275 } => ParseError::UnrecognizedToken {
276 token: (start, t.into(), end),
277 expected,
278 },
279
280 ParseError::ExtraToken {
281 token: (start, t, end),
282 } => ParseError::ExtraToken {
283 token: (start, t.into(), end),
284 },
285
286 ParseError::InvalidToken { location }
287 => ParseError::InvalidToken { location },
288
289 ParseError::User { error }
290 => ParseError::User { error },
291
292 ParseError::UnrecognizedEof { location, expected }
293 => ParseError::UnrecognizedEof { location, expected },
294 }
295}
296
297// Used by grammar.lalrpop to generate a regex class (e.g. '[a-ce]').
298fn generate_class(caret: bool, chars: impl Iterator<Item=char>) -> Hir
299{
300 tracer!(TRACE, "generate_class");
301
302 // Dealing with ranges is a bit tricky. We need to examine three
303 // tokens. If the middle one is a dash, it's a range.
304
305 let chars: Vec<Option<char>> = chars
306 // Pad it out so what we can use windows to get three
307 // characters at a time, and be sure to process all
308 // characters.
309 .map(Some)
310 .chain(std::iter::once(None))
311 .chain(std::iter::once(None))
312 .collect();
313 if chars.len() == 2 {
314 // The grammar doesn't allow an empty class.
315 unreachable!();
316 } else {
317 let r = chars
318 .windows(3)
319 .scan(0,
320 |skip: &mut usize, x: &[Option<char>]|
321 // Scan stops if the result is None.
322 // filter_map keeps only those elements that
323 // are Some.
324 -> Option<Option<hir::ClassUnicodeRange>>
325 {
326 if *skip > 0 {
327 *skip -= 1;
328 t!("Skipping: {:?} (skip now: {})", x, skip);
329 Some(None)
330 } else {
331 match (x[0], x[1], x[2]) {
332 (Some(a), Some('-'), Some(c)) => {
333 // We've got a real range.
334 *skip = 2;
335 t!("range for '{}-{}'", a, c);
336 Some(Some(hir::ClassUnicodeRange::new(a, c)))
337 }
338 (Some(a), _, _) => {
339 t!("range for '{}'", a);
340 Some(Some(hir::ClassUnicodeRange::new(a, a)))
341 }
342 (None, _, _) => unreachable!(),
343 }
344 }
345 })
346 .flatten();
347 let mut class = hir::Class::Unicode(hir::ClassUnicode::new(r));
348 if caret {
349 class.negate();
350 }
351 Hir::class(class)
352 }
353}
354
355/// A compiled OpenPGP regular expression for matching UTF-8 encoded
356/// strings.
357///
358/// A `Regex` contains a regular expression compiled according to the
359/// rules defined in [Section 8 of RFC 9580] modulo two differences.
360/// First, the compiler only works on UTF-8 strings (not bytes).
361/// Second, ranges in character classes are between UTF-8 characters,
362/// not just ASCII characters. Further, by default, strings that
363/// don't pass a sanity check (in particular, include Unicode control
364/// characters) never match. This behavior can be customized using
365/// [`Regex::disable_sanitizations`].
366///
367/// [Section 8 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
368/// [trust signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
369/// [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
370///
371/// Regular expressions are used to scope the trust that [trust
372/// signatures] extend.
373///
374/// When working with trust signatures, you'll usually want to use the
375/// [`RegexSet`] data structure, which already implements the correct
376/// semantics.
377///
378///
379/// See the [module-level documentation] for more details.
380///
381/// [module-level documentation]: self
382///
383/// # A note on equality
384///
385/// We define equality on `Regex` as the equality of the uncompiled
386/// regular expression given to the constructor and whether
387/// sanitizations are enabled.
388#[derive(Clone, Debug)]
389pub struct Regex {
390 /// The original regular expression.
391 ///
392 /// Equality is defined using this and `disable_sanitizations`.
393 re: String,
394 regex: regex::Regex,
395 disable_sanitizations: bool,
396}
397assert_send_and_sync!(Regex);
398
399impl PartialEq for Regex {
400 fn eq(&self, other: &Self) -> bool {
401 self.re == other.re
402 && self.disable_sanitizations == other.disable_sanitizations
403 }
404}
405
406impl Eq for Regex {}
407
408impl Regex {
409 /// Parses and compiles the regular expression.
410 ///
411 /// By default, strings that don't pass a sanity check (in
412 /// particular, include Unicode control characters) never match.
413 /// This behavior can be customized using
414 /// [`Regex::disable_sanitizations`].
415 ///
416 /// [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
417 pub fn new(re: &str) -> Result<Self>
418 {
419 let lexer = Lexer::new(re);
420 let hir = match grammar::RegexParser::new().parse(re, lexer) {
421 Ok(hir) => hir,
422 Err(err) => return Err(parse_error_downcast(err).into()),
423 };
424
425 // Converting the Hir to a string and the compiling that is
426 // apparently the canonical way to convert a Hir to a Regex
427 // (at least it is what rip-grep does), which the author of
428 // regex also wrote. See
429 // ripgrep/crates/regex/src/config.rs:ConfiguredHir::regex.
430 let regex = regex::RegexBuilder::new(&hir.to_string())
431 .build()?;
432
433 Ok(Self {
434 re: re.into(),
435 regex,
436 disable_sanitizations: false,
437 })
438 }
439
440 /// Parses and compiles the regular expression.
441 ///
442 /// Returns an error if `re` is not a valid UTF-8 string.
443 ///
444 /// By default, strings that don't pass a sanity check (in
445 /// particular, include Unicode control characters) never match.
446 /// This behavior can be customized using
447 /// [`Regex::disable_sanitizations`].
448 ///
449 /// [`Regex::disable_sanitizations`]: Regex::disable_sanitizations()
450 pub fn from_bytes(re: &[u8]) -> Result<Self> {
451 Self::new(std::str::from_utf8(re)?)
452 }
453
454 /// Returns the string-representation of the regular expression.
455 pub fn as_str(&self) -> &str {
456 &self.re
457 }
458
459 /// Controls whether matched strings must pass a sanity check.
460 ///
461 /// If `false` (the default), i.e., sanity checks are enabled, and
462 /// the string doesn't pass the sanity check (in particular, it
463 /// contains a Unicode control character according to
464 /// [`char::is_control`], including newlines and an embedded `NUL`
465 /// byte), this returns `false`.
466 ///
467 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
468 pub fn disable_sanitizations(&mut self, disabled: bool) {
469 self.disable_sanitizations = disabled;
470 }
471
472 /// Returns whether the regular expression matches the string.
473 ///
474 /// If sanity checks are enabled (the default) and the string
475 /// doesn't pass the sanity check (in particular, it contains a
476 /// Unicode control character according to [`char::is_control`],
477 /// including newlines and an embedded `NUL` byte), this returns
478 /// `false`.
479 ///
480 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
481 pub fn is_match(&self, s: &str) -> bool {
482 if ! self.disable_sanitizations && s.chars().any(char::is_control) {
483 return false;
484 }
485
486 self.is_match_clean(s)
487 }
488
489 // is_match, but without the sanity check.
490 fn is_match_clean(&self, s: &str) -> bool {
491 self.regex.is_match(s)
492 }
493
494 /// Returns whether the regular expression matches the User ID.
495 ///
496 /// If the User ID is not a valid UTF-8 string, this returns
497 /// `false`.
498 ///
499 /// If sanity checks are enabled (the default) and the string
500 /// doesn't pass the sanity check (in particular, it contains a
501 /// Unicode control character according to [`char::is_control`],
502 /// including newlines and an embedded `NUL` byte), this returns
503 /// `false`.
504 ///
505 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
506 pub fn matches_userid(&self, u: &UserID) -> bool {
507 if let Ok(u) = std::str::from_utf8(u.value()) {
508 self.is_match(u)
509 } else {
510 false
511 }
512 }
513}
514
515#[derive(Clone, Debug)]
516enum RegexSet_ {
517 Regex(Regex),
518 Invalid,
519 Everything,
520}
521assert_send_and_sync!(RegexSet_);
522
523/// A set of regular expressions.
524///
525/// A `RegexSet` encapsulates a set of regular expressions. The
526/// regular expressions are compiled according to the rules defined in
527/// [Section 8 of RFC 9580] modulo two differences. First, the
528/// compiler only works on UTF-8 strings (not bytes). Second, ranges
529/// in character classes are between UTF-8 characters, not just ASCII
530/// characters. Further, by default, strings that don't pass a sanity
531/// check (in particular, include Unicode control characters) never
532/// match. This behavior can be customized using
533/// [`RegexSet::disable_sanitizations`].
534///
535/// [Section 8 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-8
536/// [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
537///
538/// `RegexSet` implements the semantics of [regular expression]s used
539/// in [Trust Signatures]. In particular, a `RegexSet` makes it
540/// easier to deal with trust signatures that:
541///
542/// - Contain multiple Regular Expression subpackts,
543/// - Have no Regular Expression subpackets, and/or
544/// - Include one or more Regular Expression subpackets that are invalid.
545///
546/// [regular expressions]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.22
547/// [Trust Signatures]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
548///
549/// `RegexSet` compiles each regular expression individually. If
550/// there are no regular expressions, the `RegexSet` matches
551/// everything. If a regular expression is invalid, `RegexSet` treats
552/// it as if it doesn't match anything. Thus, if all regular
553/// expressions are invalid, the `RegexSet` matches nothing (not
554/// everything!).
555///
556/// See the [module-level documentation] for more details.
557///
558/// [module-level documentation]: self
559///
560/// # A note on equality
561///
562/// We define equality on `RegexSet` as the equality of the uncompiled
563/// regular expressions given to the constructor and whether
564/// sanitizations are enabled.
565#[derive(Clone)]
566pub struct RegexSet {
567 /// The original regular expressions.
568 ///
569 /// Equality is defined using this and `disable_sanitizations`.
570 re_bytes: Vec<Vec<u8>>,
571 re_set: RegexSet_,
572 disable_sanitizations: bool,
573}
574assert_send_and_sync!(RegexSet);
575
576impl PartialEq for RegexSet {
577 fn eq(&self, other: &Self) -> bool {
578 self.re_bytes == other.re_bytes
579 && self.disable_sanitizations == other.disable_sanitizations
580 }
581}
582
583impl Eq for RegexSet {}
584
585impl fmt::Debug for RegexSet {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 let mut d = f.debug_struct("RegexSet");
588 match self.re_set {
589 RegexSet_::Everything => {
590 d.field("regex", &"<Everything>")
591 }
592 RegexSet_::Invalid => {
593 d.field("regex", &"<Invalid>")
594 }
595 RegexSet_::Regex(ref r) => {
596 d.field("regex", &r.regex)
597 }
598 }
599 .field("sanitizations", &!self.disable_sanitizations)
600 .finish()
601 }
602}
603
604impl RegexSet {
605 /// Parses and compiles the regular expressions.
606 ///
607 /// Invalid regular expressions do not cause this to fail. See
608 /// [`RegexSet`]'s top-level documentation for details.
609 ///
610 ///
611 /// By default, strings that don't pass a sanity check (in
612 /// particular, include Unicode control characters) never match.
613 /// This behavior can be customized using
614 /// [`RegexSet::disable_sanitizations`].
615 ///
616 /// [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
617 ///
618 /// # Examples
619 ///
620 /// ```
621 /// use sequoia_openpgp as openpgp;
622 /// use openpgp::regex::RegexSet;
623 ///
624 /// # fn main() -> openpgp::Result<()> {
625 /// // Extract the regex and compile it.
626 /// let res = &[
627 /// "<[^>]+[@.]example\\.org>$",
628 /// // Invalid.
629 /// "[..",
630 /// ];
631 ///
632 /// let res = RegexSet::new(res)?;
633 ///
634 /// assert!(res.is_match("Alice <alice@example.org>"));
635 /// assert!(! res.is_match("Bob <bob@example.com>"));
636 /// # Ok(()) }
637 /// ```
638 pub fn new<'a, RE, I>(res: I) -> Result<Self>
639 where RE: Borrow<&'a str>,
640 I: IntoIterator<Item=RE>,
641 {
642 tracer!(TRACE, "RegexSet::new");
643
644 let mut regexes = Vec::with_capacity(2);
645 let mut had_good = false;
646 let mut had_bad = false;
647
648 let mut re_bytes = Vec::new();
649 for re in res {
650 let re = re.borrow();
651 re_bytes.push(re.as_bytes().into());
652
653 let lexer = Lexer::new(re);
654 match grammar::RegexParser::new().parse(re, lexer) {
655 Ok(hir) => {
656 had_good = true;
657 regexes.push(hir);
658 }
659 Err(err) => {
660 had_bad = true;
661 t!("Compiling {:?}: {}", re, err);
662 }
663 }
664 }
665
666 if had_bad && ! had_good {
667 t!("All regular expressions were invalid.");
668 Ok(RegexSet {
669 re_bytes,
670 re_set: RegexSet_::Invalid,
671 disable_sanitizations: false,
672 })
673 } else if ! had_bad && ! had_good {
674 // Match everything.
675 t!("No regular expressions provided.");
676 Ok(RegexSet {
677 re_bytes,
678 re_set: RegexSet_::Everything,
679 disable_sanitizations: false,
680 })
681 } else {
682 // Match any of the regular expressions.
683 Ok(RegexSet {
684 re_bytes,
685 re_set: RegexSet_::Regex(
686 Regex {
687 re: String::new(),
688 regex: regex::RegexBuilder::new(
689 &Hir::alternation(regexes).to_string())
690 .build()?,
691 disable_sanitizations: false,
692 }),
693 disable_sanitizations: false,
694 })
695 }
696 }
697
698 /// Parses and compiles the regular expressions.
699 ///
700 /// The regular expressions are first converted to UTF-8 strings.
701 /// Byte sequences that are not valid UTF-8 strings are considered
702 /// to be invalid regular expressions. Invalid regular
703 /// expressions do not cause this to fail. See [`RegexSet`]'s
704 /// top-level documentation for details.
705 ///
706 ///
707 /// By default, strings that don't pass a sanity check (in
708 /// particular, include Unicode control characters) never match.
709 /// This behavior can be customized using
710 /// [`RegexSet::disable_sanitizations`].
711 ///
712 /// [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
713 ///
714 /// # Examples
715 ///
716 /// ```
717 /// use sequoia_openpgp as openpgp;
718 /// use openpgp::regex::RegexSet;
719 ///
720 /// # fn main() -> openpgp::Result<()> {
721 /// // A valid and an invalid UTF-8 byte sequence. The invalid
722 /// // sequence doesn't match anything. But, that doesn't impact
723 /// // the other regular expressions.
724 /// let res: &[ &[u8] ] = &[
725 /// &b"<[^>]+[@.]example\\.org>$"[..],
726 /// // Invalid UTF-8.
727 /// &b"\xC3\x28"[..],
728 /// ];
729 /// assert!(std::str::from_utf8(res[0]).is_ok());
730 /// assert!(std::str::from_utf8(res[1]).is_err());
731 ///
732 /// let re_set = RegexSet::from_bytes(res.into_iter())?;
733 ///
734 /// assert!(re_set.is_match("Alice <alice@example.org>"));
735 /// assert!(! re_set.is_match("Bob <bob@example.com>"));
736 ///
737 /// // If we only have invalid UTF-8 strings, then nothing
738 /// // matches.
739 /// let res: &[ &[u8] ] = &[
740 /// // Invalid UTF-8.
741 /// &b"\xC3\x28"[..],
742 /// ];
743 /// assert!(std::str::from_utf8(res[0]).is_err());
744 ///
745 /// let re_set = RegexSet::from_bytes(res.into_iter())?;
746 ///
747 /// assert!(! re_set.is_match("Alice <alice@example.org>"));
748 /// assert!(! re_set.is_match("Bob <bob@example.com>"));
749 ///
750 ///
751 /// // But, if we have no regular expressions, everything matches.
752 /// let res: &[ &[u8] ] = &[];
753 /// let re_set = RegexSet::from_bytes(res.into_iter())?;
754 ///
755 /// assert!(re_set.is_match("Alice <alice@example.org>"));
756 /// assert!(re_set.is_match("Bob <bob@example.com>"));
757 /// # Ok(()) }
758 /// ```
759 pub fn from_bytes<'a, I, RE>(res: I) -> Result<Self>
760 where I: IntoIterator<Item=RE>,
761 RE: Borrow<&'a [u8]>,
762 {
763 let mut have_valid_utf8 = false;
764 let mut have_invalid_utf8 = false;
765 let mut re_bytes = Vec::new();
766 let re_set = Self::new(
767 res
768 .into_iter()
769 .scan((&mut have_valid_utf8, &mut have_invalid_utf8),
770 |(valid, invalid), re|
771 {
772 re_bytes.push(re.borrow().to_vec());
773 if let Ok(re) = std::str::from_utf8(re.borrow()) {
774 **valid = true;
775 Some(Some(re))
776 } else {
777 **invalid = true;
778 Some(None)
779 }
780 })
781 .flatten());
782
783 if !have_valid_utf8 && have_invalid_utf8 {
784 // None of the strings were valid UTF-8. Reject
785 // everything.
786 Ok(RegexSet {
787 re_bytes,
788 re_set: RegexSet_::Invalid,
789 disable_sanitizations: false,
790 })
791 } else {
792 // We had nothing or at least one string was valid UTF-8.
793 // RegexSet::new did the right thing.
794 re_set.map(|mut r| { r.re_bytes = re_bytes; r })
795 }
796 }
797
798 /// Returns the bytes-representation of the regular expressions.
799 pub fn as_bytes(&self) -> &[Vec<u8>] {
800 &self.re_bytes
801 }
802
803 /// Creates a `RegexSet` from the regular expressions stored in a
804 /// trust signature.
805 ///
806 /// This method is a convenience function, which extracts any
807 /// regular expressions from a [Trust Signature] and wraps them in a
808 /// `RegexSet`.
809 ///
810 /// [Trust Signature]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.21
811 ///
812 /// If the signature is not a valid trust signature (its [type] is
813 /// [GenericCertification], [PersonaCertification],
814 /// [CasualCertification], [PositiveCertification], or [DirectKey],
815 /// and the [Trust Signature] subpacket is present), this returns
816 /// an error.
817 ///
818 /// [type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.1
819 /// [GenericCertification]: crate::types::SignatureType::GenericCertification
820 /// [PersonaCertification]: crate::types::SignatureType::PersonaCertification
821 /// [CasualCertification]: crate::types::SignatureType::CasualCertification
822 /// [PositiveCertification]: crate::types::SignatureType::PositiveCertification
823 /// [DirectKey]: crate::types::SignatureType::DirectKey
824 ///
825 /// By default, strings that don't pass a sanity check (in
826 /// particular, include Unicode control characters) never match.
827 /// This behavior can be customized using
828 /// [`RegexSet::disable_sanitizations`].
829 ///
830 /// [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
831 ///
832 /// # Examples
833 ///
834 /// ```
835 /// use sequoia_openpgp as openpgp;
836 /// # use openpgp::cert::prelude::*;
837 /// # use openpgp::packet::prelude::*;
838 /// # use openpgp::policy::StandardPolicy;
839 /// use openpgp::regex::RegexSet;
840 /// # use openpgp::types::SignatureType;
841 /// #
842 /// # fn main() -> openpgp::Result<()> {
843 /// # let p = &StandardPolicy::new();
844 /// #
845 /// # let (alice, _)
846 /// # = CertBuilder::general_purpose(Some("Alice <alice@example.org>"))
847 /// # .generate()?;
848 /// # let mut alices_signer = alice.primary_key().key().clone()
849 /// # .parts_into_secret()?.into_keypair()?;
850 /// #
851 /// # let (example_com, _)
852 /// # = CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>"))
853 /// # .generate()?;
854 /// # let example_com_userid = example_com.with_policy(p, None)?
855 /// # .userids().nth(0).expect("Added a User ID").userid();
856 /// #
857 /// # let certification = SignatureBuilder::new(SignatureType::GenericCertification)
858 /// # .set_trust_signature(1, 120)?
859 /// # .set_regular_expression("<[^>]+[@.]example\\.org>$")?
860 /// # .add_regular_expression("<[^>]+[@.]example\\.com>$")?
861 /// # .sign_userid_binding(
862 /// # &mut alices_signer,
863 /// # example_com.primary_key().component(),
864 /// # example_com_userid)?;
865 ///
866 /// // certification is a trust signature, which contains two regular
867 /// // expressions: one that matches all mail addresses for 'example.org'
868 /// // and another that matches all mail addresses for 'example.com'.
869 /// let certification: &Signature = // ...;
870 /// # &certification;
871 ///
872 /// // Extract the regex and compile it.
873 /// let res = RegexSet::from_signature(certification)?;
874 ///
875 /// // Some positive examples.
876 /// assert!(res.is_match("Alice <alice@example.org>"));
877 /// assert!(res.is_match("Bob <bob@example.com>"));
878 ///
879 /// // Wrong domain.
880 /// assert!(! res.is_match("Carol <carol@acme.com>"));
881 ///
882 /// // The standard regex, "<[^>]+[@.]example\\.org>$" only matches
883 /// // email addresses wrapped in <>.
884 /// assert!(! res.is_match("dave@example.com"));
885 ///
886 /// // And, it is case-sensitive.
887 /// assert!(res.is_match("Ellen <ellen@example.com>"));
888 /// assert!(! res.is_match("Ellen <ellen@EXAMPLE.COM>"));
889 /// # Ok(()) }
890 /// ```
891 pub fn from_signature(sig: &Signature) -> Result<Self>
892 {
893 use SignatureType::*;
894 match sig.typ() {
895 GenericCertification => (),
896 PersonaCertification => (),
897 CasualCertification => (),
898 PositiveCertification => (),
899 DirectKey => (),
900 t => return Err(
901 Error::InvalidArgument(
902 format!(
903 "Expected a certification or direct key signature, found a {}",
904 t))
905 .into()),
906 }
907
908 if sig.trust_signature().is_none() {
909 return Err(
910 Error::InvalidArgument(
911 "Expected a trust signature, \
912 but the signature does not include \
913 a valid Trust Signature subpacket".into())
914 .into());
915 }
916
917 Self::from_bytes(sig.regular_expressions())
918 }
919
920 /// Returns a `RegexSet` that matches everything.
921 ///
922 /// Note: sanitizations are still enabled. So, to really match
923 /// everything, you still need to call
924 /// [`RegexSet::disable_sanitizations`].
925 ///
926 /// [`RegexSet::disable_sanitizations`]: RegexSet::disable_sanitizations()
927 ///
928 /// This can be used to optimize the evaluation of scoping rules
929 /// along a path: if a `RegexSet` matches everything, then it
930 /// doesn't further constrain the path.
931 pub fn everything() -> Self
932 {
933 Self {
934 re_bytes: vec![vec![]],
935 re_set: RegexSet_::Everything,
936 disable_sanitizations: false,
937 }
938 }
939
940 /// Returns whether a `RegexSet` matches everything.
941 ///
942 /// Normally, this only returns true if the `RegexSet` was created
943 /// using [`RegexSet::everything`]. [`RegexSet::new`],
944 /// [`RegexSet::from_bytes`], [`RegexSet::from_signature`] do
945 /// detect some regular expressions that match everything (e.g.,
946 /// if no regular expressions are supplied). But, they do not
947 /// guarantee that a `RegexSet` containing a regular expression
948 /// like `.?`, which does in fact match everything, is detected as
949 /// matching everything.
950 ///
951 /// [`RegexSet::everything`]: RegexSet::everything()
952 /// [`RegexSet::new`]: RegexSet::everything()
953 /// [`RegexSet::from_bytes`]: RegexSet::from_bytes()
954 /// [`RegexSet::from_signature`]: RegexSet::from_signature()
955 ///
956 /// # Examples
957 ///
958 /// ```
959 /// use sequoia_openpgp as openpgp;
960 /// use openpgp::regex::RegexSet;
961 ///
962 /// # fn main() -> openpgp::Result<()> {
963 /// assert!(RegexSet::everything().matches_everything());
964 /// let empty: &[ &str ] = &[];
965 /// assert!(RegexSet::new(empty)?.matches_everything());
966 ///
967 /// // A regular expression that matches everything. But
968 /// // `RegexSet` returns false, because it can't detect it.
969 /// let res: &[ &str ] = &[
970 /// &".?"[..],
971 /// ];
972 /// let re_set = RegexSet::new(res.into_iter())?;
973 /// assert!(! re_set.matches_everything());
974 /// # Ok(()) }
975 /// ```
976 pub fn matches_everything(&self) -> bool {
977 matches!(self.re_set, RegexSet_::Everything)
978 }
979
980 /// Controls whether strings with control characters are allowed.
981 ///
982 /// If `false` (the default), i.e., sanity checks are enabled, and
983 /// the string doesn't pass the sanity check (in particular, it
984 /// contains a Unicode control character according to
985 /// [`char::is_control`], including newlines and an embedded `NUL`
986 /// byte), this returns `false`.
987 ///
988 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
989 pub fn disable_sanitizations(&mut self, allowed: bool) {
990 self.disable_sanitizations = allowed;
991 if let RegexSet_::Regex(ref mut re) = self.re_set {
992 re.disable_sanitizations(allowed);
993 }
994 }
995
996 /// Returns whether the regular expression set matches the string.
997 ///
998 /// If sanity checks are enabled (the default) and the string
999 /// doesn't pass the sanity check (in particular, it contains a
1000 /// Unicode control character according to [`char::is_control`],
1001 /// including newlines and an embedded `NUL` byte), this returns
1002 /// `false`.
1003 ///
1004 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
1005 ///
1006 /// If the `RegexSet` contains one or more regular expressions,
1007 /// this method returns whether at least one of the regular
1008 /// expressions matches. Invalid regular expressions never match.
1009 ///
1010 /// If the `RegexSet` does not contain any regular expressions
1011 /// (valid or otherwise), this method returns `true`.
1012 ///
1013 /// # Examples
1014 ///
1015 /// ```
1016 /// use sequoia_openpgp as openpgp;
1017 /// use openpgp::regex::RegexSet;
1018 ///
1019 /// # fn main() -> openpgp::Result<()> {
1020 /// // A regular expression that matches anything. (Note: this is
1021 /// // equivalent to providing no regular expressions.)
1022 /// let res: &[ &str ] = &[
1023 /// &""[..],
1024 /// ];
1025 /// let re_set = RegexSet::new(res.into_iter())?;
1026 ///
1027 /// assert!(re_set.is_match("Alice Lovelace <alice@example.org>"));
1028 ///
1029 /// // If a User ID has an embedded control character, it doesn't
1030 /// // match.
1031 /// assert!(! re_set.is_match("Alice <alice@example.org>\0"));
1032 /// # Ok(()) }
1033 /// ```
1034 pub fn is_match(&self, s: &str) -> bool {
1035 if ! self.disable_sanitizations && s.chars().any(char::is_control) {
1036 return false;
1037 }
1038
1039 match self.re_set {
1040 RegexSet_::Regex(ref re) =>
1041 re.is_match_clean(s),
1042 RegexSet_::Invalid =>
1043 false,
1044 RegexSet_::Everything =>
1045 true,
1046 }
1047 }
1048
1049 /// Returns whether the regular expression matches the User ID.
1050 ///
1051 /// If the User ID is not a valid UTF-8 string, this returns `false`.
1052 ///
1053 /// If sanity checks are enabled (the default) and the string
1054 /// doesn't pass the sanity check (in particular, it contains a
1055 /// Unicode control character according to [`char::is_control`],
1056 /// including newlines and an embedded `NUL` byte), this returns
1057 /// `false`.
1058 ///
1059 /// [`char::is_control`]: https://doc.rust-lang.org/std/primitive.char.html#method.is_control
1060 ///
1061 /// If the `RegexSet` contains one or more regular expressions,
1062 /// this method returns whether at least one of the regular
1063 /// expressions matches. Invalid regular expressions never match.
1064 ///
1065 /// If the `RegexSet` does not contain any regular expressions
1066 /// (valid or otherwise), this method returns `true`.
1067 ///
1068 /// # Examples
1069 ///
1070 /// ```
1071 /// use sequoia_openpgp as openpgp;
1072 /// use openpgp::packet::UserID;
1073 /// use openpgp::regex::RegexSet;
1074 ///
1075 /// # fn main() -> openpgp::Result<()> {
1076 /// // A regular expression that matches anything. (Note: this is
1077 /// // equivalent to providing no regular expressions.)
1078 /// let res: &[ &str ] = &[
1079 /// "",
1080 /// ];
1081 /// let re_set = RegexSet::new(res.into_iter())?;
1082 ///
1083 /// assert!(re_set.matches_userid(
1084 /// &UserID::from(&b"Alice Lovelace <alice@example.org>"[..])));
1085 ///
1086 /// // If a User ID is not valid UTF-8, it never matches.
1087 /// assert!(! re_set.matches_userid(
1088 /// &UserID::from(&b"Alice \xC3\x28 Lovelace <alice@example.org>"[..])));
1089 ///
1090 /// // If a User ID has an embedded control character, it doesn't
1091 /// // match.
1092 /// assert!(! re_set.matches_userid(
1093 /// &UserID::from(&b"Alice <alice@example.org>\0"[..])));
1094 /// # Ok(()) }
1095 /// ```
1096 pub fn matches_userid(&self, u: &UserID) -> bool
1097 {
1098 if let Ok(u) = std::str::from_utf8(u.value()) {
1099 self.is_match(u)
1100 } else {
1101 false
1102 }
1103 }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108 use super::*;
1109
1110 #[test]
1111 fn regex() -> Result<()> {
1112 fn a(regex: &str, matches: &[(bool, &str)]) {
1113 eprint!("{} -> ", regex);
1114 let mut compiled = Regex::new(regex).unwrap();
1115 compiled.disable_sanitizations(true);
1116 eprintln!("{:?}", compiled);
1117 for &(matches, text) in matches {
1118 assert_eq!(matches, compiled.is_match(text),
1119 "regex: {}\n text: {:?} should{} match",
1120 regex, text, if matches { "" } else { " not" });
1121 }
1122 }
1123 fn f(regex: &str) {
1124 eprint!("{} -> ", regex);
1125 let compiled = Regex::new(regex);
1126 assert!(compiled.is_err());
1127 eprintln!("failed (expected)");
1128 }
1129
1130 // Test an important corner case: the + should only apply to
1131 // the b! See: https://github.com/rust-lang/regex/issues/731
1132 a("xab+y", &[
1133 (true, "xaby"),
1134 (true, "xabby"),
1135 (false, "xababy"),
1136 ]);
1137 a("x(ab+)y", &[
1138 (false, "xy"),
1139 (false, "xay"),
1140 (true, "xaby"),
1141 (true, "xabby"),
1142 (true, "xabbby"),
1143 (false, "xababy"),
1144 ]);
1145 // But here the + matches "ab", not just the "b".
1146 a("x(ab)+y", &[
1147 (false, "xy"),
1148 (true, "xaby"),
1149 (false, "xabby"),
1150 (true, "xababy"),
1151 (true, "xabababy"),
1152 (false, "x(ab)y"),
1153 ]);
1154
1155
1156
1157 a("", &[
1158 (true, "s"),
1159 (true, "ss"),
1160 ]);
1161 a("s", &[
1162 (true, "s"),
1163 (true, "ss"),
1164 (false, "a"),
1165 (true, "hello, my prettiessss"),
1166 (false, "S"),
1167 ]);
1168 a("ss", &[
1169 (false, "s"),
1170 (true, "ss"),
1171 (true, "sss"),
1172 (false, "this has lots of ses, but not two ses together"),
1173 (true, "halloss"),
1174 ]);
1175
1176 a("a|b", &[
1177 (true, "a"),
1178 (true, "b"),
1179 (false, "c"),
1180 (true, "xxxaxxxbxxx"),
1181 ]);
1182 a("a|b|c", &[
1183 (true, "a"),
1184 (true, "b"),
1185 (true, "c"),
1186 (false, "d"),
1187 (true, "xxxaxxxbxxx"),
1188 ]);
1189 // This should match anything.
1190 a("|a", &[
1191 (true, "a"),
1192 (true, "b"),
1193 ]);
1194 a("a|", &[
1195 (true, "a"),
1196 (true, "b"),
1197 ]);
1198 a("|a|b", &[
1199 (true, "a"),
1200 (true, "b"),
1201 (true, "c"),
1202 ]);
1203 a("|a|b|c|d", &[
1204 (true, "a"),
1205 (true, "b"),
1206 (true, "c"),
1207 (true, "d"),
1208 (true, "eeee"),
1209 ]);
1210 a("a|b|", &[
1211 (true, "a"),
1212 (true, "b"),
1213 (true, "c"),
1214 ]);
1215 a("a|b|c|", &[
1216 (true, "a"),
1217 (true, "b"),
1218 (true, "c"),
1219 (true, "d"),
1220 (true, "eeee"),
1221 ]);
1222 a("|", &[
1223 (true, "a"),
1224 (true, "b"),
1225 (true, "c"),
1226 (true, "d"),
1227 (true, "eeee"),
1228 ]);
1229 a("|a|", &[
1230 (true, "a"),
1231 (true, "b"),
1232 (true, "c"),
1233 (true, "d"),
1234 (true, "eeee"),
1235 ]);
1236 a("|a|b|", &[
1237 (true, "a"),
1238 (true, "b"),
1239 (true, "c"),
1240 (true, "d"),
1241 (true, "eeee"),
1242 ]);
1243 // A nested empty.
1244 a("(a|)|b", &[
1245 (true, "a"),
1246 (true, "b"),
1247 ]);
1248 // empty+
1249 a("(a|b|()+)", &[
1250 (true, "a"),
1251 (true, "b"),
1252 ]);
1253 // (empty)+
1254 a("(a|b|(())+)", &[
1255 (true, "a"),
1256 (true, "b"),
1257 ]);
1258 // Multiple empty branches.
1259 a("(a|b|(()())())", &[
1260 (true, "a"),
1261 (true, "b"),
1262 ]);
1263 a("(a|b|(()())())|", &[
1264 (true, "a"),
1265 (true, "b"),
1266 ]);
1267
1268 // This is: "ab" or "cd", not a followed by b or c followed by d:
1269 //
1270 // A regular expression is zero or more branches, separated by '|'.
1271 // ...
1272 // A branch is zero or more pieces, concatenated.
1273 // ...
1274 // A piece is an atom
1275 // ...
1276 // An atom is... a single character.
1277 a("ab|cd", &[
1278 (true, "abd"),
1279 (true, "acd"),
1280 (true, "abcd"),
1281 (false, "ad"),
1282 (false, "b"),
1283 (false, "c"),
1284 (false, "bb"),
1285 ]);
1286
1287 a("a*", &[
1288 (true, ""),
1289 (true, "a"),
1290 (true, "aa"),
1291 (true, "b"),
1292 ]);
1293 a("xa*y", &[
1294 (true, "xy"),
1295 (true, "xay"),
1296 (true, "xaay"),
1297 (false, "y"),
1298 (false, "ay"),
1299 (false, "aay"),
1300 (false, "x y"),
1301 (false, "x ay"),
1302 (false, "x aay"),
1303 ]);
1304 f("*");
1305
1306 a("a+", &[
1307 (false, ""),
1308 (true, "a"),
1309 (true, "aa"),
1310 (false, "b"),
1311 (true, "baab"),
1312 (true, "by ab"),
1313 (true, "baa b"),
1314 ]);
1315 a("ab+", &[
1316 (false, ""),
1317 (false, "a"),
1318 (false, "b"),
1319 (true, "ab"),
1320 (false, "bb"),
1321 (true, "baab"),
1322 (true, "by ab"),
1323 (false, "baa b"),
1324 ]);
1325 f("+");
1326
1327 a("a?", &[
1328 (true, ""),
1329 (true, "a"),
1330 (true, "aa"),
1331 (true, "aaa"),
1332 (true, "b"),
1333 (true, "baab"),
1334 (true, "by ab"),
1335 (true, "baa b"),
1336 ]);
1337 a("xa?y", &[
1338 (false, ""),
1339 (true, "xy"),
1340 (false, "a"),
1341 (true, "xay"),
1342 (false, "aa"),
1343 (false, "xaay"),
1344 (false, "b"),
1345 (false, "bxaayb"),
1346 (true, "by xayb"),
1347 (true, "baxay b"),
1348 ]);
1349 f("?");
1350
1351 f("a*?");
1352 a("a*b?c+", &[
1353 (false, ""),
1354 (true, "c"),
1355 (true, "abc"),
1356 (true, "aabbcc"),
1357 (false, "aab"),
1358 (true, "aaaaaabcccccccc"),
1359 ]);
1360 f("a?*+");
1361
1362 a("a?|b+", &[
1363 (true, ""),
1364 (true, "aaa"),
1365 (true, "bbb"),
1366 (true, "abaa"),
1367 ]);
1368 a("a+|b+", &[
1369 (false, ""),
1370 (true, "a"),
1371 (true, "aaa"),
1372 (true, "b"),
1373 (true, "bbb"),
1374 (true, "abaa"),
1375 ]);
1376 a("a+|b+|c+", &[
1377 (false, ""),
1378 (true, "a"),
1379 (true, "aaa"),
1380 (true, "b"),
1381 (true, "bbb"),
1382 (true, "abaa"),
1383 (true, "c"),
1384 (true, "ccc"),
1385 (true, "abaaccc"),
1386 ]);
1387 a("xa+|b+|c+y", &[
1388 (false, ""),
1389 (true, "xa"),
1390 (true, "xaa"),
1391 (true, "b"),
1392 (true, "bb"),
1393 (true, "cy"),
1394 (true, "ccy"),
1395
1396 (false, "a"),
1397 (false, "aaa"),
1398 (false, "c"),
1399 (false, "ccc"),
1400 ]);
1401 a("xa+y|sb+u", &[
1402 (false, ""),
1403 (true, "xay"),
1404 (true, "xaay"),
1405 (true, "sbu"),
1406 (true, "sbbu"),
1407 (true, "xysbu"),
1408
1409 (false, "a"),
1410 (false, "aaa"),
1411 (false, "xyu"),
1412 (false, "ccc"),
1413 ]);
1414 a("a*|a+|ab+cd+|", &[
1415 (true, ""),
1416 ]);
1417
1418 a("()", &[
1419 (true, ""),
1420 (true, "xyzzy"),
1421 ]);
1422 a("(())", &[
1423 (true, ""),
1424 (true, "xyzzy"),
1425 ]);
1426 a("((()))", &[
1427 (true, ""),
1428 (true, "xyzzy"),
1429 ]);
1430 f("((())");
1431 f("((())))");
1432 a("(a)", &[
1433 (true, "a"),
1434 (true, "(a)"),
1435 (false, "b"),
1436 ]);
1437 a("x(a)y", &[
1438 (false, "xy"),
1439 (true, "xay"),
1440 (false, "x(a)y"),
1441 (true, "(xay)"),
1442 (false, "a"),
1443 (false, "yax"),
1444 ]);
1445 a("x(ab)y", &[
1446 (false, "xy"),
1447 (false, "xay"),
1448 (false, "xby"),
1449 (true, "xaby"),
1450 (false, "x(ab)y"),
1451 (true, "(xaby)"),
1452 ]);
1453 a("x(ab)(cd)y", &[
1454 (true, "xabcdy"),
1455 (true, "zxabcdyz"),
1456 ]);
1457 a("a(bc)d(ef)g", &[
1458 (true, "abcdefg"),
1459 (true, "xabcdefgy"),
1460 (false, "xa(bc)d(ef)gy"),
1461 ]);
1462 a("a((bc))d((ef))g", &[
1463 (true, "abcdefg"),
1464 (true, "xabcdefgy"),
1465 (false, "xa(bc)d(ef)gy"),
1466 ]);
1467 a("a(b(c)d)e", &[
1468 (true, "abcde"),
1469 (true, "xabcdey"),
1470 (false, "xa(b(c)d)ey"),
1471 ]);
1472 a("x(a|b)y", &[
1473 (false, "xy"),
1474 (true, "xay"),
1475 (true, "xby"),
1476 (false, "xaay"),
1477 (false, "xbby"),
1478 (false, "xaby"),
1479 (false, "xaaby"),
1480 (false, "xabby"),
1481 (false, "xaabby"),
1482 (false, "xcy"),
1483 ]);
1484 a("x(a|bc)y", &[
1485 (false, "xy"),
1486 (true, "xay"),
1487 (false, "xby"),
1488 (true, "xbcy"),
1489 (false, "xaay"),
1490 (false, "xbby"),
1491 (false, "xaby"),
1492 (false, "xabcy"),
1493 (false, "xabby"),
1494 (false, "xaabby"),
1495 (false, "xcy"),
1496 (false, "xacy"),
1497 ]);
1498 a("x(a|b|c)y", &[
1499 (false, "xy"),
1500 (true, "xay"),
1501 (true, "xby"),
1502 (true, "xcy"),
1503 (false, "xaay"),
1504 (false, "xbby"),
1505 (false, "xaby"),
1506 (false, "xabcy"),
1507 (false, "xabby"),
1508 (false, "xaabby"),
1509 (false, "xacy"),
1510 ]);
1511 a("x(a|b)(c|d)y", &[
1512 (false, "xy"),
1513 (false, "xay"),
1514 (false, "xby"),
1515 (false, "xcy"),
1516 (false, "xdy"),
1517 (false, "xaay"),
1518 (false, "xbby"),
1519 (false, "xccy"),
1520 (false, "xddy"),
1521 (false, "xaby"),
1522 (false, "xcdy"),
1523 (true, "xacy"),
1524 (true, "xady"),
1525 (true, "xbcy"),
1526 (true, "xbdy"),
1527 (false, "xabcy"),
1528 (false, "xabby"),
1529 (false, "xaabby"),
1530 ]);
1531 a("x(a+|b+)y", &[
1532 (false, "xy"),
1533 (true, "xay"),
1534 (true, "xby"),
1535 (true, "xaay"),
1536 (true, "xbby"),
1537 (false, "xaby"),
1538 (false, "xaaby"),
1539 (false, "xabby"),
1540 (false, "xaabby"),
1541 (false, "xcy"),
1542 ]);
1543
1544 a(".", &[
1545 (false, ""),
1546 (true, "a"),
1547 (true, "ab"),
1548 (true, "ab\nc"),
1549 (true, "ab.c"),
1550 ]);
1551 a("x.y", &[
1552 (false, ""),
1553 (false, "xy"),
1554 (true, "xay"),
1555 (true, "x\ny"),
1556 (true, "x.y"),
1557 (false, "x..y"),
1558 ]);
1559
1560 a("^", &[
1561 (true, ""),
1562 (true, "xx"),
1563 ]);
1564 a("^abc", &[
1565 (false, ""),
1566 (true, "abcdef"),
1567 (false, "xabcdef"),
1568 (false, "\nabcdef"),
1569 ]);
1570 a("(^abc|^def)", &[
1571 (false, ""),
1572 (true, "abcd"),
1573 (true, "defg"),
1574 (false, "xabcd"),
1575 (false, "xdefg"),
1576 (false, "^abc"),
1577 (false, "^(abc|def)"),
1578 (false, "\nabcdef"),
1579 ]);
1580 a("(^abc|def)", &[
1581 (false, ""),
1582 (true, "abcd"),
1583 (true, "defg"),
1584 (false, "xabcd"),
1585 (true, "xdefg"),
1586 (false, "^abc"),
1587 (true, "^(abc|def)"),
1588 (false, "\nabcde"),
1589 ]);
1590 a("^^", &[
1591 (true, ""),
1592 (true, "abcdef"),
1593 ]);
1594 a("^abc^", &[
1595 (false, ""),
1596 (false, "abcdef"),
1597 (false, "xabcdef"),
1598 (false, "abc\n"),
1599 (false, "\nabc\n"),
1600 (false, "^abc^"),
1601 ]);
1602
1603 a("$", &[
1604 (true, ""),
1605 (true, "abc"),
1606 ]);
1607 a("abc$", &[
1608 (false, ""),
1609 (true, "abc"),
1610 (false, "abcx"),
1611 (false, "abc\n"),
1612 (false, "abc$"),
1613 ]);
1614 a("abc$$", &[
1615 (false, ""),
1616 (true, "abc"),
1617 (false, "abcx"),
1618 (false, "abc\n"),
1619 (false, "abc$"),
1620 ]);
1621 a("(abc$)x", &[
1622 (false, ""),
1623 (false, "abc"),
1624 (false, "abcx"),
1625 (false, "abc\nx"),
1626 (false, "abc$x"),
1627 ]);
1628 a("abc$|def$", &[
1629 (false, ""),
1630 (true, "abc"),
1631 (false, "abcx"),
1632 (false, "abc\n"),
1633 (false, "abc$"),
1634 (true, "def"),
1635 (false, "defx"),
1636 (false, "def\n"),
1637 (false, "def$"),
1638 (true, "abcdef"),
1639 ]);
1640
1641 a("\\|", &[
1642 (true, "|"),
1643 (false, ""),
1644 (false, "a"),
1645 ]);
1646 a("\\*", &[
1647 (true, "*"),
1648 (false, ""),
1649 (false, "a"),
1650 ]);
1651 a("\\+", &[
1652 (true, "+"),
1653 (false, ""),
1654 (false, "a"),
1655 ]);
1656 a("\\?", &[
1657 (true, "?"),
1658 (false, ""),
1659 (false, "a"),
1660 ]);
1661 a("\\.", &[
1662 (true, "."),
1663 (false, ""),
1664 (false, "a"),
1665 ]);
1666 a("\\^", &[
1667 (true, "^"),
1668 (false, ""),
1669 (false, "a"),
1670 ]);
1671 a("\\$", &[
1672 (true, "$"),
1673 (false, ""),
1674 (false, "a"),
1675 ]);
1676 a("\\\\", &[
1677 (true, "\\"),
1678 (false, ""),
1679 (false, "a"),
1680 ]);
1681 a("\\[", &[
1682 (true, "["),
1683 (false, ""),
1684 (false, "a"),
1685 ]);
1686 a("\\]", &[
1687 (true, "]"),
1688 (false, ""),
1689 (false, "a"),
1690 ]);
1691 a("\\-", &[
1692 (true, "-"),
1693 (false, ""),
1694 (false, "a"),
1695 ]);
1696 f("\\");
1697
1698 a("[a]", &[
1699 (true, "a"),
1700 (false, "b"),
1701 ]);
1702 a("[abc]", &[
1703 (true, "a"),
1704 (true, "b"),
1705 (true, "c"),
1706 (false, "d"),
1707 ]);
1708 a("[a-c]", &[
1709 (true, "a"),
1710 (true, "b"),
1711 (true, "c"),
1712 (false, "d"),
1713 ]);
1714 a("[xa-c]", &[
1715 (true, "a"),
1716 (true, "b"),
1717 (true, "c"),
1718 (true, "x"),
1719 (false, "d"),
1720 ]);
1721 a("[a-cxyz]", &[
1722 (true, "a"),
1723 (true, "b"),
1724 (true, "c"),
1725 (true, "x"),
1726 (false, "d"),
1727 ]);
1728 a("[a-c]x", &[
1729 (false, "a"),
1730 (false, "b"),
1731 (false, "c"),
1732 (false, "x"),
1733 (true, "ax"),
1734 (true, "bx"),
1735 (true, "cx"),
1736 (false, "d"),
1737 (false, "dx"),
1738 ]);
1739 a("[a-cxy]", &[
1740 (true, "a"),
1741 (true, "b"),
1742 (true, "c"),
1743 (true, "x"),
1744 (true, "y"),
1745 (false, "d"),
1746 ]);
1747 a("[a-c]xy", &[
1748 (false, "a"),
1749 (false, "b"),
1750 (false, "c"),
1751 (false, "x"),
1752 (false, "ax"),
1753 (false, "bx"),
1754 (false, "cx"),
1755 (true, "axy"),
1756 (true, "bxy"),
1757 (true, "cxy"),
1758 (false, "d"),
1759 ]);
1760 a("[a-cxyz]", &[
1761 (true, "a"),
1762 (true, "b"),
1763 (true, "c"),
1764 (true, "x"),
1765 (true, "y"),
1766 (true, "z"),
1767 (false, "d"),
1768 ]);
1769 a("[a-c]xyz", &[
1770 (false, "a"),
1771 (false, "b"),
1772 (false, "c"),
1773 (false, "x"),
1774 (false, "ax"),
1775 (false, "bx"),
1776 (false, "cx"),
1777 (false, "axy"),
1778 (false, "bxy"),
1779 (false, "cxy"),
1780 (true, "axyz"),
1781 (true, "bxyz"),
1782 (true, "cxyz"),
1783 (false, "d"),
1784 ]);
1785 a("xyz[a-c]", &[
1786 (false, "a"),
1787 (false, "b"),
1788 (false, "c"),
1789 (false, "x"),
1790 (false, "xa"),
1791 (false, "xb"),
1792 (false, "xc"),
1793 (false, "xya"),
1794 (false, "xyb"),
1795 (false, "xyc"),
1796 (true, "xyza"),
1797 (true, "xyzb"),
1798 (true, "xyzc"),
1799 (false, "d"),
1800 ]);
1801 a("[xyza-c]", &[
1802 (true, "a"),
1803 (true, "b"),
1804 (true, "c"),
1805 (true, "x"),
1806 (true, "y"),
1807 (true, "z"),
1808 (false, "d"),
1809 ]);
1810 a("[xya-cyz]", &[
1811 (true, "a"),
1812 (true, "b"),
1813 (true, "c"),
1814 (true, "x"),
1815 (true, "y"),
1816 (true, "z"),
1817 (false, "d"),
1818 ]);
1819 a("[x-za-c]", &[
1820 (true, "a"),
1821 (true, "b"),
1822 (true, "c"),
1823 (true, "x"),
1824 (true, "y"),
1825 (true, "z"),
1826 (false, "d"),
1827 ]);
1828 a("[x-zmna-c]", &[
1829 (true, "a"),
1830 (true, "b"),
1831 (true, "c"),
1832 (true, "x"),
1833 (true, "y"),
1834 (true, "z"),
1835 (true, "m"),
1836 (true, "n"),
1837 (false, "d"),
1838 ]);
1839 a("[-]", &[
1840 (true, "-"),
1841 (false, "d"),
1842 ]);
1843 a("[a-]", &[
1844 (true, "-"),
1845 (true, "a"),
1846 (false, "d"),
1847 ]);
1848 a("[-b]", &[
1849 (true, "-"),
1850 (true, "b"),
1851 (false, "d"),
1852 ]);
1853 a("[-bd-g]", &[
1854 (false, "a"),
1855 (true, "-"),
1856 (true, "b"),
1857 (true, "d"),
1858 (true, "f"),
1859 ]);
1860 a("[bd-g-]", &[
1861 (false, "a"),
1862 (true, "-"),
1863 (true, "b"),
1864 (true, "d"),
1865 (true, "f"),
1866 ]);
1867 // Backwards ranges.
1868 a("[9-0]", &[
1869 (false, "a"),
1870 (false, "-"),
1871 (true, "9"),
1872 (true, "0"),
1873 (true, "5"),
1874 ]);
1875
1876 a("[^a]", &[
1877 (false, "a"),
1878 (true, "b"),
1879 ]);
1880 a("[^abc]", &[
1881 (false, "a"),
1882 (false, "b"),
1883 (false, "c"),
1884 (true, "d"),
1885 ]);
1886 a("[^a-c]", &[
1887 (false, "a"),
1888 (false, "b"),
1889 (false, "c"),
1890 (true, "d"),
1891 ]);
1892 a("[^xa-c]", &[
1893 (false, "a"),
1894 (false, "b"),
1895 (false, "c"),
1896 (false, "x"),
1897 (true, "d"),
1898 ]);
1899 a("[^a-cxyz]", &[
1900 (false, "a"),
1901 (false, "b"),
1902 (false, "c"),
1903 (false, "x"),
1904 (true, "d"),
1905 ]);
1906 a("[^a-c]x", &[
1907 (false, "a"),
1908 (false, "b"),
1909 (false, "c"),
1910 (false, "x"),
1911 (false, "ax"),
1912 (false, "bx"),
1913 (false, "cx"),
1914 (false, "d"),
1915 (true, "dx"),
1916 ]);
1917 a("[^a-cxy]", &[
1918 (false, "a"),
1919 (false, "b"),
1920 (false, "c"),
1921 (false, "x"),
1922 (false, "y"),
1923 (true, "d"),
1924 ]);
1925 a("[^a-c]xy", &[
1926 (false, "a"),
1927 (false, "b"),
1928 (false, "c"),
1929 (false, "x"),
1930 (false, "ax"),
1931 (false, "bx"),
1932 (false, "cx"),
1933 (false, "axy"),
1934 (false, "bxy"),
1935 (false, "cxy"),
1936 (true, "dxy"),
1937 (false, "d"),
1938 ]);
1939 a("[^a-cxyz]", &[
1940 (false, "a"),
1941 (false, "b"),
1942 (false, "c"),
1943 (false, "x"),
1944 (false, "y"),
1945 (false, "z"),
1946 (true, "d"),
1947 ]);
1948 a("[^a-c]xyz", &[
1949 (false, "a"),
1950 (false, "b"),
1951 (false, "c"),
1952 (false, "x"),
1953 (false, "ax"),
1954 (false, "bx"),
1955 (false, "cx"),
1956 (false, "axy"),
1957 (false, "bxy"),
1958 (false, "cxy"),
1959 (false, "axyz"),
1960 (false, "bxyz"),
1961 (false, "cxyz"),
1962 (true, "dxyz"),
1963 (false, "d"),
1964 ]);
1965 a("xyz[^a-c]", &[
1966 (false, "a"),
1967 (false, "b"),
1968 (false, "c"),
1969 (false, "x"),
1970 (false, "xa"),
1971 (false, "xb"),
1972 (false, "xc"),
1973 (false, "xya"),
1974 (false, "xyb"),
1975 (false, "xyc"),
1976 (false, "xyza"),
1977 (false, "xyzb"),
1978 (false, "xyzc"),
1979 (true, "xyzd"),
1980 (false, "d"),
1981 ]);
1982 a("[^xyza-c]", &[
1983 (false, "a"),
1984 (false, "b"),
1985 (false, "c"),
1986 (false, "x"),
1987 (false, "y"),
1988 (false, "z"),
1989 (true, "d"),
1990 ]);
1991 a("[^xya-cyz]", &[
1992 (false, "a"),
1993 (false, "b"),
1994 (false, "c"),
1995 (false, "x"),
1996 (false, "y"),
1997 (false, "z"),
1998 (true, "d"),
1999 ]);
2000 a("[^x-za-c]", &[
2001 (false, "a"),
2002 (false, "b"),
2003 (false, "c"),
2004 (false, "x"),
2005 (false, "y"),
2006 (false, "z"),
2007 (true, "d"),
2008 ]);
2009 a("[^x-zmna-c]", &[
2010 (false, "a"),
2011 (false, "b"),
2012 (false, "c"),
2013 (false, "x"),
2014 (false, "y"),
2015 (false, "z"),
2016 (false, "m"),
2017 (false, "n"),
2018 (true, "d"),
2019 ]);
2020 a("[^-]", &[
2021 (false, "-"),
2022 (true, "d"),
2023 ]);
2024 a("[^a-]", &[
2025 (false, "-"),
2026 (false, "a"),
2027 (true, "d"),
2028 ]);
2029 a("[^-b]", &[
2030 (false, "-"),
2031 (false, "b"),
2032 (true, "d"),
2033 ]);
2034 a("[^-bd-g]", &[
2035 (true, "a"),
2036 (false, "-"),
2037 (false, "b"),
2038 (false, "d"),
2039 (false, "f"),
2040 ]);
2041 a("[^bd-g-]", &[
2042 (true, "a"),
2043 (false, "-"),
2044 (false, "b"),
2045 (false, "d"),
2046 (false, "f"),
2047 ]);
2048
2049 a("[a|b]", &[
2050 (true, "a"),
2051 (true, "|"),
2052 (false, "c"),
2053 ]);
2054 a("[a\\|b]", &[
2055 (true, "a"),
2056 (true, "|"),
2057 (true, "\\"),
2058 (false, "c"),
2059 ]);
2060 a("[a(b]", &[
2061 (true, "a"),
2062 (true, "("),
2063 (false, "c"),
2064 ]);
2065 a("[a)b]", &[
2066 (true, "a"),
2067 (true, ")"),
2068 (false, "c"),
2069 ]);
2070 a("[a^b]", &[
2071 (true, "a"),
2072 (true, "^"),
2073 (false, "c"),
2074 ]);
2075
2076 f("[]");
2077 f("[^]");
2078 a("[^]]", &[
2079 (true, "a"),
2080 (false, "]"),
2081 (true, "^"),
2082 ]);
2083 a("[]]", &[
2084 (false, "a"),
2085 (true, "]"),
2086 ]);
2087 // Matches [ or ].
2088 a("[][]", &[
2089 (false, "a"),
2090 (true, "["),
2091 (true, "]"),
2092 ]);
2093 // Matches anything but [ or ].
2094 a("[^][]", &[
2095 (true, "a"),
2096 (false, "["),
2097 (false, "]"),
2098 ]);
2099 // Anything but ^.
2100 a("[^^]", &[
2101 (true, "a"),
2102 (false, "^"),
2103 (true, "c"),
2104 ]);
2105
2106 // Make sure - is recognized as an atom when it is not part of
2107 // a range. That is: a-z matches a or - or z, but it doesn't
2108 // match b (it's not a range).
2109 a("a-z", &[
2110 (true, "a-z"),
2111 (false, "a"),
2112 (false, "-"),
2113 (false, "z"),
2114 (false, "c"),
2115 ]);
2116
2117 a("a|-|z", &[
2118 (true, "a"),
2119 (true, "-"),
2120 (true, "z"),
2121 (false, "c"),
2122 ]);
2123
2124 Ok(())
2125 }
2126
2127 #[test]
2128 fn regex_set() -> Result<()> {
2129 let re = RegexSet::new(&[ "ab", "cd" ])?;
2130 assert!(re.is_match("ab"));
2131 assert!(re.is_match("cdef"));
2132 assert!(!re.is_match("xxx"));
2133
2134 // Try to make sure one re does not leak into another.
2135 let re = RegexSet::new(&[ "cd$", "^ab" ])?;
2136 assert!(re.is_match("abxx"));
2137 assert!(! re.is_match("xabxx"));
2138 assert!(re.is_match("xxcd"));
2139 assert!(! re.is_match("xxcdx"));
2140 assert!(re.is_match("abcdx"));
2141
2142 // Invalid regular expressions should be ignored.
2143 let re = RegexSet::new(&[ "[ab", "cd]", "x" ])?;
2144 assert!(!re.is_match("a"));
2145 assert!(!re.is_match("ab"));
2146 assert!(!re.is_match("[ab"));
2147 assert!(!re.is_match("c"));
2148 assert!(!re.is_match("cd"));
2149 assert!(!re.is_match("cd]"));
2150 assert!(re.is_match("x"));
2151
2152 // If all regular expressions are invalid, nothing should
2153 // match.
2154 let re = RegexSet::new(&[ "[ab", "cd]" ])?;
2155 assert!(!re.is_match("a"));
2156 assert!(!re.is_match("ab"));
2157 assert!(!re.is_match("[ab"));
2158 assert!(!re.is_match("c"));
2159 assert!(!re.is_match("cd"));
2160 assert!(!re.is_match("cd]"));
2161 assert!(!re.is_match("x"));
2162
2163 // If there are no regular expressions, everything should
2164 // match.
2165 let s: [&str; 0] = [];
2166 let re = RegexSet::new(&s)?;
2167 assert!(re.is_match("a"));
2168 assert!(re.is_match("ab"));
2169 assert!(re.is_match("[ab"));
2170 assert!(re.is_match("c"));
2171 assert!(re.is_match("cd"));
2172 assert!(re.is_match("cd]"));
2173 assert!(re.is_match("x"));
2174
2175 // The empty branch of the alternation should match everything.
2176 let re = RegexSet::new(&[ "ab|", "cd" ])?;
2177 assert!(re.is_match("a"));
2178 assert!(re.is_match("b"));
2179 assert!(re.is_match("x"));
2180 assert!(re.is_match("xyx"));
2181 assert!(re.is_match(""));
2182
2183 Ok(())
2184 }
2185
2186 #[test]
2187 fn regex_set_from_direct_key_signature() -> Result<()> {
2188 use crate::cert::CertBuilder;
2189
2190 let (alice, _) =
2191 CertBuilder::general_purpose(Some("Alice <alice@example.org>")).generate()?;
2192 let mut alices_signer = alice
2193 .primary_key()
2194 .key()
2195 .clone()
2196 .parts_into_secret()?
2197 .into_keypair()?;
2198
2199 let (ca, _) =
2200 CertBuilder::general_purpose(Some("OpenPGP CA <openpgp-ca@example.com>")).generate()?;
2201
2202 let trust_signature = SignatureBuilder::new(SignatureType::DirectKey)
2203 .set_trust_signature(1, 120)?
2204 .set_regular_expression("<[^>]+[@.]example\\.com>$")?
2205 .sign_direct_key(&mut alices_signer, ca.primary_key().key())?;
2206
2207 let re = RegexSet::from_signature(&trust_signature)?;
2208 assert!(re.is_match("Alice <alice@example.com>"));
2209 assert!(!re.is_match("Alice <alice@example.org>"));
2210
2211 Ok(())
2212 }
2213
2214 #[test]
2215 fn regex_set_sequoia() -> Result<()> {
2216 let re = RegexSet::new(&["<[^>]+[@.]sequoia-pgp\\.org>$"])?;
2217 dbg!(&re);
2218 assert!(re.is_match("<justus@sequoia-pgp.org>"));
2219 assert!(!re.is_match("<justus@gnupg.org>"));
2220 Ok(())
2221 }
2222
2223 #[test]
2224 fn regex_set_sequoia_nodash() -> Result<()> {
2225 let re = RegexSet::new(&["<[^>]+[@.]sequoiapgp\\.org>$"])?;
2226 dbg!(&re);
2227 assert!(re.is_match("<justus@sequoiapgp.org>"));
2228 assert!(!re.is_match("<justus@gnupg.org>"));
2229 Ok(())
2230 }
2231}