bindle/invoice/
verification.rs

1use crate::invoice::Signed;
2
3use super::signature::KeyRing;
4use super::{Invoice, Signature, SignatureError, SignatureRole};
5use ed25519_dalek::{PublicKey, Signature as EdSignature};
6use tracing::debug;
7
8use std::borrow::{Borrow, BorrowMut};
9use std::fmt::Debug;
10use std::str::FromStr;
11
12const GREEDY_VERIFICATION_ROLES: &[SignatureRole] = &[SignatureRole::Creator];
13const CREATIVE_INTEGITY_ROLES: &[SignatureRole] = &[SignatureRole::Creator];
14const AUTHORITATIVE_INTEGRITY_ROLES: &[SignatureRole] =
15    &[SignatureRole::Creator, SignatureRole::Approver];
16const EXHAUSTIVE_VERIFICATION_ROLES: &[SignatureRole] = &[
17    SignatureRole::Creator,
18    SignatureRole::Approver,
19    SignatureRole::Host,
20    SignatureRole::Proxy,
21];
22
23/// A marker trait indicating that an invoice has been verified
24pub trait Verified: super::sealed::Sealed {}
25
26/// This enumerates the verifications strategies described in the signing spec.
27#[derive(Debug, Clone)]
28pub enum VerificationStrategy {
29    /// CreativeIntegrity verifies that (a) the key that signs as Creator is a known key,
30    /// and that the signature is valid.
31    CreativeIntegrity,
32    /// AuthoritativeIntegrity verifies that at least one of the Creator or Approver keys
33    /// is known and the signature is valid.
34    AuthoritativeIntegrity,
35    /// Verify that the Creator key is known and that all signatures are valid.
36    ///
37    /// This is subject to a DOS attack if a signer can generate intentionally bad signatures.
38    GreedyVerification,
39    /// Verify that every key on the invoice is known, and that every signature is valid.
40    ExhaustiveVerification,
41    /// Verifies that all signatures of the given roles are valid and signed by known keys.
42    MultipleAttestation(Vec<SignatureRole>),
43    /// Verifies that all signatures of the given roles are valid and signed by known keys. Will
44    /// also validate unknown signers similar to GreedyVerification
45    ///
46    /// Unknown signers will also be validated. Be aware that doing so may make
47    /// the validation subject to a special form of DOS attack in which someone can generate a
48    /// known-bad signature.
49    MultipleAttestationGreedy(Vec<SignatureRole>),
50}
51
52impl Default for VerificationStrategy {
53    fn default() -> Self {
54        VerificationStrategy::GreedyVerification
55    }
56}
57
58/// This implementation will parse the strategy from a string. MultipleAttestation strategies should
59/// be of the format `MultipleAttestation[Creator, Approver]`
60impl FromStr for VerificationStrategy {
61    type Err = &'static str;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        if s.is_empty() {
65            return Err("Cannot parse VerificationStrategy from an empty string");
66        }
67        // We are very flexible here, allowing for any casing and trimming leading or trailing
68        // whitespace
69        let normalized = s.trim().to_lowercase();
70        let parts: Vec<&str> = normalized.splitn(2, '[').collect();
71        // Safety: Shouldn't panic here because we checked for an empty string _and_ splitn on an
72        // empty string still will return a vec with a length of 1
73        match parts[0] {
74            "creativeintegrity" => Ok(Self::CreativeIntegrity),
75            "authoritativeintegrity" => Ok(Self::AuthoritativeIntegrity),
76            "greedyverification" => Ok(Self::GreedyVerification),
77            "exhaustiveverification" => Ok(Self::ExhaustiveVerification),
78            "multipleattestation" => Ok(Self::MultipleAttestation(parse_roles(parts.get(1))?)),
79            "multipleattestationgreedy" => {
80                Ok(Self::MultipleAttestationGreedy(parse_roles(parts.get(1))?))
81            }
82            _ => Err("Unknown verification strategy"),
83        }
84    }
85}
86
87/// Manual implementation of deserialize due to TOML not supporting "newtype" enum variants. This
88/// deserializes using the same parsing rules as `FromStr`
89impl<'de> serde::Deserialize<'de> for VerificationStrategy {
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: serde::Deserializer<'de>,
93    {
94        deserializer.deserialize_string(StrategyVisitor)
95    }
96}
97
98struct StrategyVisitor;
99
100impl<'de> serde::de::Visitor<'de> for StrategyVisitor {
101    type Value = VerificationStrategy;
102
103    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
104        formatter.write_str("a valid verification strategy value")
105    }
106
107    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
108    where
109        E: serde::de::Error,
110    {
111        match v.parse::<VerificationStrategy>() {
112            Ok(s) => Ok(s),
113            Err(e) => Err(E::custom(e)),
114        }
115    }
116
117    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
118    where
119        E: serde::de::Error,
120    {
121        self.visit_str(&v)
122    }
123}
124
125fn parse_roles(r: Option<&&str>) -> Result<Vec<SignatureRole>, &'static str> {
126    let raw = r.ok_or("Multiple attestation strategy is missing roles")?;
127    if !raw.ends_with(']') {
128        return Err("Missing closing ']' on roles");
129    }
130    raw.trim_end_matches(']')
131        .split(',')
132        .map(|role| role.parse::<SignatureRole>())
133        .collect::<Result<Vec<_>, _>>()
134}
135
136/// A strategy for verifying an invoice.
137impl VerificationStrategy {
138    fn verify_signature(&self, sig: &Signature, cleartext: &[u8]) -> Result<(), SignatureError> {
139        let pk = base64::decode(sig.key.as_bytes())
140            .map_err(|_| SignatureError::CorruptKey(sig.key.clone()))?;
141        let sig_block = base64::decode(sig.signature.as_bytes())
142            .map_err(|_| SignatureError::CorruptSignature(sig.key.clone()))?;
143
144        let pubkey =
145            PublicKey::from_bytes(&pk).map_err(|_| SignatureError::CorruptKey(sig.key.clone()))?;
146        let ed_sig = EdSignature::try_from(sig_block.as_slice())
147            .map_err(|_| SignatureError::CorruptSignature(sig.key.clone()))?;
148        pubkey
149            .verify_strict(cleartext, &ed_sig)
150            .map_err(|_| SignatureError::Unverified(sig.key.clone()))
151    }
152    /// Verify that every signature on this invoice is correct.
153    ///
154    /// The verification strategy will determine how this verification is performed.
155    /// Depending on the selected strategy, the `[[signature]]` blocks will be evaluated
156    /// for the following:
157    ///
158    /// - Is the key in the keyring?
159    /// - Can the signature be verified?
160    ///
161    /// Note that the purpose of the keyring is to ensure that we know about the
162    /// entity that claims to have signed the invoice.
163    ///
164    /// If no signatures are on the invoice, this will succeed.
165    ///
166    /// A strategy will determine success or failure based on whether the signature is verified,
167    /// whether the keys are known, whether the requisite number/roles are satisfied, and
168    /// so on.
169    pub fn verify<I>(
170        &self,
171        invoice: I,
172        keyring: &KeyRing,
173    ) -> Result<VerifiedInvoice<I>, SignatureError>
174    where
175        I: Borrow<Invoice> + Into<Invoice>,
176    {
177        let inv = invoice.borrow();
178        let (roles, all_valid, all_verified, all_roles) = match self {
179            VerificationStrategy::GreedyVerification => {
180                (GREEDY_VERIFICATION_ROLES, true, true, true)
181            }
182            VerificationStrategy::CreativeIntegrity => (CREATIVE_INTEGITY_ROLES, false, true, true),
183            VerificationStrategy::AuthoritativeIntegrity => {
184                (AUTHORITATIVE_INTEGRITY_ROLES, false, false, false)
185            }
186            VerificationStrategy::ExhaustiveVerification => {
187                (EXHAUSTIVE_VERIFICATION_ROLES, true, true, false)
188            }
189            VerificationStrategy::MultipleAttestation(a) => (a.as_slice(), false, true, true),
190            VerificationStrategy::MultipleAttestationGreedy(a) => (a.as_slice(), true, true, true),
191        };
192
193        // Either the Creator or an Approver must be in the keyring
194        match inv.signature.as_ref() {
195            None => {
196                debug!(id = %inv.bindle.id, "No signatures on invoice");
197                Err(SignatureError::Unverified(
198                    "No signatures found on invoice. At least one signature is required"
199                        .to_string(),
200                ))
201            }
202            Some(signatures) => {
203                let mut known_key = false;
204                let mut filled_roles: Vec<SignatureRole> = vec![];
205                for s in signatures {
206                    debug!(by = %s.by, "Checking signature");
207                    let target_role = roles.contains(&s.role);
208
209                    // If we're not validating all, and this role isn't one we're interested in,
210                    // skip it.
211                    if !all_valid && !target_role {
212                        debug!("Not a target role, and not running all_valid");
213                        continue;
214                    }
215
216                    let role = s.role.clone();
217                    let cleartext = inv.cleartext(&s.by, &role);
218
219                    // Verify the signature
220                    // TODO: This would allow a trivial DOS attack in which an attacker
221                    // would only need to attach a known-bad signature, and that would
222                    // prevent the module from ever being usable. This is marginally
223                    // better if we only verify signatures on known keys.
224                    self.verify_signature(s, cleartext.as_bytes())?;
225                    debug!("Signature verified");
226
227                    if !target_role && !all_verified {
228                        debug!("Not a target role, not checking for verification");
229                        continue;
230                    } else if all_roles {
231                        filled_roles.push(role);
232                    }
233                    // See if the public key is known to us
234                    let pubkey = base64::decode(&s.key)
235                        .map_err(|_| SignatureError::CorruptKey(s.key.to_string()))?;
236                    let pko = PublicKey::from_bytes(pubkey.as_slice())
237                        .map_err(|_| SignatureError::CorruptKey(s.key.to_string()))?;
238
239                    debug!("Looking for key");
240                    // If the keyring contains PKO, then we are successful for this round.
241                    if keyring.contains(&pko) {
242                        debug!("Found key {}", s.by);
243                        known_key = true;
244                    } else if all_verified {
245                        // If the keyring does not contain pko AND every key must be known,
246                        // then we bail on error early.
247                        return Err(SignatureError::Unverified(
248                            "strategy requires that all signatures for role(s) must be verified"
249                                .to_owned(),
250                        ));
251                    }
252                }
253                if !known_key {
254                    debug!("No known key");
255                    // If we get here, then the none of the signatures were created with
256                    // a key from the keyring. This means the package is untrusted.
257                    return Err(SignatureError::NoKnownKey);
258                }
259                // If we are supposed to make sure that all are valid, then we need to make sure
260                // that at least one of each requested role is present.
261                if all_roles {
262                    for should_role in roles {
263                        if !filled_roles.contains(should_role) {
264                            return Err(SignatureError::Unverified(format!(
265                                "No signature found for role {:?}",
266                                should_role,
267                            )));
268                        }
269                    }
270                }
271                Ok(VerifiedInvoice(invoice))
272            }
273        }
274    }
275}
276
277/// An invoice whose signatures have been verified. Can be converted or borrowed as a plain
278/// [`Invoice`]
279pub struct VerifiedInvoice<T: Into<crate::Invoice>>(T);
280
281impl<T: Into<crate::Invoice>> Verified for VerifiedInvoice<T> {}
282
283impl<T: Into<crate::Invoice>> super::sealed::Sealed for VerifiedInvoice<T> {}
284
285impl<T> Signed for VerifiedInvoice<T>
286where
287    T: Signed + Into<crate::Invoice>,
288{
289    fn signed(self) -> crate::Invoice {
290        self.0.signed()
291    }
292}
293
294impl<T> Borrow<crate::Invoice> for VerifiedInvoice<T>
295where
296    T: Into<crate::Invoice> + Borrow<crate::Invoice>,
297{
298    fn borrow(&self) -> &crate::Invoice {
299        self.0.borrow()
300    }
301}
302
303impl<T> BorrowMut<crate::Invoice> for VerifiedInvoice<T>
304where
305    T: Into<crate::Invoice> + BorrowMut<crate::Invoice>,
306{
307    fn borrow_mut(&mut self) -> &mut crate::Invoice {
308        self.0.borrow_mut()
309    }
310}
311
312/// An internal only type that implementes `Verified` for use in caches and other passthroughs
313pub(crate) struct NoopVerified<T: Into<crate::Invoice>>(pub(crate) T);
314
315impl<T: Into<crate::Invoice>> Verified for NoopVerified<T> {}
316
317impl<T: Into<crate::Invoice>> super::sealed::Sealed for NoopVerified<T> {}
318
319impl<T> Signed for NoopVerified<T>
320where
321    T: Signed + Into<crate::Invoice>,
322{
323    fn signed(self) -> crate::Invoice {
324        self.0.signed()
325    }
326}
327
328// Need the into implementation so that it can be passed into NoopSigned
329#[allow(clippy::from_over_into)]
330impl<T: Into<crate::Invoice>> Into<crate::Invoice> for NoopVerified<T> {
331    fn into(self) -> crate::Invoice {
332        self.0.into()
333    }
334}
335
336// Only implementing this in one direction (e.g. no `From`) because we only want to be able to
337// convert into an invoice
338#[allow(clippy::from_over_into)]
339impl<T: Into<crate::Invoice>> Into<crate::Invoice> for VerifiedInvoice<T> {
340    fn into(self) -> crate::Invoice {
341        self.0.into()
342    }
343}
344
345impl<T> Debug for VerifiedInvoice<T>
346where
347    T: Debug + Into<crate::Invoice>,
348{
349    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
350        self.0.fmt(f)
351    }
352}
353
354#[cfg(test)]
355mod test {
356    use super::*;
357    use crate::invoice::*;
358    use std::convert::TryInto;
359
360    #[test]
361    fn test_parse_verification_strategies() {
362        // Happy path
363        let strat = "CreativeIntegrity"
364            .parse::<VerificationStrategy>()
365            .expect("should parse");
366        assert!(
367            matches!(strat, VerificationStrategy::CreativeIntegrity),
368            "Should parse to the correct type"
369        );
370        let strat = "AuthoritativeIntegrity"
371            .parse::<VerificationStrategy>()
372            .expect("should parse");
373        assert!(
374            matches!(strat, VerificationStrategy::AuthoritativeIntegrity),
375            "Should parse to the correct type"
376        );
377        let strat = "GreedyVerification"
378            .parse::<VerificationStrategy>()
379            .expect("should parse");
380        assert!(
381            matches!(strat, VerificationStrategy::GreedyVerification),
382            "Should parse to the correct type"
383        );
384        let strat = "ExhaustiveVerification"
385            .parse::<VerificationStrategy>()
386            .expect("should parse");
387        assert!(
388            matches!(strat, VerificationStrategy::ExhaustiveVerification),
389            "Should parse to the correct type"
390        );
391        let strat = "MultipleAttestation[Creator, Host, Approver]"
392            .parse::<VerificationStrategy>()
393            .expect("should parse");
394        assert!(
395            matches!(strat, VerificationStrategy::MultipleAttestation(_)),
396            "Should parse to the correct type"
397        );
398        let strat = "MultipleAttestationGreedy[Creator, Host, Approver]"
399            .parse::<VerificationStrategy>()
400            .expect("should parse");
401
402        // Validate everything parsed to the right type as a sanity check
403        match strat {
404            VerificationStrategy::MultipleAttestationGreedy(roles) => {
405                assert!(
406                    roles.contains(&SignatureRole::Creator),
407                    "Roles should contain creator"
408                );
409                assert!(
410                    roles.contains(&SignatureRole::Host),
411                    "Roles should contain host"
412                );
413                assert!(
414                    roles.contains(&SignatureRole::Approver),
415                    "Roles should contain approver"
416                );
417            }
418            _ => panic!("Wrong type returned"),
419        }
420
421        // Odd formatting
422        let strat = "CrEaTiVeInTeGrItY"
423            .parse::<VerificationStrategy>()
424            .expect("mixed case should parse");
425        assert!(
426            matches!(strat, VerificationStrategy::CreativeIntegrity),
427            "Should parse to the correct type"
428        );
429        let strat = "  multipleAttestAtion[Creator, Host, Approver] "
430            .parse::<VerificationStrategy>()
431            .expect("extra spaces should parse");
432        assert!(
433            matches!(strat, VerificationStrategy::MultipleAttestation(_)),
434            "Should parse to the correct type"
435        );
436
437        // Unhappy path
438        "nopenopenope"
439            .parse::<VerificationStrategy>()
440            .expect_err("non-existent strategy shouldn't parse");
441        "Creative Integrity"
442            .parse::<VerificationStrategy>()
443            .expect_err("spacing in the middle shouldn't parse");
444        "MultipleAttestationCreator, Host, Approver]"
445            .parse::<VerificationStrategy>()
446            .expect_err("missing start brace shouldn't parse");
447        "MultipleAttestation[Creator, Host, Approver"
448            .parse::<VerificationStrategy>()
449            .expect_err("missing end brace shouldn't parse");
450        "MultipleAttestation[Blah, Host, Approver]"
451            .parse::<VerificationStrategy>()
452            .expect_err("Invalid role shouldn't parse");
453    }
454
455    #[test]
456    fn test_strategy_deserialize() {
457        #[derive(serde::Deserialize)]
458        struct StrategyMock {
459            verification_strategy: VerificationStrategy,
460        }
461
462        let toml_value = r#"
463        verification_strategy = "MultipleAttestation[Creator, Host, Approver]"
464        "#;
465
466        let mock: StrategyMock = toml::from_str(toml_value).expect("toml should parse");
467
468        assert!(
469            matches!(
470                mock.verification_strategy,
471                VerificationStrategy::MultipleAttestation(_)
472            ),
473            "Should parse to the correct type"
474        );
475
476        // Sanity check: A non-array variant
477
478        let toml_value = r#"
479        verification_strategy = "CreativeIntegrity"
480        "#;
481
482        let mock: StrategyMock = toml::from_str(toml_value).expect("toml should parse");
483
484        assert!(
485            matches!(
486                mock.verification_strategy,
487                VerificationStrategy::CreativeIntegrity
488            ),
489            "Should parse to the correct type"
490        );
491
492        // Now check JSON
493        let json_value = r#"
494        {
495            "verification_strategy": "MultipleAttestation[Creator, Host, Approver]"
496        }
497        "#;
498
499        let mock: StrategyMock = serde_json::from_str(json_value).expect("json should parse");
500
501        assert!(
502            matches!(
503                mock.verification_strategy,
504                VerificationStrategy::MultipleAttestation(_)
505            ),
506            "Should parse to the correct type"
507        );
508    }
509
510    #[test]
511    fn test_verification_strategies() {
512        let invoice = r#"
513        bindleVersion = "1.0.0"
514
515        [bindle]
516        name = "arecebo"
517        version = "1.2.3"
518
519        [[parcel]]
520        [parcel.label]
521        sha256 = "aaabbbcccdddeeefff"
522        name = "telescope.gif"
523        mediaType = "image/gif"
524        size = 123_456
525        
526        [[parcel]]
527        [parcel.label]
528        sha256 = "111aaabbbcccdddeee"
529        name = "telescope.txt"
530        mediaType = "text/plain"
531        size = 123_456
532        "#;
533        let invoice: crate::Invoice = toml::from_str(invoice).expect("a nice clean parse");
534
535        let key_creator = SecretKeyEntry::new("Test Creator", vec![SignatureRole::Creator]);
536        let key_approver = SecretKeyEntry::new("Test Approver", vec![SignatureRole::Approver]);
537        let key_host = SecretKeyEntry::new("Test Host", vec![SignatureRole::Host]);
538        let key_proxy = SecretKeyEntry::new("Test Proxy", vec![SignatureRole::Proxy]);
539        let keyring_keys = vec![
540            key_approver.clone().try_into().expect("convert to pubkey"),
541            key_host.clone().try_into().expect("convert to pubkey"),
542            key_creator.clone().try_into().expect("convert to pubkey"),
543            key_proxy.clone().try_into().expect("convert to pubkey"),
544        ];
545        let keyring = KeyRing::new(keyring_keys);
546
547        // Only signed by host
548        {
549            let mut inv = invoice.clone();
550            inv.sign(SignatureRole::Host, &key_host)
551                .expect("signed as host");
552            // This should fail
553            VerificationStrategy::CreativeIntegrity
554                .verify(inv.clone(), &keyring)
555                .expect_err("inv should not pass: Requires creator");
556            VerificationStrategy::AuthoritativeIntegrity
557                .verify(inv.clone(), &keyring)
558                .expect_err("inv should not pass: Requires creator or approver");
559            VerificationStrategy::GreedyVerification
560                .verify(inv.clone(), &keyring)
561                .expect_err("inv should not pass: Requires creator and all valid");
562            VerificationStrategy::MultipleAttestationGreedy(vec![SignatureRole::Host])
563                .verify(inv.clone(), &keyring)
564                .expect("inv should pass: Only requires host");
565            VerificationStrategy::MultipleAttestationGreedy(vec![
566                SignatureRole::Host,
567                SignatureRole::Proxy,
568            ])
569            .verify(inv.clone(), &keyring)
570            .expect_err("inv should not pass: Requires proxy");
571
572            VerificationStrategy::ExhaustiveVerification
573                .verify(inv, &keyring)
574                .expect("inv should not pass: Requires that all signatures must be verified");
575        }
576        // Signed by creator and host
577        {
578            let mut inv = invoice.clone();
579            inv.sign(SignatureRole::Host, &key_host)
580                .expect("signed as host");
581            inv.sign(SignatureRole::Creator, &key_creator)
582                .expect("signed as creator");
583            // This should fail
584            VerificationStrategy::CreativeIntegrity
585                .verify(inv.clone(), &keyring)
586                .expect("inv should pass: Signed by creator");
587            VerificationStrategy::AuthoritativeIntegrity
588                .verify(inv.clone(), &keyring)
589                .expect("inv should pass: Signed by creator");
590            VerificationStrategy::GreedyVerification
591                .verify(inv.clone(), &keyring)
592                .expect("inv should pass: Requires creator and all valid");
593            VerificationStrategy::MultipleAttestationGreedy(vec![SignatureRole::Host])
594                .verify(inv.clone(), &keyring)
595                .expect("inv should pass: Only requires host");
596            VerificationStrategy::MultipleAttestationGreedy(vec![
597                SignatureRole::Host,
598                SignatureRole::Proxy,
599            ])
600            .verify(inv.clone(), &keyring)
601            .expect_err("inv should not pass: Requires proxy");
602
603            VerificationStrategy::ExhaustiveVerification
604                .verify(inv, &keyring)
605                .expect("inv should not pass: Requires that all signatures must be verified");
606        }
607        // Signed by approver and host
608        {
609            let mut inv = invoice.clone();
610            inv.sign(SignatureRole::Host, &key_host)
611                .expect("signed as host");
612            inv.sign(SignatureRole::Approver, &key_approver)
613                .expect("signed as approver");
614            // This should fail
615            VerificationStrategy::CreativeIntegrity
616                .verify(inv.clone(), &keyring)
617                .expect_err("inv should not pass: not signed by creator");
618            VerificationStrategy::AuthoritativeIntegrity
619                .verify(inv.clone(), &keyring)
620                .expect("inv should pass: Signed by approver");
621            VerificationStrategy::GreedyVerification
622                .verify(inv.clone(), &keyring)
623                .expect_err("inv should not pass: Requires creator and all valid");
624            VerificationStrategy::MultipleAttestationGreedy(vec![SignatureRole::Host])
625                .verify(inv.clone(), &keyring)
626                .expect("inv should pass: Only requires host");
627            VerificationStrategy::MultipleAttestationGreedy(vec![
628                SignatureRole::Host,
629                SignatureRole::Proxy,
630            ])
631            .verify(inv.clone(), &keyring)
632            .expect_err("inv should not pass: Requires proxy");
633
634            VerificationStrategy::ExhaustiveVerification
635                .verify(inv, &keyring)
636                .expect("inv should not pass: Requires that all signatures must be verified");
637        }
638        // Signed by creator, proxy, and host
639        {
640            let mut inv = invoice.clone();
641            inv.sign(SignatureRole::Host, &key_host)
642                .expect("signed as host");
643            inv.sign(SignatureRole::Creator, &key_creator)
644                .expect("signed as creator");
645            inv.sign(SignatureRole::Proxy, &key_proxy)
646                .expect("signed as proxy");
647            // This should fail
648            VerificationStrategy::CreativeIntegrity
649                .verify(inv.clone(), &keyring)
650                .expect("inv should pass: signed by creator");
651            VerificationStrategy::AuthoritativeIntegrity
652                .verify(inv.clone(), &keyring)
653                .expect("inv should pass: Signed by creator");
654            VerificationStrategy::GreedyVerification
655                .verify(inv.clone(), &keyring)
656                .expect("inv should pass: Requires creator and all valid");
657            VerificationStrategy::MultipleAttestationGreedy(vec![SignatureRole::Host])
658                .verify(inv.clone(), &keyring)
659                .expect("inv should pass: Only requires host");
660            VerificationStrategy::MultipleAttestationGreedy(vec![
661                SignatureRole::Host,
662                SignatureRole::Proxy,
663            ])
664            .verify(inv.clone(), &keyring)
665            .expect("inv should pass: Requires proxy");
666
667            VerificationStrategy::ExhaustiveVerification
668                .verify(inv, &keyring)
669                .expect("inv should pass: Requires that all signatures must be verified");
670        }
671        println!("Signed by creator, host, and unknown key");
672        {
673            let mut inv = invoice;
674            inv.sign(SignatureRole::Host, &key_host)
675                .expect("signed as host");
676            inv.sign(SignatureRole::Creator, &key_creator)
677                .expect("signed as creator");
678
679            // Mock an unknown key and don't add it to keyring
680            let key_anon = SecretKeyEntry::new("Unknown key", vec![SignatureRole::Approver]);
681            inv.sign(SignatureRole::Approver, &key_anon)
682                .expect("signed with unknown key");
683
684            // This should fail
685            VerificationStrategy::CreativeIntegrity
686                .verify(inv.clone(), &keyring)
687                .expect("inv should pass: signed by creator");
688            VerificationStrategy::AuthoritativeIntegrity
689                .verify(inv.clone(), &keyring)
690                .expect("inv should pass: Signed by creator");
691            VerificationStrategy::GreedyVerification
692                .verify(inv.clone(), &keyring)
693                .expect_err(
694                    "inv should not pass: Requires creator and all known, anon is not known",
695                );
696            VerificationStrategy::MultipleAttestation(vec![SignatureRole::Host])
697                .verify(inv.clone(), &keyring)
698                .expect("inv should pass: Only requires host");
699            VerificationStrategy::MultipleAttestationGreedy(vec![
700                SignatureRole::Host,
701                SignatureRole::Approver,
702            ])
703            .verify(inv.clone(), &keyring)
704            .expect_err("inv should not pass: Requires approver to be known");
705
706            VerificationStrategy::ExhaustiveVerification
707                .verify(inv, &keyring)
708                .expect_err("inv should not pass: Requires that all signatures must be verified");
709        }
710    }
711}