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 anyhow::Context;
12
13use git2::{
14 Repository,
15 Oid,
16};
17use serde::{Deserialize, Serialize};
18
19use sequoia_openpgp::{
20 self as openpgp,
21 Cert,
22 Fingerprint,
23 KeyHandle,
24 Packet,
25 cert::{
26 amalgamation::ValidAmalgamation,
27 prelude::{SubordinateKeyAmalgamation, UserIDAmalgamation},
28 raw::{RawCert, RawCertParser},
29 },
30 packet::{
31 Signature,
32 UserID,
33 key::PublicParts,
34 },
35 parse::Parse,
36 parse::{stream::*},
37 policy::StandardPolicy,
38 serialize::Serialize as _,
39 types::SignatureType,
40};
41
42use crate::{
43 Error,
44 Result,
45 utils::prune_cert,
46 utils::serialize_packet,
47 utils::serialize_signature,
48};
49
50const TRACE: bool = false;
52
53#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
60pub struct Policy {
61 #[serde(default)]
67 version: usize,
68
69 #[serde(default)]
76 commit_goodlist: BTreeSet<String>,
77
78 #[serde(default)]
83 authorization: BTreeMap<String, Authorization>,
84}
85
86impl Policy {
87 fn working_dir_policy_file() -> Result<PathBuf> {
90 let git = git2::Repository::discover(env::current_dir()?)?;
91 if let Some(wd) = git.workdir() {
92 Ok(wd.join("openpgp-policy.toml"))
93 } else {
94 Err(Error::InvalidOperation("doesn't work on bare repos".into()))
95 }
96 }
97
98 pub fn parse_bytes<D: AsRef<[u8]>>(bytes: D) -> Result<Self> {
99 let bytes = bytes.as_ref();
100 let s = std::str::from_utf8(bytes)
101 .map_err(|e| Error::StorageError(e.to_string()))?;
102 let policy = toml::from_str(s)
103 .map_err(|e| Error::StorageError(e.to_string()))?;
104 Ok(policy)
105 }
106
107 pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Policy> {
109 let path = path.as_ref();
110 let mut f = fs::File::open(path)?;
111
112 let mut s = String::new();
113 f.read_to_string(&mut s)?;
114 let p: Policy =
115 toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
116
117 Ok(p)
118 }
119
120 pub fn read_file_or_default<P: AsRef<Path>>(path: P) -> Result<Policy> {
122 let path = path.as_ref();
123 let mut f = match fs::File::open(path) {
124 Ok(f) => f,
125 Err(e) => if e.kind() == io::ErrorKind::NotFound {
126 return Ok(Policy::default());
127 } else {
128 return Err(e.into());
129 },
130 };
131
132 let mut s = String::new();
133 f.read_to_string(&mut s)?;
134 let p: Policy =
135 toml::from_str(&s).map_err(|e| Error::StorageError(e.to_string()))?;
136
137 Ok(p)
138 }
139
140 pub fn read_from_working_dir() -> Result<Policy> {
145 Self::read_file_or_default(&Self::working_dir_policy_file()?)
146 }
147
148 pub fn read_bytes_from_commit(git: &Repository, commit: &Oid)
150 -> Result<Vec<u8>>
151 {
152 tracer!(TRACE, "Policy::read_bytes_from_commit");
153 t!("(_, {})", commit);
154
155 let commit = git.find_commit(commit.clone())?;
156 let tree = commit.tree()?;
157 let result = if let Some(entry) = tree.get_name("openpgp-policy.toml") {
158 Ok(entry.to_object(&git)?.peel_to_blob()?.content().to_vec())
159 } else {
160 Err(Error::MissingPolicy(commit.id()))
161 };
162 result
163 }
164
165 pub fn read_from_commit(git: &Repository, commit: &Oid) -> Result<Self> {
167 Self::parse_bytes(Self::read_bytes_from_commit(git, commit)?)
168 }
169
170 pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<()> {
172 let path = path.as_ref();
173 let mut new =
174 tempfile::NamedTempFile::new_in(path.parent().unwrap())?;
175
176 new.write_all(toml::to_string_pretty(&self)
177 .map_err(|e| Error::StorageError(e.to_string()))?
178 .as_bytes())?;
179
180 new.persist(path).map_err(|e| Error::StorageError(e.to_string()))?;
181
182 Ok(())
183 }
184
185 pub fn write_to_working_dir(&self) -> Result<()> {
187 self.write(&Self::working_dir_policy_file()?)
188 }
189
190 pub fn version(&self) -> usize {
192 self.version
193 }
194
195 pub fn commit_goodlist(&self) -> &BTreeSet<String> {
202 &self.commit_goodlist
203 }
204
205 pub fn commit_goodlist_mut(&mut self) -> &mut BTreeSet<String> {
212 &mut self.commit_goodlist
213 }
214
215 pub fn authorization(&self) -> &BTreeMap<String, Authorization> {
220 &self.authorization
221 }
222
223 pub fn authorization_mut(&mut self) -> &mut BTreeMap<String, Authorization> {
228 &mut self.authorization
229 }
230
231 pub fn diff<'f, 't>(&'f self, other: &'t Policy) -> Result<Diff<'f, 't>> {
233 let mut changes = Vec::new();
234
235 if self.version != other.version {
237 changes.push(Change::VersionChange {
238 from: self.version,
239 to: other.version,
240 });
241 }
242
243 for c in self.commit_goodlist.difference(&other.commit_goodlist) {
245 changes.push(Change::UngoodlistCommit(c.parse()?));
246 }
247 for c in other.commit_goodlist.difference(&self.commit_goodlist) {
248 changes.push(Change::GoodlistCommit(c.parse()?));
249 }
250
251 let null_auth = Authorization::default();
254
255 for (k, from) in self.authorization.iter()
257 .filter(|(k, _)| ! other.authorization.contains_key(k.as_str()))
258 {
259 from.diff(&null_auth, k.into(), &mut changes);
261
262 changes.push(Change::RetireUser(k.into()));
264 }
265
266 for (k, from, to) in self.authorization.iter()
268 .filter_map(|(k, from)| other.authorization.get(k)
269 .map(|to| (k, from, to)))
270 {
271 from.diff(to, k.into(), &mut changes);
272 }
273
274 for (k, to) in other.authorization.iter()
276 .filter(|(k, _)| ! self.authorization.contains_key(k.as_str()))
277 {
278 changes.push(Change::AddUser(k.into()));
280
281 null_auth.diff(to, k.into(), &mut changes);
283 }
284
285 Ok(Diff {
286 version: DIFF_JSON_VERSION,
287 from: self,
288 changes,
289 to: other,
290 })
291 }
292
293 pub fn verify(&self, git: &Repository, commit_id: &Oid,
303 commit_policy: &Policy,
304 signer_keys: &mut BTreeSet<Fingerprint>,
305 primary_uids: &mut BTreeSet<UserID>)
306 -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
307 {
308 tracer!(TRACE, "Policy::verify");
309 t!("verify(_, {})", commit_id);
310
311 if self.commit_goodlist.contains(&commit_id.to_string()) {
312 Ok(vec![])
313 } else {
314 let Ok((sig, data)) = git.extract_signature(commit_id, None)
315 else {
316 return Ok(vec![Err(Error::MissingSignature(commit_id.clone()))]);
317 };
318 t!("{} bytes of signature", sig.len());
319
320 self.verify_(&sig[..], &data[..],
335 commit_policy,
336 None,
337 signer_keys,
338 primary_uids,
339 Error::MissingSignature(commit_id.clone()),
340 Right::SignCommit)
341 }
342 }
343
344 pub fn verify_tag(&self, git: &Repository, tag_id: Oid,
351 commit_policy: &Policy,
352 signer_keys: &mut BTreeSet<Fingerprint>,
353 primary_uids: &mut BTreeSet<UserID>)
354 -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
355 {
356 tracer!(TRACE, "Policy::verify_tag");
357
358 let odb = git.odb().context("Getting repository's odb")?;
367 let reader = odb.read(tag_id)
368 .with_context(|| {
369 format!("Reading tag object ({})", tag_id)
370 })?;
371 let content = reader.data();
372
373 let begin_signature = b"-----BEGIN PGP SIGNATURE-----\n";
375 let mut start = None;
376 for (i, window) in content.windows(begin_signature.len()).enumerate() {
377 if window == begin_signature {
378 start = Some(i);
379 break;
380 }
381 }
382 let Some(start) = start else {
383 t!("Tag does not contain a signature");
384 return Ok(vec![Err(Error::MissingSignature(tag_id.clone()))]);
385 };
386 let data = &content[..start];
387 t!("Data: {}", String::from_utf8_lossy(data));
388 let sig = &content[start..];
389 t!("Signature: {}", String::from_utf8_lossy(sig));
390
391 self.verify_(sig, data,
406 commit_policy,
407 None,
408 signer_keys,
409 primary_uids,
410 Error::MissingSignature(tag_id.clone()),
411 Right::SignTag)
412 }
413
414 pub fn verify_archive<T, S>(&self,
415 signature: S,
416 archive: T)
417 -> Result<Vec<Result<(String, Signature, Cert,
418 Fingerprint)>>>
419 where
420 T: AsRef<[u8]>,
421 S: AsRef<[u8]>,
422 {
423 let mut signer_keys = Default::default();
424 let mut primary_uids = Default::default();
425 self.verify_(signature.as_ref(),
426 archive.as_ref(),
427 self,
428 None,
429 &mut signer_keys,
430 &mut primary_uids,
431 Error::MissingDataSignature("Tarball".into()),
432 Right::SignArchive)
433 }
434
435 fn verify_(&self,
436 signature: &[u8],
437 data: &[u8],
438 commit_policy: &Policy,
439 commit_time: Option<SystemTime>,
440 signer_keys: &mut BTreeSet<Fingerprint>,
441 primary_uids: &mut BTreeSet<UserID>,
442 missing_signature_error: Error,
443 require_right: Right)
444 -> Result<Vec<Result<(String, Signature, Cert, Fingerprint)>>>
445 {
446 tracer!(TRACE, "Policy::verify_");
447 t!("verify_({} bytes, {} bytes, _, {:?}, _, _, {}, {})",
448 signature.len(), data.len(), commit_time,
449 missing_signature_error, require_right);
450
451 let p = &StandardPolicy::new();
452 let h = Helper {
453 parent_policy: self,
454 child_policy: commit_policy,
455 signer_keys,
456 primary_uids,
457 results: Default::default(),
458 };
459
460 let mut v = DetachedVerifierBuilder::from_bytes(signature)?
461 .with_policy(p, commit_time, h)?;
462 v.verify_bytes(data)?;
463 let h = v.into_helper();
464 let signature_results = h.results;
465
466 if signature_results.is_empty() {
467 t!("no signatures found!");
468 return Ok(vec![Err(missing_signature_error)]);
469 }
470
471 if signature_results.iter().all(|r| r.is_err()) {
472 let e = signature_results.into_iter().find(|r| r.is_err())
473 .expect("not empty and not all were ok");
474 return Err(e.unwrap_err());
475 }
476
477 let diff = self.diff(commit_policy)?;
481
482 let mut results: Vec<Result<(String, Signature, Cert, Fingerprint)>>
483 = Vec::new();
484 for r in signature_results {
485 match r {
486 Ok((sig, cert, signer_fpr)) => {
487 let cert_fp = cert.fingerprint();
490 for (name, a) in self.authorization.iter()
491 .filter(|(_, a)| a.certs().into_iter()
492 .flat_map(|r| r.into_iter())
493 .flat_map(|r| r.into_iter())
494 .any(|c| c.fingerprint() == cert_fp))
495 {
496 t!("{}: valid signature", name);
497 let r = a.rights();
498 t!("{}: {:?}", name, r);
499
500 if let Err(e) = r.assert(require_right)
501 .and_then(|_| diff.assert(&r))
502 {
503 results.push(Err(e));
504 } else {
505 results.push(
506 Ok((name.into(), sig.clone(), cert.clone(),
507 signer_fpr.clone())));
508 }
509 }
510 },
511 Err(e) => results.push(Err(e)),
512 }
513 }
514
515 Ok(results)
516 }
517}
518
519struct Helper<'p> {
521 parent_policy: &'p Policy,
522 child_policy: &'p Policy,
523 signer_keys: &'p mut BTreeSet<openpgp::Fingerprint>,
525 primary_uids: &'p mut BTreeSet<UserID>,
526 results: Vec<Result<(Signature, Cert, Fingerprint)>>,
527}
528
529impl Helper<'_> {
530 fn handle_result(&mut self, r: VerificationResult) {
531 tracer!(TRACE, "VerificationHelper::handle_result");
532 match r {
533 Ok(sig) => {
534 self.signer_keys.insert(sig.ka.key().fingerprint());
535
536 if let Ok(userid) = sig.ka.valid_cert().primary_userid() {
537 let u = userid.userid();
538 if ! self.primary_uids.contains(u) {
539 self.primary_uids.insert(u.clone());
540 }
541 }
542
543 self.results.push(
544 Ok((sig.sig.clone(), sig.ka.cert().clone(),
545 sig.ka.key().fingerprint().clone())));
546 },
547 Err(e) => {
548 t!("Signature verification failed: {}", e);
549 use VerificationError::*;
550 self.results.push(Err(match e {
551 MalformedSignature { error, .. } =>
552 Error::BadSignature(error.to_string()),
553 MissingKey { sig } => {
554 let mut issuers = sig.get_issuers();
555 if issuers.is_empty() {
556 Error::BadSignature(
557 "No issuer information".into())
558 } else {
559 Error::MissingKey(issuers.remove(0))
560 }
561 },
562 UnboundKey { cert, error, .. } =>
563 Error::BadKey(cert.key_handle(),
564 error.to_string()),
565 BadKey { ka, error, .. } =>
566 Error::BadKey(ka.cert().key_handle(),
567 error.to_string()),
568 BadSignature { error, .. } =>
569 Error::BadSignature(error.to_string()),
570 UnknownSignature { sig, .. } =>
571 Error::BadSignature(sig.error().to_string()),
572 u =>
573 Error::BadSignature(u.to_string()),
574 }));
575 },
576 }
577 }
578}
579
580impl VerificationHelper for Helper<'_> {
581 fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
582 tracer!(TRACE, "VerificationHelper::get_certs");
583 t!("get_certs({:?})", ids);
584
585 let mut matches: BTreeMap<Fingerprint, Cert> = BTreeMap::new();
586
587 let mut parent: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
588
589 for (name, auth) in self.parent_policy.authorization.iter() {
590 for cert in auth.certs()? {
591 let cert = cert?;
592
593 let entry = parent.entry(cert.fingerprint());
595 match entry {
596 Entry::Occupied(mut oe) => {
597 oe.get_mut().push(cert.clone());
598 }
599 Entry::Vacant(ve) => {
600 ve.insert(vec![ cert.clone() ]);
601 }
602 }
603
604 if cert.keys().any(
605 |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
606 {
607 t!("Signature could be from {}", name);
608
609 let cert = Cert::try_from(cert)?;
610
611 let entry = matches.entry(cert.fingerprint());
612 match entry {
613 Entry::Occupied(mut oe) => {
614 let c: &mut Cert = oe.get_mut();
615 *c = c.clone().merge_public(cert)?;
616 }
617 Entry::Vacant(ve) => {
618 ve.insert(cert);
619 }
620 }
621 }
622 }
623 }
624
625 if self.parent_policy != self.child_policy {
631 let merge = |parent: Cert, child: Cert| -> Result<Cert> {
634 let child = child.into_packets().filter(|p| {
635 if let Packet::Signature(sig) = p {
636 ! matches!(sig.typ(),
637 SignatureType::KeyRevocation
638 | SignatureType::SubkeyRevocation
639 | SignatureType::CertificationRevocation)
640 } else {
641 true
642 }
643 });
644
645 Ok(parent.insert_packets(child)?.0)
646 };
647
648 for (name, auth) in self.child_policy.authorization.iter() {
649 let child_certs = if let Ok(certs) = auth.certs() {
650 certs
651 } else {
652 continue;
654 };
655
656 for child_cert in child_certs {
657 let child_cert = if let Ok(cert) = child_cert {
658 cert
659 } else {
660 continue;
662 };
663
664 let fpr = child_cert.fingerprint();
665 if let Some(cert) = matches.get_mut(&fpr) {
666 t!("Updating {}", fpr);
667
668 let child_cert = if let Ok(cert)
669 = Cert::try_from(child_cert)
670 {
671 cert
672 } else {
673 continue;
675 };
676
677 if let Ok(merged) = merge(cert.clone(), child_cert) {
678 *cert = merged;
679 }
680 } else {
681 if child_cert.keys().any(
682 |k| ids.iter().any(|i| i.aliases(&k.key_handle())))
683 {
684 t!("Signature could be from {}", name);
685
686 if let Some(certs)
692 = parent.get(&child_cert.fingerprint())
693 {
694 if let Ok(child_cert) = Cert::try_from(child_cert) {
695 let mut parent: Option<Cert> = None;
696 for c in certs.into_iter() {
697 let c = if let Ok(c) = Cert::try_from(c) {
698 c
699 } else {
700 continue;
701 };
702 if let Some(parent) = parent.as_mut() {
703 if let Ok(merged) =
704 parent.clone().merge_public(c)
705 {
706 *parent = merged;
707 }
708 } else {
709 parent = Some(c);
710 }
711 }
712
713 let parent = if let Some(parent) = parent {
714 parent
715 } else {
716 continue;
717 };
718
719 if let Ok(merged) = merge(parent, child_cert) {
721 matches.insert(
722 merged.fingerprint(), merged);
723 }
724 }
725 }
726 }
727 }
728 }
729 }
730 }
731
732 Ok(matches.into_values().collect())
733 }
734 fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
735 tracer!(TRACE, "VerificationHelper::get_certs");
736 if false {
737 t!("check({:?})", structure);
738 }
739
740 for (i, layer) in structure.into_iter().enumerate() {
741 match layer {
742 MessageLayer::SignatureGroup { results } if i == 0 => {
743 for r in results {
744 self.handle_result(r);
745 }
746 },
747 _ => return Err(Error::BadSignature(
748 "Unexpected signature structure".into()).into()),
749 }
750 }
751 Ok(())
752 }
753}
754
755#[derive(Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
756pub struct Authorization {
757 #[serde(default, skip_serializing_if = "bool_is_false")]
758 pub sign_commit: bool,
759 #[serde(default, skip_serializing_if = "bool_is_false")]
760 pub sign_tag: bool,
761 #[serde(default, skip_serializing_if = "bool_is_false")]
762 pub sign_archive: bool,
763 #[serde(default, skip_serializing_if = "bool_is_false")]
764 pub add_user: bool,
765 #[serde(default, skip_serializing_if = "bool_is_false")]
766 pub retire_user: bool,
767 #[serde(default, skip_serializing_if = "bool_is_false")]
768 pub audit: bool,
769 pub keyring: String,
770}
771
772fn bool_is_false(b: &bool) -> bool {
773 *b == false
774}
775
776impl Authorization {
777 pub fn rights(&self) -> Rights {
778 use Right::*;
779
780 let mut r = BTreeSet::default();
781
782 if self.sign_commit {
783 r.insert(SignCommit);
784 }
785 if self.sign_tag {
786 r.insert(SignTag);
787 }
788 if self.sign_archive {
789 r.insert(SignArchive);
790 }
791 if self.add_user {
792 r.insert(AddUser);
793 }
794 if self.retire_user {
795 r.insert(RetireUser);
796 }
797 if self.audit {
798 r.insert(Audit);
799 }
800
801 Rights(r)
802 }
803
804 pub fn certs(&self) -> Result<impl Iterator<Item = openpgp::Result<RawCert<'_>>>> {
805 Ok(RawCertParser::from_bytes(self.keyring.as_bytes())?)
806 }
807
808 pub fn set_certs(&mut self, certs: Vec<openpgp::Cert>) -> Result<()> {
809 self.set_certs_filter(certs, |_| true, |_| true)
810 }
811
812 pub fn set_certs_filter<S, U>(&mut self, certs: Vec<openpgp::Cert>,
813 mut subkeys: S,
814 mut userids: U)
815 -> Result<()>
816 where
817 S: FnMut(&SubordinateKeyAmalgamation<PublicParts>) -> bool,
818 U: FnMut(&UserIDAmalgamation) -> bool,
819 {
820 let mut keyring = Vec::new();
821
822 for c in certs {
823 let c = prune_cert(c, &mut subkeys, &mut userids)?;
824 c.armored().export(&mut keyring)?;
825 }
826
827 self.keyring = String::from_utf8(keyring)
828 .map_err(|e| Error::StorageError(e.to_string()))?;
829 Ok(())
830 }
831
832 fn diff(&self, other: &Authorization, name: String,
835 changes: &mut Vec<Change>)
836 {
837 let (from, to) = (self, other);
838
839 if from.sign_commit && ! to.sign_commit {
841 changes.push(Change::RemoveRight(name.clone(), Right::SignCommit));
842 }
843 if from.sign_tag && ! to.sign_tag {
844 changes.push(Change::RemoveRight(name.clone(), Right::SignTag));
845 }
846 if from.sign_archive && ! to.sign_archive {
847 changes.push(Change::RemoveRight(name.clone(), Right::SignArchive));
848 }
849 if from.add_user && ! to.add_user {
850 changes.push(Change::RemoveRight(name.clone(), Right::AddUser));
851 }
852 if from.retire_user && ! to.retire_user {
853 changes.push(Change::RemoveRight(name.clone(), Right::RetireUser));
854 }
855 if from.audit && ! to.audit {
856 changes.push(Change::RemoveRight(name.clone(), Right::Audit));
857 }
858
859 if ! from.sign_commit && to.sign_commit {
861 changes.push(Change::AddRight(name.clone(), Right::SignCommit));
862 }
863 if ! from.sign_tag && to.sign_tag {
864 changes.push(Change::AddRight(name.clone(), Right::SignTag));
865 }
866 if ! from.sign_archive && to.sign_archive {
867 changes.push(Change::AddRight(name.clone(), Right::SignArchive));
868 }
869 if ! from.add_user && to.add_user {
870 changes.push(Change::AddRight(name.clone(), Right::AddUser));
871 }
872 if ! from.retire_user && to.retire_user {
873 changes.push(Change::AddRight(name.clone(), Right::RetireUser));
874 }
875 if ! from.audit && to.audit {
876 changes.push(Change::AddRight(name.clone(), Right::Audit));
877 }
878
879 if self.keyring != other.keyring {
881 fn parse_keyring<'a>(name: &str, keyring: &'a str)
887 -> BTreeMap<Fingerprint, Vec<RawCert<'a>>>
888 {
889 match RawCertParser::from_bytes(keyring) {
890 Err(err) => {
891 eprintln!("Parsing {}'s keyring: {}", name, err);
892 Default::default()
893 }
894 Ok(certs) => {
895 let certs = certs.into_iter()
896 .filter_map(|cert| {
897 match cert {
898 Err(err) => {
899 eprintln!("Parsing certificate from {}'s keyring: {}",
900 name, err);
901 None
902 }
903 Ok(cert) => Some(cert),
904 }
905 });
906
907 let mut map: BTreeMap<Fingerprint, Vec<RawCert>> = BTreeMap::new();
908 for cert in certs {
909 let entry = map.entry(cert.fingerprint());
910 match entry {
911 Entry::Occupied(mut oe) => {
912 let oe: &mut Vec<RawCert> = oe.get_mut();
913 oe.push(cert);
914 }
915 Entry::Vacant(ve) => {
916 ve.insert(vec![ cert ]);
917 }
918 }
919 }
920
921 map
922 }
923 }
924 }
925
926 let old = parse_keyring(&name, &self.keyring);
927 let new = parse_keyring(&name, &other.keyring);
928
929 let old_certs: BTreeSet<&Fingerprint> = old.keys().collect();
931 let new_certs: BTreeSet<&Fingerprint> = new.keys().collect();
932
933 for &removed in old_certs.difference(&new_certs) {
934 changes.push(Change::RemoveCert(name.clone(), removed.clone()));
935 }
936 for &added in new_certs.difference(&old_certs) {
937 changes.push(Change::AddCert(name.clone(), added.clone()));
938 }
939
940 for &fpr in old_certs.intersection(&new_certs) {
943 let old = old.get(fpr).expect("have it");
944 let new = new.get(fpr).expect("have it");
945
946 if old == new {
947 continue;
948 }
949
950 let into_packets = |certs: Vec<RawCert>| -> HashSet<(Packet, Signature)> {
953 let mut pairs: HashSet<(Packet, Signature)> = HashSet::new();
954
955 for cert in certs.into_iter() {
956 let mut packets = cert.packets();
957 let primary_key
958 = packets.next().expect("have a primary key");
959 let primary_key = match Packet::try_from(primary_key) {
960 Ok(p) => p,
961 Err(err) => {
962 eprintln!(
963 "Warning: {} has a corrupted primary key \
964 packet (skipped): {}",
965 cert.fingerprint(), err);
966 continue;
967 }
968 };
969
970 packets.fold(
971 primary_key,
972 |component, packet| {
973 let packet = match Packet::try_from(packet) {
974 Ok(p) => p,
975 Err(err) => {
976 eprintln!(
977 "Warning: {} has a corrupted packet \
978 (skipped): {}",
979 cert.fingerprint(), err);
980 return component;
981 }
982 };
983
984 match packet {
985 Packet::Signature(sig) => {
986 pairs.insert(
987 (component.clone(), sig.clone()));
988 component
989 }
990 Packet::Marker(_) => {
991 component
993 }
994 _ => {
995 packet.clone()
997 }
998 }
999 });
1000 }
1001
1002 pairs
1003 };
1004
1005 let old_packets = into_packets(old.clone());
1006 let new_packets = into_packets(new.clone());
1007
1008 for (component, sig) in old_packets.difference(&new_packets) {
1009 changes.push(Change::RemovePacket(name.clone(),
1010 fpr.clone(),
1011 component.clone(),
1012 sig.clone()));
1013 }
1014 for (component, sig) in new_packets.difference(&old_packets) {
1015 changes.push(Change::AddPacket(name.clone(),
1016 fpr.clone(),
1017 component.clone(),
1018 sig.clone()));
1019 }
1020 }
1021 }
1022 }
1023}
1024
1025static DIFF_JSON_VERSION: &'static str = "1.0.0";
1028
1029#[derive(Serialize)]
1031pub struct Diff<'f, 't> {
1032 version: &'static str,
1033
1034 pub from: &'f Policy,
1035 pub changes: Vec<Change>,
1036 pub to: &'t Policy,
1037}
1038
1039impl Diff<'_, '_> {
1040 fn assert(&self, r: &Rights) -> Result<()> {
1041 for c in &self.changes {
1042 c.assert(r)?;
1043 }
1044 Ok(())
1045 }
1046}
1047
1048use crate::utils::{serialize_fp, serialize_oid};
1049
1050#[derive(Clone, Serialize)]
1051pub enum Change {
1052 VersionChange {
1053 from: usize,
1054 to: usize,
1055 },
1056 GoodlistCommit(
1057 #[serde(serialize_with = "serialize_oid")] Oid),
1058 UngoodlistCommit(
1059 #[serde(serialize_with = "serialize_oid")] Oid),
1060
1061 AddUser(String),
1062 RetireUser(String),
1063
1064 AddRight(String, Right),
1065 RemoveRight(String, Right),
1066
1067 AddCert(String,
1068 #[serde(serialize_with = "serialize_fp")] Fingerprint),
1069 RemoveCert(String,
1070 #[serde(serialize_with = "serialize_fp")] Fingerprint),
1071
1072 AddPacket(String,
1073 #[serde(serialize_with = "serialize_fp")] Fingerprint,
1075 #[serde(serialize_with = "serialize_packet")] Packet,
1077 #[serde(serialize_with = "serialize_signature")] Signature),
1079 RemovePacket(String,
1080 #[serde(serialize_with = "serialize_fp")] Fingerprint,
1082 #[serde(serialize_with = "serialize_packet")] Packet,
1084 #[serde(serialize_with = "serialize_signature")] Signature),
1086}
1087
1088impl Change {
1089 fn assert(&self, r: &Rights) -> Result<()> {
1090 use Change::*;
1091 match self {
1092 VersionChange { .. } => r.assert(Right::Audit),
1093 GoodlistCommit(_) => r.assert(Right::Audit),
1094 UngoodlistCommit(_) => r.assert(Right::Audit),
1095
1096 AddUser(_) => r.assert(Right::AddUser),
1098 RetireUser(_) => r.assert(Right::RetireUser),
1099 AddRight(_, right) =>
1100 r.assert(Right::AddUser).and_then(|_| r.assert(*right)),
1101 RemoveRight(_, right) =>
1102 r.assert(Right::RetireUser).and_then(|_| r.assert(*right)),
1103
1104 AddCert(_, _) => r.assert(Right::AddUser),
1106 RemoveCert(_, _) => r.assert(Right::RetireUser),
1107
1108 AddPacket(_, _, _, _) => Ok(()),
1110
1111 RemovePacket(_, fpr, _component, sig) => {
1113 if sig.get_issuers().into_iter()
1114 .any(|kh| kh.aliases(KeyHandle::from(fpr)))
1115 {
1116 r.assert(Right::RetireUser)
1119 } else {
1120 Ok(())
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127#[derive(Debug)]
1128pub struct Rights(BTreeSet<Right>);
1129
1130impl Rights {
1131 fn assert(&self, r: Right) -> Result<()> {
1132 if ! self.0.contains(&r) {
1133 Err(Error::Unauthorized(format!("Right {} is missing", r)))
1134 } else {
1135 Ok(())
1136 }
1137 }
1138}
1139
1140#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)]
1141pub enum Right {
1142 SignCommit,
1143 SignTag,
1144 SignArchive,
1145 AddUser,
1146 RetireUser,
1147 Audit,
1148}
1149
1150impl fmt::Display for Right {
1151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152 use Right::*;
1153 match self {
1154 SignCommit => f.write_str("sign-commit"),
1155 SignTag => f.write_str("sign-tag"),
1156 SignArchive => f.write_str("sign-archive"),
1157 AddUser => f.write_str("add-user"),
1158 RetireUser => f.write_str("retire-user"),
1159 Audit => f.write_str("audit"),
1160 }
1161 }
1162}