1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
//! Shared OpenPGP Certificate Directory
//!
//! This crate implements a generic [OpenPGP certificate store] that can
//! be shared between implementations. It also defines a way to root
//! trust, and a way to associate pet names with certificates.
//! Sharing certificates and trust decisions increases security by
//! enabling more applications to take advantage of OpenPGP. It also
//! improves privacy by reducing the required certificate discoveries
//! that go out to the network.
//!
//! Note that this crate is only concerned with the low-level
//! mechanics of the certificate directory and does not depend on an
//! OpenPGP implementation. This is the reason it returns opaque
//! [`Data`] when retrieving certificates. Generally, it has to be
//! combined with an OpenPGP implementation to be useful.
//!
//! [OpenPGP certificate store]: https://datatracker.ietf.org/doc/draft-nwjw-openpgp-cert-d/
use std::time::{Duration, UNIX_EPOCH};
#[macro_use]
mod macros;
mod certd;
pub use certd::CertD;
mod error;
pub use error::*;
mod pgp;
/// Special name of the trust root.
///
/// This is the [special name] under which the [trust root] is stored.
///
/// [special name]: https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.html#name-special-names
/// [trust root]: https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.html#name-trust-root
pub const TRUST_ROOT: &str = "trust-root";
// SPECIAL_NAMES must be sorted. This allows using
// SPECIAL_NAMES.binary_search(needle).is_ok() (O(log(n))) instead of
// SPECIAL_NAMES.contains(needle) (O(n))).
const SPECIAL_NAMES: &[&str] = &[TRUST_ROOT];
/// Opaque data returned by this crate.
pub type Data = Box<[u8]>;
/// Facilitates caching of derived data.
///
/// Every time you look up a cert in the directory, the operation also
/// returns a tag. This tag, which can be converted to a `u64`, is an
/// opaque identifier that changes whenever the cert in the directory
/// changes.
///
/// To use it, store the tag with your cached data, and every time you
/// re-do the lookup, compare the returned tag with the stored tag to
/// see if your cached data is still up-to-date.
///
/// # Examples
///
/// This demonstrates how to use the tag to prevent useless
/// recomputations.
///
/// ```
/// # use openpgp_cert_d::*;
/// # fn dosth(certd: &CertD) -> Result<()> {
/// let fp = "eb85bb5fa33a75e15e944e63f231550c4f47e38e";
/// let (tag, cert) = certd.get(fp)?.expect("cert to exist");
/// // ...
/// if let Some((new_cert, new_tag)) = certd.get_if_changed(tag, fp)? {
/// // cert changed...
/// } else {
/// // cert didn't change...
/// }
/// # Ok(()) }
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Tag(u64);
impl std::convert::TryFrom<std::fs::Metadata> for Tag {
type Error = std::io::Error;
/// Compute a `Tag` from file metadata.
fn try_from(m: std::fs::Metadata) -> std::io::Result<Self> {
let d = m
.modified()?
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| Duration::new(0, 0));
let inode = platform! {
unix => {
use std::os::unix::fs::MetadataExt;
m.ino()
},
windows => 0,
};
// XXX better hashing
Ok(Tag(inode ^ d.as_secs() ^ d.subsec_nanos() as u64))
}
}
impl From<u64> for Tag {
fn from(t: u64) -> Self {
Tag(t)
}
}
impl From<Tag> for u64 {
fn from(t: Tag) -> Self {
t.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn special_names_is_sorted() {
let mut sn = SPECIAL_NAMES.to_vec();
sn.sort_unstable();
assert_eq!(sn, SPECIAL_NAMES);
}
}