diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2021-01-15 09:35:04 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2021-01-15 11:34:49 +0100 |
commit | 59a59ac5bf9cc9be2698eb35376c3b58fd483252 (patch) | |
tree | 5eb5c8256c67b8397669c48fae905840c3302866 /net | |
parent | de5c18230ddcad3928ae5c4bd2c1badbe9ec2e92 (diff) |
net: Decouple from core.
- Move core::NetworkPolicy to net::Policy, update all code
accordingly.
Diffstat (limited to 'net')
-rw-r--r-- | net/Cargo.toml | 1 | ||||
-rw-r--r-- | net/src/lib.rs | 166 | ||||
-rw-r--r-- | net/tests/hkp.rs | 18 |
3 files changed, 144 insertions, 41 deletions
diff --git a/net/Cargo.toml b/net/Cargo.toml index 307a65f0..cff91a70 100644 --- a/net/Cargo.toml +++ b/net/Cargo.toml @@ -22,7 +22,6 @@ maintenance = { status = "actively-developed" } [dependencies] sequoia-openpgp = { path = "../openpgp", version = "1.0.0", default-features = false } -sequoia-core = { path = "../core", version = "0.22" } anyhow = "1.0.18" futures-util = "0.3.5" diff --git a/net/src/lib.rs b/net/src/lib.rs index fa5b1659..dda95da8 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -15,11 +15,9 @@ //! //! ```no_run //! # use sequoia_openpgp::KeyID; -//! # use sequoia_core::Context; -//! # use sequoia_net::{KeyServer, Result}; +//! # use sequoia_net::{KeyServer, Policy, Result}; //! # async fn f() -> Result<()> { -//! let ctx = Context::new()?; -//! let mut ks = KeyServer::keys_openpgp_org(&ctx)?; +//! let mut ks = KeyServer::keys_openpgp_org(Policy::Encrypted)?; //! let keyid: KeyID = "31855247603831FD".parse()?; //! println!("{:?}", ks.get(keyid).await?); //! # Ok(()) @@ -36,6 +34,7 @@ use native_tls::{Certificate, TlsConnector}; use percent_encoding::{percent_encode, AsciiSet, CONTROLS}; use std::convert::From; +use std::fmt; use std::io::Cursor; use url::Url; @@ -48,7 +47,6 @@ use openpgp::{ parse::Parse, serialize::Serialize, }; -use sequoia_core::{Context, NetworkPolicy}; pub mod wkd; @@ -61,6 +59,75 @@ const KEYSERVER_ENCODE_SET: &AsciiSet = // respect to the encoding. .add(b'-').add(b'+').add(b'/'); +/// Network policy for Sequoia. +/// +/// With this policy you can control how Sequoia accesses remote +/// systems. +#[derive(PartialEq, PartialOrd, Debug, Copy, Clone)] +pub enum Policy { + /// Do not contact remote systems. + Offline, + + /// Only contact remote systems using anonymization techniques + /// like TOR. + Anonymized, + + /// Only contact remote systems using transports offering + /// encryption and authentication like TLS. + Encrypted, + + /// Contact remote systems even with insecure transports. + Insecure, +} + +impl fmt::Display for Policy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", match self { + &Policy::Offline => "Offline", + &Policy::Anonymized => "Anonymized", + &Policy::Encrypted => "Encrypted", + &Policy::Insecure => "Insecure", + }) + } +} + +impl Policy { + /// Asserts that this policy allows an action requiring policy + /// `action`. + pub fn assert(&self, action: Policy) -> Result<()> { + if action > *self { + Err(Error::PolicyViolation(action).into()) + } else { + Ok(()) + } + } +} + +impl<'a> From<&'a Policy> for u8 { + fn from(policy: &Policy) -> Self { + match policy { + &Policy::Offline => 0, + &Policy::Anonymized => 1, + &Policy::Encrypted => 2, + &Policy::Insecure => 3, + } + } +} + + +// XXX: TryFrom would be nice. +impl From<u8> for Policy { + fn from(policy: u8) -> Self { + match policy { + 0 => Policy::Offline, + 1 => Policy::Anonymized, + 2 => Policy::Encrypted, + 3 => Policy::Insecure, + n => panic!("Bad network policy: {}", n), + } + } +} + /// For accessing keyservers using HKP. pub struct KeyServer { client: Box<dyn AClient>, @@ -69,7 +136,7 @@ pub struct KeyServer { impl KeyServer { /// Returns a handle for the given URI. - pub fn new(ctx: &Context, uri: &str) -> Result<Self> { + pub fn new(p: Policy, uri: &str) -> Result<Self> { let uri: Url = uri.parse() .or_else(|_| format!("hkps://{}", uri).parse())?; @@ -82,13 +149,13 @@ impl KeyServer { _ => return Err(Error::MalformedUri.into()), }; - Self::make(ctx, client, uri) + Self::make(p, client, uri) } /// Returns a handle for the given URI. /// /// `cert` is used to authenticate the server. - pub fn with_cert(ctx: &Context, uri: &str, cert: Certificate) + pub fn with_cert(p: Policy, uri: &str, cert: Certificate) -> Result<Self> { let uri: Url = uri.parse()?; @@ -103,23 +170,23 @@ impl KeyServer { .build(HttpsConnector::from((http, tls.into())))) }; - Self::make(ctx, client, uri) + Self::make(p, client, uri) } /// Returns a handle for keys.openpgp.org. /// /// The server at `hkps://keys.openpgp.org` distributes updates /// for OpenPGP certificates. It is a good default choice. - pub fn keys_openpgp_org(ctx: &Context) -> Result<Self> { - Self::new(ctx, "hkps://keys.openpgp.org") + pub fn keys_openpgp_org(p: Policy) -> Result<Self> { + Self::new(p, "hkps://keys.openpgp.org") } /// Common code for the above functions. - fn make(ctx: &Context, client: Box<dyn AClient>, uri: Url) -> Result<Self> { + fn make(p: Policy, client: Box<dyn AClient>, uri: Url) -> Result<Self> { let s = uri.scheme(); match s { - "hkp" => ctx.network_policy().assert(NetworkPolicy::Insecure), - "hkps" => ctx.network_policy().assert(NetworkPolicy::Encrypted), + "hkp" => p.assert(Policy::Insecure), + "hkps" => p.assert(Policy::Encrypted), _ => return Err(Error::MalformedUri.into()) }?; let uri = @@ -301,6 +368,10 @@ pub type Result<T> = ::std::result::Result<T, anyhow::Error>; #[derive(thiserror::Error, Debug)] /// Errors returned from the network routines. pub enum Error { + /// The network policy was violated by the given action. + #[error("Unmet network policy requirement: {0}")] + PolicyViolation(Policy), + /// A requested key was not found. #[error("Key not found")] NotFound, @@ -346,22 +417,63 @@ pub enum Error { mod tests { use super::*; + fn ok(policy: Policy, required: Policy) { + assert!(policy.assert(required).is_ok()); + } + + fn fail(policy: Policy, required: Policy) { + assert!(matches!( + policy.assert(required) + .err().unwrap().downcast::<Error>().unwrap(), + Error::PolicyViolation(_))); + } + #[test] - fn uris() { - let ctx = Context::configure() - .network_policy(sequoia_core::NetworkPolicy::Insecure) - .build().unwrap(); + fn offline() { + let p = Policy::Offline; + ok(p, Policy::Offline); + fail(p, Policy::Anonymized); + fail(p, Policy::Encrypted); + fail(p, Policy::Insecure); + } - assert!(KeyServer::new(&ctx, "keys.openpgp.org").is_ok()); - assert!(KeyServer::new(&ctx, "hkp://keys.openpgp.org").is_ok()); - assert!(KeyServer::new(&ctx, "hkps://keys.openpgp.org").is_ok()); + #[test] + fn anonymized() { + let p = Policy::Anonymized; + ok(p, Policy::Offline); + ok(p, Policy::Anonymized); + fail(p, Policy::Encrypted); + fail(p, Policy::Insecure); + } + + #[test] + fn encrypted() { + let p = Policy::Encrypted; + ok(p, Policy::Offline); + ok(p, Policy::Anonymized); + ok(p, Policy::Encrypted); + fail(p, Policy::Insecure); + } - let ctx = Context::configure() - .network_policy(sequoia_core::NetworkPolicy::Encrypted) - .build().unwrap(); + #[test] + fn insecure() { + let p = Policy::Insecure; + ok(p, Policy::Offline); + ok(p, Policy::Anonymized); + ok(p, Policy::Encrypted); + ok(p, Policy::Insecure); + } - assert!(KeyServer::new(&ctx, "keys.openpgp.org").is_ok()); - assert!(KeyServer::new(&ctx, "hkp://keys.openpgp.org").is_err()); - assert!(KeyServer::new(&ctx, "hkps://keys.openpgp.org").is_ok()); + #[test] + fn uris() { + let p = Policy::Insecure; + assert!(KeyServer::new(p, "keys.openpgp.org").is_ok()); + assert!(KeyServer::new(p, "hkp://keys.openpgp.org").is_ok()); + assert!(KeyServer::new(p, "hkps://keys.openpgp.org").is_ok()); + + let p = Policy::Encrypted; + assert!(KeyServer::new(p, "keys.openpgp.org").is_ok()); + assert!(KeyServer::new(p, "hkp://keys.openpgp.org").is_err()); + assert!(KeyServer::new(p, "hkps://keys.openpgp.org").is_ok()); } } diff --git a/net/tests/hkp.rs b/net/tests/hkp.rs index 8dafe9a6..f1984529 100644 --- a/net/tests/hkp.rs +++ b/net/tests/hkp.rs @@ -11,7 +11,7 @@ use sequoia_openpgp::KeyID; use sequoia_openpgp::armor::Reader; use sequoia_openpgp::Cert; use sequoia_openpgp::parse::Parse; -use sequoia_core::{Context, NetworkPolicy}; +use sequoia_net as net; use sequoia_net::KeyServer; const RESPONSE: &'static str = "-----BEGIN PGP PUBLIC KEY BLOCK----- @@ -118,17 +118,14 @@ fn start_server() -> SocketAddr { addr } +const P: net::Policy = net::Policy::Insecure; + #[tokio::test] async fn get() -> anyhow::Result<()> { - let ctx = Context::configure() - .ephemeral() - .network_policy(NetworkPolicy::Insecure) - .build()?; - // Start server. let addr = start_server(); - let mut keyserver = KeyServer::new(&ctx, &format!("hkp://{}", addr))?; + let mut keyserver = KeyServer::new(P, &format!("hkp://{}", addr))?; let keyid: KeyID = ID.parse()?; let key = keyserver.get(keyid).await?; @@ -139,16 +136,11 @@ async fn get() -> anyhow::Result<()> { #[tokio::test] async fn send() -> anyhow::Result<()> { - let ctx = Context::configure() - .ephemeral() - .network_policy(NetworkPolicy::Insecure) - .build()?; - // Start server. let addr = start_server(); eprintln!("{}", format!("hkp://{}", addr)); let mut keyserver = - KeyServer::new(&ctx, &format!("hkp://{}", addr))?; + KeyServer::new(P, &format!("hkp://{}", addr))?; let key = Cert::from_reader(Reader::new(Cursor::new(RESPONSE), None))?; keyserver.send(&key).await?; |