sequoia_git/
policy.rs

1use std::{
2    collections::{BTreeMap, BTreeSet, btree_map::Entry, HashSet},
3    env,
4    fmt,
5    fs,
6    io::{self, Read, Write},
7    path::{Path, PathBuf},
8    time::SystemTime,
9};
10
11use git2::{
12    Repository,
13    Oid,
14};
15use serde::{Deserialize, Serialize};
16
17use sequoia_openpgp::{
18    self as openpgp,
19    Cert,
20    Fingerprint,
21    KeyHandle,
22    Packet,
23    cert::{
24        amalgamation::ValidAmalgamation,
25        prelude::{SubordinateKeyAmalgamation, UserIDAmalgamation},
26        raw::{RawCert, RawCertParser},
27    },
28    packet::{
29        Signature,
30        UserID,
31        key::PublicParts,
32    },
33    parse::Parse,
34    parse::{stream::*},
35    policy::StandardPolicy,
36    serialize::Serialize as _,
37    types::SignatureType,
38};
39
40use crate::{
41    Error,
42    Result,
43    utils::prune_cert,
44    utils::serialize_packet,
45    utils::serialize_signature,
46};
47
48/// Whether to trace execution by default (on stderr).
49const TRACE: bool = false;
50
51/// A policy for OpenPGP signatures in git.
52///
53/// A `Policy` governs state changes in git repositories.  A state
54/// change is a change from one git commit with a policy embedded into
55/// it to the next commit, which may change the policy, the source, or
56/// both.
57#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
58pub struct Policy {
59    /// Policy version.
60    ///
61    /// We provide backwards-compatibility but not
62    /// forward-compatibility, so that we can evolve the policy
63    /// language.
64    #[serde(default)]
65    version: usize,
66
67    /// Set of commits that is assumed to be good.
68    ///
69    /// The commits will pass verification even if it would fail for
70    /// whatever reason.
71    ///
72    /// To change this set, you need the `audit` right.
73    #[serde(default)]
74    commit_goodlist: BTreeSet<String>,
75
76    /// Set of authorizations.
77    ///
78    /// The key is a free-form, human-readable identifier for the
79    /// authorization.
80    #[serde(default)]
81    authorization: BTreeMap<String, Authorization>,
82}
83
84impl Policy {
85    /// Returns the path to the policy file in the current git
86    /// repository.
87    fn working_dir_policy_file()  -> Result<PathBuf> {
88        let git = git2::Repository::discover(env::current_dir()?)?;
89        if let Some(wd) = git.workdir() {
90            Ok(wd.join("openpgp-policy.toml"))
91        } else {
92            Err(Error::InvalidOperation("doesn't work on bare repos".into()))
93        }
94    }
95
96    pub fn parse_bytes<D: AsRef<[u8]>>(bytes: D) -> Result<Self> {
97        let bytes = bytes.as_ref();
98        let s = std::str::from_utf8(bytes)
99            .map_err(|e| Error::StorageError(e.to_string()))?;
100        let policy = toml::from_str(s)
101            .map_err(|e| Error::StorageError(e.to_string()))?;
102        Ok(policy)
103    }
104
105    /// Reads the policy from the given path.
106    pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Policy> {
107        let path = path.as_ref();
108        let mut f = fs::File::open(path)?;
109
110        let mut s = String::new();
111        f.read_to_string(&mut s)?;
112        let p: Policy =
113            toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
114
115        Ok(p)
116    }
117
118    /// Reads the policy from the given path.
119    pub fn read_file_or_default<P: AsRef<Path>>(path: P) -> Result<Policy> {
120        let path = path.as_ref();
121        let mut f = match fs::File::open(path) {
122            Ok(f) => f,
123            Err(e) => if e.kind() == io::ErrorKind::NotFound {
124                return Ok(Policy::default());
125            } else {
126                return Err(e.into());
127            },
128        };
129
130        let mut s = String::new();
131        f.read_to_string(&mut s)?;
132        let p: Policy =
133            toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
134
135        Ok(p)
136    }
137
138    /// Reads the policy from the current git working directory.
139    ///
140    /// Default to the empty policy if the work directory doesn't
141    /// contain a policy.
142    pub fn read_from_working_dir() -> Result<Policy> {
143        Self::read_file_or_default(&Self::working_dir_policy_file()?)
144    }
145
146    /// Reads the policy from the given git commit.
147    pub fn read_bytes_from_commit(git: &Repository, commit: &Oid)
148        -> Result<Vec<u8>>
149    {
150        tracer!(TRACE, "Policy::read_bytes_from_commit");
151        t!("(_, {})", commit);
152
153        let commit = git.find_commit(commit.clone())?;
154        let tree = commit.tree()?;
155        let result = if let Some(entry) = tree.get_name("openpgp-policy.toml") {
156            Ok(entry.to_object(&git)?.peel_to_blob()?.content().to_vec())
157        } else {
158            Err(Error::MissingPolicy(commit.id()))
159        };
160        result
161    }
162
163    /// Reads the policy from the given git commit.
164    pub fn read_from_commit(git: &Repository, commit: &Oid) -> Result<Self> {
165        Self::parse_bytes(Self::read_bytes_from_commit(git, commit)?)
166    }
167
168    /// Writes the policy into a file with the given path.
169    pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<()> {
170        let path = path.as_ref();
171        let mut new =
172            tempfile::NamedTempFile::new_in(path.parent().unwrap())?;
173
174        new.write_all(toml::to_string_pretty(&self)
175                      .map_err(|e| Error::StorageError(e.to_string()))?
176                      .as_bytes())?;
177
178        new.persist(path).map_err(|e| Error::StorageError(e.to_string()))?;
179
180        Ok(())
181    }
182
183    /// Writes the policy to the current git working directory.
184    pub fn write_to_working_dir(&self) -> Result<()> {
185        self.write(&Self::working_dir_policy_file()?)
186    }
187
188    /// The policy version.
189    pub fn version(&self) -> usize {
190        self.version
191    }
192
193    /// Set of commits that is assumed to be good.
194    ///
195    /// The commits will pass verification even if it would fail for
196    /// whatever reason.
197    ///
198    /// To change this set, you need the `audit` right.
199    pub fn commit_goodlist(&self) -> &BTreeSet<String> {
200        &self.commit_goodlist
201    }
202
203    /// Set of commits that is assumed to be good.
204    ///
205    /// The commits will pass verification even if it would fail for
206    /// whatever reason.
207    ///
208    /// To change this set, you need the `audit` right.
209    pub fn commit_goodlist_mut(&mut self) -> &mut BTreeSet<String> {
210        &mut self.commit_goodlist
211    }
212
213    /// Set of authorizations.
214    ///
215    /// The key is a free-form, human-readable identifier for the
216    /// authorization.
217    pub fn authorization(&self) -> &BTreeMap<String, Authorization> {
218        &self.authorization
219    }
220
221    /// Set of authorizations.
222    ///
223    /// The key is a free-form, human-readable identifier for the
224    /// authorization.
225    pub fn authorization_mut(&mut self) -> &mut BTreeMap<String, Authorization> {
226        &mut self.authorization
227    }
228
229    /// Computes the difference between this policy and `other`.
230    pub fn diff<'f, 't>(&'f self, other: &'t Policy) -> Result<Diff<'f, 't>> {
231        let mut changes = Vec::new();
232
233        // First, the version.
234        if self.version != other.version {
235            changes.push(Change::VersionChange {
236                from: self.version,
237                to: other.version,
238            });
239        }
240
241        // Then, the commit goodlist.
242        for c in self.commit_goodlist.difference(&other.commit_goodlist) {
243            changes.push(Change::UngoodlistCommit(c.parse()?));
244        }
245        for c in other.commit_goodlist.difference(&self.commit_goodlist) {
246            changes.push(Change::GoodlistCommit(c.parse()?));
247        }
248
249        // This null authorization comes in handy when introducing
250        // new users and removing users.
251        let null_auth = Authorization::default();
252
253        // Now for the authorizations.  First, see if some vanished.
254        for (k, from) in self.authorization.iter()
255            .filter(|(k, _)| ! other.authorization.contains_key(k.as_str()))
256        {
257            // First, remove all the rights and certs.
258            from.diff(&null_auth, k.into(), &mut changes);
259
260            // Finally, remove the user.
261            changes.push(Change::RetireUser(k.into()));
262        }
263
264        // Then, compare the common ones.
265        for (k, from, to) in self.authorization.iter()
266            .filter_map(|(k, from)| other.authorization.get(k)
267                        .map(|to| (k, from, to)))
268        {
269            from.diff(to, k.into(), &mut changes);
270        }
271
272        // See if new users were introduced.
273        for (k, to) in other.authorization.iter()
274            .filter(|(k, _)| ! self.authorization.contains_key(k.as_str()))
275        {
276            // First introduce the new user.
277            changes.push(Change::AddUser(k.into()));
278
279            // Then, all the new rights and certs.
280            null_auth.diff(to, k.into(), &mut changes);
281        }
282
283        Ok(Diff {
284            version: DIFF_JSON_VERSION,
285            from: self,
286            changes,
287            to: other,
288        })
289    }
290
291    /// Verifies that the given commit adheres to this policy.
292    ///
293    /// During verification, the key(s) used are stored in
294    /// `signer_keys`, and the primary user id of the issuing cert at
295    /// the time of the signing is stored in `primary_uids`.  This
296    /// information can be used to prune certs in a policy.
297    ///
298    /// If the commit is goodlisted, this function returns Ok with an
299    /// empty vector of verification results.
300    pub fn verify(&self, git: &Repository, commit_id: &Oid,
301                  commit_policy: &Policy,
302                  signer_keys: &mut BTreeSet<Fingerprint>,
303                  primary_uids: &mut BTreeSet<UserID>)
304                  -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
305    {
306        tracer!(TRACE, "Policy::verify");
307        t!("verify(_, {})", commit_id);
308
309        if self.commit_goodlist.contains(&commit_id.to_string()) {
310            Ok(vec![])
311        } else {
312            let Ok((sig, data)) = git.extract_signature(commit_id, None)
313            else {
314                return Ok(vec![Err(Error::MissingSignature(commit_id.clone()))]);
315            };
316            t!("{} bytes of signature", sig.len());
317
318            //let commit = git.find_commit(commit_id.clone())?;
319            //let commit_time = commit.time();
320            //let commit_time = std::time::UNIX_EPOCH
321            //    + std::time::Duration::new(commit_time.seconds() as u64, 0);
322
323            // Note the commit time and the signature time will often
324            // diverge.  This is because the signature is created
325            // after the commit is made (the signature is over the
326            // commit, including the creation time).  If we use the
327            // commit's time as the reference time, then the signature
328            // will appear to have been made in the future.  Note: it
329            // is not enough to allow a few seconds of divergence,
330            // because commits can be resigned without changing the
331            // commit's time.
332            self.verify_(&sig[..], &data[..],
333                         commit_policy,
334                         None,
335                         signer_keys,
336                         primary_uids,
337                         Error::MissingSignature(commit_id.clone()),
338                         Right::SignCommit)
339        }
340    }
341
342    pub fn verify_archive<T, S>(&self,
343                                signature: S,
344                                archive: T)
345                                -> Result<Vec<Result<(String, Signature, Cert,
346                                                      Fingerprint)>>>
347    where
348        T: AsRef<[u8]>,
349        S: AsRef<[u8]>,
350    {
351        let mut signer_keys = Default::default();
352        let mut primary_uids = Default::default();
353        self.verify_(signature.as_ref(),
354                     archive.as_ref(),
355                     self,
356                     None,
357                     &mut signer_keys,
358                     &mut primary_uids,
359                     Error::MissingDataSignature("Tarball".into()),
360                     Right::SignArchive)
361    }
362
363    fn verify_(&self,
364               signature: &[u8],
365               data: &[u8],
366               commit_policy: &Policy,
367               commit_time: Option<SystemTime>,
368               signer_keys: &mut BTreeSet<Fingerprint>,
369               primary_uids: &mut BTreeSet<UserID>,
370               missing_signature_error: Error,
371               require_right: Right)
372               -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
373    {
374        tracer!(TRACE, "Policy::verify_");
375        t!("verify_({} bytes, {} bytes, _, {:?}, _, _, {}, {})",
376           signature.len(), data.len(), commit_time,
377           missing_signature_error, require_right);
378
379        let p = &StandardPolicy::new();
380        let h = Helper {
381            parent_policy: self,
382            child_policy: commit_policy,
383            signer_keys,
384            primary_uids,
385            results: Default::default(),
386        };
387
388        let mut v = DetachedVerifierBuilder::from_bytes(signature)?
389            .with_policy(p, commit_time, h)?;
390        v.verify_bytes(data)?;
391        let h = v.into_helper();
392        let signature_results = h.results;
393
394        if signature_results.is_empty() {
395            t!("no signatures found!");
396            return Ok(vec![Err(missing_signature_error)]);
397        }
398
399        if signature_results.iter().all(|r| r.is_err()) {
400            let e = signature_results.into_iter().find(|r| r.is_err())
401                .expect("not empty and not all were ok");
402            return Err(e.unwrap_err());
403        }
404
405        // If we are here, there is at least one valid OpenPGP
406        // signature.  Compute the diff between the policies, and
407        // check whether the authorization invariant is intact.
408        let diff = self.diff(commit_policy)?;
409
410        let mut results: Vec<Result<(String, Signature, Cert, Fingerprint)>>
411            = Vec::new();
412        for r in signature_results {
413            match r {
414                Ok((sig, cert, signer_fpr)) => {
415                    // Find all authorizations that contain a
416                    // certificate that did issue a valid signature.
417                    let cert_fp = cert.fingerprint();
418                    for (name, a) in self.authorization.iter()
419                        .filter(|(_, a)| a.certs().into_iter()
420                                .flat_map(|r| r.into_iter())
421                                .flat_map(|r| r.into_iter())
422                                .any(|c| c.fingerprint() == cert_fp))
423                    {
424                        t!("{}: valid signature", name);
425                        let r = a.rights();
426                        t!("{}: {:?}", name, r);
427
428                        if let Err(e) = r.assert(require_right)
429                            .and_then(|_| diff.assert(&r))
430                        {
431                            results.push(Err(e));
432                        } else {
433                            results.push(
434                                Ok((name.into(), sig.clone(), cert.clone(),
435                                    signer_fpr.clone())));
436                        }
437                    }
438                },
439                Err(e) => results.push(Err(e)),
440            }
441        }
442
443        Ok(results)
444    }
445}
446
447// This fetches keys and computes the validity of the verification.
448struct Helper<'p> {
449    parent_policy: &'p Policy,
450    child_policy: &'p Policy,
451    //signer_userids: &'p mut BTreeSet<openpgp::packet::UserID>,
452    signer_keys: &'p mut BTreeSet<openpgp::Fingerprint>,
453    primary_uids: &'p mut BTreeSet<UserID>,
454    results: Vec<Result<(Signature, Cert, Fingerprint)>>,
455}
456
457impl Helper<'_> {
458    fn handle_result(&mut self, r: VerificationResult) {
459        tracer!(TRACE, "VerificationHelper::handle_result");
460        match r {
461            Ok(sig) => {
462                self.signer_keys.insert(sig.ka.fingerprint());
463
464                if let Ok(userid) = sig.ka.cert().primary_userid() {
465                    let u = userid.userid();
466                    if ! self.primary_uids.contains(u) {
467                        self.primary_uids.insert(u.clone());
468                    }
469                }
470
471                self.results.push(
472                    Ok((sig.sig.clone(), sig.ka.cert().cert().clone(),
473                        sig.ka.fingerprint().clone())));
474            },
475            Err(e) => {
476                t!("Signature verification failed: {}", e);
477                use VerificationError::*;
478                self.results.push(Err(match e {
479                    MalformedSignature { error, .. } =>
480                        Error::BadSignature(error.to_string()),
481                    MissingKey { sig } => {
482                        let mut issuers = sig.get_issuers();
483                        if issuers.is_empty() {
484                            Error::BadSignature(
485                                "No issuer information".into())
486                        } else {
487                            Error::MissingKey(issuers.remove(0))
488                        }
489                    },
490                    UnboundKey { cert, error, .. } =>
491                        Error::BadKey(cert.key_handle(),
492                                      error.to_string()),
493                    BadKey { ka, error, .. } =>
494                        Error::BadKey(ka.cert().key_handle(),
495                                      error.to_string()),
496                    BadSignature { error, .. } =>
497                        Error::BadSignature(error.to_string()),
498                }));
499            },
500        }
501    }
502}
503
504impl VerificationHelper for Helper<'_> {
505    fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
506        tracer!(TRACE, "VerificationHelper::get_certs");
507        t!("get_certs({:?})", ids);
508
509        let mut matches: BTreeMap<Fingerprint, Cert> = BTreeMap::new();
510
511        let mut parent: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
512
513        for (name, auth) in self.parent_policy.authorization.iter() {
514            for cert in auth.certs()? {
515                let cert = cert?;
516
517                // parent contains all of the certificates.
518                let entry = parent.entry(cert.fingerprint());
519                match entry {
520                    Entry::Occupied(mut oe) => {
521                        oe.get_mut().push(cert.clone());
522                    }
523                    Entry::Vacant(ve) => {
524                        ve.insert(vec![ cert.clone() ]);
525                    }
526                }
527
528                if cert.keys().any(
529                    |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
530                {
531                    t!("Signature could be from {}", name);
532
533                    let cert = Cert::try_from(cert)?;
534
535                    let entry = matches.entry(cert.fingerprint());
536                    match entry {
537                        Entry::Occupied(mut oe) => {
538                            let c: &mut Cert = oe.get_mut();
539                            *c = c.clone().merge_public(cert)?;
540                        }
541                        Entry::Vacant(ve) => {
542                            ve.insert(cert);
543                        }
544                    }
545                }
546            }
547        }
548
549        // We now merge in certificate updates from the child.  That
550        // is, we *don't* add new certificates.
551
552        // Optimize the typical case: the policies are the same and
553        // thus the child has no updates.
554        if self.parent_policy != self.child_policy {
555            // Merge child into parent stripping any revocation
556            // certificates from the child.
557            let merge = |parent: Cert, child: Cert| -> Result<Cert> {
558                let child = child.into_packets2().filter(|p| {
559                    if let Packet::Signature(sig) = p {
560                        ! matches!(sig.typ(),
561                                   SignatureType::KeyRevocation
562                                   | SignatureType::SubkeyRevocation
563                                   | SignatureType::CertificationRevocation)
564                    } else {
565                        true
566                    }
567                });
568
569                Ok(parent.insert_packets2(child)?.0)
570            };
571
572            for (name, auth) in self.child_policy.authorization.iter() {
573                let child_certs = if let Ok(certs) = auth.certs() {
574                    certs
575                } else {
576                    // Silently ignore any parse errors.
577                    continue;
578                };
579
580                for child_cert in child_certs {
581                    let child_cert = if let Ok(cert) = child_cert {
582                        cert
583                    } else {
584                        // Silently ignore any parse errors.
585                        continue;
586                    };
587
588                    let fpr = child_cert.fingerprint();
589                    if let Some(cert) = matches.get_mut(&fpr) {
590                        t!("Updating {}", fpr);
591
592                        let child_cert = if let Ok(cert)
593                            = Cert::try_from(child_cert)
594                        {
595                            cert
596                        } else {
597                            // Silently ignore any parse errors.
598                            continue;
599                        };
600
601                        if let Ok(merged) = merge(cert.clone(), child_cert) {
602                            *cert = merged;
603                        }
604                    } else {
605                        if child_cert.keys().any(
606                            |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
607                        {
608                            t!("Signature could be from {}", name);
609
610                            // cert is not in matches, but it is
611                            // present in the parent policy.  This
612                            // happens if the certificate gains a
613                            // subkey in the child policy, and the
614                            // commit is signed by that subkey.
615                            if let Some(certs)
616                                = parent.get(&child_cert.fingerprint())
617                            {
618                                if let Ok(child_cert) = Cert::try_from(child_cert) {
619                                    let mut parent: Option<Cert> = None;
620                                    for c in certs.into_iter() {
621                                        let c = if let Ok(c) = Cert::try_from(c) {
622                                            c
623                                        } else {
624                                            continue;
625                                        };
626                                        if let Some(parent) = parent.as_mut() {
627                                            if let Ok(merged) =
628                                                parent.clone().merge_public(c)
629                                            {
630                                                *parent = merged;
631                                            }
632                                        } else {
633                                            parent = Some(c);
634                                        }
635                                    }
636
637                                    let parent = if let Some(parent) = parent {
638                                        parent
639                                    } else {
640                                        continue;
641                                    };
642
643                                    // Merge in the child.
644                                    if let Ok(merged) = merge(parent, child_cert) {
645                                        matches.insert(
646                                            merged.fingerprint(), merged);
647                                    }
648                                }
649                            }
650                        }
651                    }
652                }
653            }
654        }
655
656        Ok(matches.into_values().collect())
657    }
658    fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
659        tracer!(TRACE, "VerificationHelper::get_certs");
660        if false {
661            t!("check({:?})", structure);
662        }
663
664        for (i, layer) in structure.into_iter().enumerate() {
665            match layer {
666                MessageLayer::SignatureGroup { results } if i == 0 => {
667                    for r in results {
668                        self.handle_result(r);
669                    }
670                },
671                _ => return Err(Error::BadSignature(
672                    "Unexpected signature structure".into()).into()),
673            }
674        }
675        Ok(())
676    }
677}
678
679#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
680pub struct Authorization {
681    #[serde(default, skip_serializing_if = "bool_is_false")]
682    pub sign_commit: bool,
683    #[serde(default, skip_serializing_if = "bool_is_false")]
684    pub sign_tag: bool,
685    #[serde(default, skip_serializing_if = "bool_is_false")]
686    pub sign_archive: bool,
687    #[serde(default, skip_serializing_if = "bool_is_false")]
688    pub add_user: bool,
689    #[serde(default, skip_serializing_if = "bool_is_false")]
690    pub retire_user: bool,
691    #[serde(default, skip_serializing_if = "bool_is_false")]
692    pub audit: bool,
693    pub keyring: String,
694}
695
696fn bool_is_false(b: &bool) -> bool {
697    *b == false
698}
699
700impl Authorization {
701    pub fn rights(&self) -> Rights {
702        use Right::*;
703
704        let mut r = BTreeSet::default();
705
706        if self.sign_commit {
707            r.insert(SignCommit);
708        }
709        if self.sign_tag {
710            r.insert(SignTag);
711        }
712        if self.sign_archive {
713            r.insert(SignArchive);
714        }
715        if self.add_user {
716            r.insert(AddUser);
717        }
718        if self.retire_user {
719            r.insert(RetireUser);
720        }
721        if self.audit {
722            r.insert(Audit);
723        }
724
725        Rights(r)
726    }
727
728    pub fn certs(&self) -> Result<impl Iterator<Item = openpgp::Result<RawCert>>> {
729        Ok(RawCertParser::from_bytes(self.keyring.as_bytes())?)
730    }
731
732    pub fn set_certs(&mut self, certs: Vec<openpgp::Cert>) -> Result<()> {
733        self.set_certs_filter(certs, |_| true, |_| true)
734    }
735
736    pub fn set_certs_filter<S, U>(&mut self, certs: Vec<openpgp::Cert>,
737                                  mut subkeys: S,
738                                  mut userids: U)
739                                  -> Result<()>
740    where
741        S: FnMut(&SubordinateKeyAmalgamation<PublicParts>) -> bool,
742        U: FnMut(&UserIDAmalgamation) -> bool,
743    {
744        let mut keyring = Vec::new();
745
746        for c in certs {
747            let c = prune_cert(c, &mut subkeys, &mut userids)?;
748            c.armored().export(&mut keyring)?;
749        }
750
751        self.keyring = String::from_utf8(keyring)
752            .map_err(|e| Error::StorageError(e.to_string()))?;
753        Ok(())
754    }
755
756    /// Computes the difference between this authorization and `other`
757    /// recording the changes in `changes`.
758    fn diff(&self, other: &Authorization, name: String,
759            changes: &mut Vec<Change>)
760    {
761        let (from, to) = (self, other);
762
763        // First, see if rights were removed.
764        if from.sign_commit && ! to.sign_commit {
765            changes.push(Change::RemoveRight(name.clone(), Right::SignCommit));
766        }
767        if from.sign_tag && ! to.sign_tag {
768            changes.push(Change::RemoveRight(name.clone(), Right::SignTag));
769        }
770        if from.sign_archive && ! to.sign_archive {
771            changes.push(Change::RemoveRight(name.clone(), Right::SignArchive));
772        }
773        if from.add_user && ! to.add_user {
774            changes.push(Change::RemoveRight(name.clone(), Right::AddUser));
775        }
776        if from.retire_user && ! to.retire_user {
777            changes.push(Change::RemoveRight(name.clone(), Right::RetireUser));
778        }
779        if from.audit && ! to.audit {
780            changes.push(Change::RemoveRight(name.clone(), Right::Audit));
781        }
782
783        // Then, see if rights were added.
784        if ! from.sign_commit && to.sign_commit {
785            changes.push(Change::AddRight(name.clone(), Right::SignCommit));
786        }
787        if ! from.sign_tag && to.sign_tag {
788            changes.push(Change::AddRight(name.clone(), Right::SignTag));
789        }
790        if ! from.sign_archive && to.sign_archive {
791            changes.push(Change::AddRight(name.clone(), Right::SignArchive));
792        }
793        if ! from.add_user && to.add_user {
794            changes.push(Change::AddRight(name.clone(), Right::AddUser));
795        }
796        if ! from.retire_user && to.retire_user {
797            changes.push(Change::AddRight(name.clone(), Right::RetireUser));
798        }
799        if ! from.audit && to.audit {
800            changes.push(Change::AddRight(name.clone(), Right::Audit));
801        }
802
803        // Check for changes to the certificates.
804        if self.keyring != other.keyring {
805            // Parse the keyring.
806            //
807            // XXX: We silently ignore certificates that we are unable
808            // to parse.  We should perhaps reject any changes that we
809            // don't understand.
810            fn parse_keyring<'a>(name: &str, keyring: &'a str)
811                -> BTreeMap<Fingerprint, Vec<RawCert<'a>>>
812            {
813                match RawCertParser::from_bytes(keyring) {
814                    Err(err) => {
815                        eprintln!("Parsing {}'s keyring: {}", name, err);
816                        Default::default()
817                    }
818                    Ok(certs) => {
819                        let certs = certs.into_iter()
820                            .filter_map(|cert| {
821                                match cert {
822                                    Err(err) => {
823                                        eprintln!("Parsing certificate from {}'s keyring: {}",
824                                                  name, err);
825                                        None
826                                    }
827                                    Ok(cert) => Some(cert),
828                                }
829                            });
830
831                        let mut map: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
832                        for cert in certs {
833                            let entry = map.entry(cert.fingerprint());
834                            match entry {
835                                Entry::Occupied(mut oe) => {
836                                    let oe: &mut Vec<RawCert> = oe.get_mut();
837                                    oe.push(cert);
838                                }
839                                Entry::Vacant(ve) => {
840                                    ve.insert(vec![ cert ]);
841                                }
842                            }
843                        }
844
845                        map
846                    }
847                }
848            }
849
850            let old = parse_keyring(&name, &self.keyring);
851            let new = parse_keyring(&name, &other.keyring);
852
853            // Identify certificates have been added or removed.
854            let old_certs: BTreeSet<&Fingerprint> = old.keys().collect();
855            let new_certs: BTreeSet<&Fingerprint> = new.keys().collect();
856
857            for &removed in old_certs.difference(&new_certs) {
858                changes.push(Change::RemoveCert(name.clone(), removed.clone()));
859            }
860            for &added in new_certs.difference(&old_certs) {
861                changes.push(Change::AddCert(name.clone(), added.clone()));
862            }
863
864            // For certificates that the two keyrings have in common,
865            // identify the packets that have been added or removed.
866            for &fpr in old_certs.intersection(&new_certs) {
867                let old = old.get(fpr).expect("have it");
868                let new = new.get(fpr).expect("have it");
869
870                if old == new {
871                    continue;
872                }
873
874                // Convert each signature into a tuple consisting of a
875                // signature and its associated component.
876                let into_packets = |certs: Vec<RawCert>| -> HashSet<(Packet, Signature)> {
877                    let mut pairs: HashSet<(Packet, Signature)> = HashSet::new();
878
879                    for cert in certs.into_iter() {
880                        let mut packets = cert.packets();
881                        let primary_key
882                            = packets.next().expect("have a primary key");
883                        let primary_key = match Packet::try_from(primary_key) {
884                            Ok(p) => p,
885                            Err(err) => {
886                                eprintln!(
887                                    "Warning: {} has a corrupted primary key \
888                                     packet (skipped): {}",
889                                    cert.fingerprint(), err);
890                                continue;
891                            }
892                        };
893
894                        packets.fold(
895                            primary_key,
896                            |component, packet| {
897                                let packet = match Packet::try_from(packet) {
898                                    Ok(p) => p,
899                                    Err(err) => {
900                                        eprintln!(
901                                            "Warning: {} has a corrupted packet \
902                                             (skipped): {}",
903                                            cert.fingerprint(), err);
904                                        return component;
905                                    }
906                                };
907
908                                match packet {
909                                    Packet::Signature(sig) => {
910                                        pairs.insert(
911                                            (component.clone(), sig.clone()));
912                                        component
913                                    }
914                                    Packet::Marker(_) => {
915                                        // Silently skip marker packets.
916                                        component
917                                    }
918                                    _ => {
919                                        // New component.
920                                        packet.clone()
921                                    }
922                                }
923                            });
924                    }
925
926                    pairs
927                };
928
929                let old_packets = into_packets(old.clone());
930                let new_packets = into_packets(new.clone());
931
932                for (component, sig) in old_packets.difference(&new_packets) {
933                    changes.push(Change::RemovePacket(name.clone(),
934                                                      fpr.clone(),
935                                                      component.clone(),
936                                                      sig.clone()));
937                }
938                for (component, sig) in new_packets.difference(&old_packets) {
939                    changes.push(Change::AddPacket(name.clone(),
940                                                   fpr.clone(),
941                                                   component.clone(),
942                                                   sig.clone()));
943                }
944            }
945        }
946    }
947}
948
949// The version of the commit output.  This follows semantic
950// versioning.
951static DIFF_JSON_VERSION: &'static str = "1.0.0";
952
953/// The difference between two [`Policy`]s.
954#[derive(Serialize)]
955pub struct Diff<'f, 't> {
956    version: &'static str,
957
958    pub from: &'f Policy,
959    pub changes: Vec<Change>,
960    pub to: &'t Policy,
961}
962
963impl Diff<'_, '_> {
964    fn assert(&self, r: &Rights) -> Result<()> {
965        for c in &self.changes {
966            c.assert(r)?;
967        }
968        Ok(())
969    }
970}
971
972use crate::utils::{serialize_fp, serialize_oid};
973
974#[derive(Clone, Serialize)]
975pub enum Change {
976    VersionChange {
977        from: usize,
978        to: usize,
979    },
980    GoodlistCommit(
981        #[serde(serialize_with = "serialize_oid")] Oid),
982    UngoodlistCommit(
983        #[serde(serialize_with = "serialize_oid")] Oid),
984
985    AddUser(String),
986    RetireUser(String),
987
988    AddRight(String, Right),
989    RemoveRight(String, Right),
990
991    AddCert(String,
992            #[serde(serialize_with = "serialize_fp")] Fingerprint),
993    RemoveCert(String,
994               #[serde(serialize_with = "serialize_fp")] Fingerprint),
995
996    AddPacket(String,
997              // The certificate.
998              #[serde(serialize_with = "serialize_fp")] Fingerprint,
999              // The component that the packet is associated with.
1000              #[serde(serialize_with = "serialize_packet")] Packet,
1001              // The signature that was added.
1002              #[serde(serialize_with = "serialize_signature")] Signature),
1003    RemovePacket(String,
1004                 // The certificate.
1005                 #[serde(serialize_with = "serialize_fp")] Fingerprint,
1006                 // The component that the packet is associated with.
1007                 #[serde(serialize_with = "serialize_packet")] Packet,
1008                 // The signature that was removed.
1009                 #[serde(serialize_with = "serialize_signature")] Signature),
1010}
1011
1012impl Change {
1013    fn assert(&self, r: &Rights) -> Result<()> {
1014        use Change::*;
1015        match self {
1016            VersionChange { .. } => r.assert(Right::Audit),
1017            GoodlistCommit(_) => r.assert(Right::Audit),
1018            UngoodlistCommit(_) => r.assert(Right::Audit),
1019
1020            // Rights management.
1021            AddUser(_) => r.assert(Right::AddUser),
1022            RetireUser(_) => r.assert(Right::RetireUser),
1023            AddRight(_, right) =>
1024                r.assert(Right::AddUser).and_then(|_| r.assert(*right)),
1025            RemoveRight(_, right) =>
1026                r.assert(Right::RetireUser).and_then(|_| r.assert(*right)),
1027
1028            // Cert management.
1029            AddCert(_, _) => r.assert(Right::AddUser),
1030            RemoveCert(_, _) => r.assert(Right::RetireUser),
1031
1032            // Lenient cert updates.
1033            AddPacket(_, _, _, _) => Ok(()),
1034
1035            // Strict cert trimmings.
1036            RemovePacket(_, fpr, _component, sig) => {
1037                if sig.get_issuers().into_iter()
1038                    .any(|kh| kh.aliases(KeyHandle::from(fpr)))
1039                {
1040                    // First party signatures can only be removed by
1041                    // authorized users.
1042                    r.assert(Right::RetireUser)
1043                } else {
1044                    Ok(())
1045                }
1046            }
1047        }
1048    }
1049}
1050
1051#[derive(Debug)]
1052pub struct Rights(BTreeSet<Right>);
1053
1054impl Rights {
1055    fn assert(&self, r: Right) -> Result<()> {
1056        if ! self.0.contains(&r) {
1057            Err(Error::Unauthorized(format!("Right {} is missing", r)))
1058        } else {
1059            Ok(())
1060        }
1061    }
1062}
1063
1064#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)]
1065pub enum Right {
1066    SignCommit,
1067    SignTag,
1068    SignArchive,
1069    AddUser,
1070    RetireUser,
1071    Audit,
1072}
1073
1074impl fmt::Display for Right {
1075    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076        use Right::*;
1077        match self {
1078            SignCommit => f.write_str("sign-commit"),
1079            SignTag => f.write_str("sign-tag"),
1080            SignArchive => f.write_str("sign-archive"),
1081            AddUser => f.write_str("add-user"),
1082            RetireUser => f.write_str("retire-user"),
1083            Audit => f.write_str("audit"),
1084        }
1085    }
1086}