From 12b0c76b4fef2442eaead7759d48b209a57d5eef Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Fri, 15 Jan 2021 11:49:30 +0100 Subject: sq: Move networking services to their own file. --- sq/src/commands/mod.rs | 1 + sq/src/commands/net.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++++ sq/src/sq.rs | 131 ++------------------------------------ 3 files changed, 173 insertions(+), 127 deletions(-) create mode 100644 sq/src/commands/net.rs diff --git a/sq/src/commands/mod.rs b/sq/src/commands/mod.rs index 96246ec7..cd40d255 100644 --- a/sq/src/commands/mod.rs +++ b/sq/src/commands/mod.rs @@ -42,6 +42,7 @@ pub mod key; pub mod merge_signatures; pub use self::merge_signatures::merge_signatures; pub mod certring; +pub mod net; /// Returns suitable signing keys from a given list of Certs. fn get_signing_keys(certs: &[openpgp::Cert], p: &dyn Policy, diff --git a/sq/src/commands/net.rs b/sq/src/commands/net.rs new file mode 100644 index 00000000..c531f69e --- /dev/null +++ b/sq/src/commands/net.rs @@ -0,0 +1,168 @@ +//! Network services. + +use anyhow::Context; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Result, + KeyHandle, + KeyID, + Fingerprint, + armor, + cert::{ + Cert, + CertParser, + }, + packet::{ + UserID, + }, + parse::Parse, + serialize::Serialize, +}; +use sequoia_net as net; +use net::{ + KeyServer, + wkd, +}; + +use crate::{ + Config, + open_or_stdin, + create_or_stdout, + create_or_stdout_pgp, + serialize_keyring, +}; + +pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> { + let mut ks = if let Some(uri) = m.value_of("server") { + KeyServer::new(config.network_policy, &uri) + } else { + KeyServer::keys_openpgp_org(config.network_policy) + }.context("Malformed keyserver URI")?; + + let mut rt = tokio::runtime::Builder::new() + .basic_scheduler() + .enable_io() + .enable_time() + .build()?; + + match m.subcommand() { + ("get", Some(m)) => { + let query = m.value_of("query").unwrap(); + + let handle: Option = { + let q_fp = query.parse::(); + let q_id = query.parse::(); + if let Ok(Fingerprint::V4(_)) = q_fp { + q_fp.ok().map(Into::into) + } else if let Ok(KeyID::V4(_)) = q_id { + q_fp.ok().map(Into::into) + } else { + None + } + }; + + if let Some(handle) = handle { + let cert = rt.block_on(ks.get(handle)) + .context("Failed to retrieve cert")?; + + let mut output = + create_or_stdout(m.value_of("output"), config.force)?; + if ! m.is_present("binary") { + cert.armored().serialize(&mut output) + } else { + cert.serialize(&mut output) + }.context("Failed to serialize cert")?; + } else if let Ok(Some(addr)) = UserID::from(query).email() { + let certs = rt.block_on(ks.search(addr)) + .context("Failed to retrieve certs")?; + + let mut output = + create_or_stdout_pgp(m.value_of("output"), config.force, + m.is_present("binary"), + armor::Kind::PublicKey)?; + for cert in certs { + cert.serialize(&mut output) + .context("Failed to serialize cert")?; + } + output.finalize()?; + } else { + Err(anyhow::anyhow!( + "Query must be a fingerprint, a keyid, \ + or an email address: {:?}", query))?; + } + }, + ("send", Some(m)) => { + let mut input = open_or_stdin(m.value_of("input"))?; + let cert = Cert::from_reader(&mut input). + context("Malformed key")?; + + rt.block_on(ks.send(&cert)) + .context("Failed to send key to server")?; + }, + _ => unreachable!(), + } + + Ok(()) +} + +pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> { + let mut rt = tokio::runtime::Builder::new() + .basic_scheduler() + .enable_io() + .enable_time() + .build()?; + + match m.subcommand() { + ("url", Some(m)) => { + let email_address = m.value_of("input").unwrap(); + let wkd_url = wkd::Url::from(email_address)?; + // XXX: Add other subcomand to specify whether it should be + // created with the advanced or the direct method. + let url = wkd_url.to_url(None)?; + println!("{}", url); + }, + ("get", Some(m)) => { + let email_address = m.value_of("input").unwrap(); + // XXX: EmailAddress could be created here to + // check it's a valid email address, print the error to + // stderr and exit. + // Because it might be created a WkdServer struct, not + // doing it for now. + let certs = rt.block_on(wkd::get(&email_address))?; + // ```text + // The HTTP GET method MUST return the binary representation of the + // OpenPGP key for the given mail address. + // [draft-koch]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-07 + // ``` + // But to keep the parallelism with `store export` and `keyserver get`, + // The output is armored if not `--binary` option is given. + let mut output = create_or_stdout(m.value_of("output"), + config.force)?; + serialize_keyring(&mut output, &certs, + m.is_present("binary"))?; + }, + ("generate", Some(m)) => { + let domain = m.value_of("domain").unwrap(); + let f = open_or_stdin(m.value_of("input"))?; + let base_path = + m.value_of("base_directory").expect("required"); + let variant = if m.is_present("direct_method") { + wkd::Variant::Direct + } else { + wkd::Variant::Advanced + }; + let parser = CertParser::from_reader(f)?; + let certs: Vec = parser.filter_map(|cert| cert.ok()) + .collect(); + for cert in certs { + wkd::insert(&base_path, domain, variant, &cert) + .context(format!("Failed to generate the WKD in \ + {}.", base_path))?; + } + }, + _ => unreachable!(), + } + + Ok(()) +} diff --git a/sq/src/sq.rs b/sq/src/sq.rs index 3a97bee0..39c30527 100644 --- a/sq/src/sq.rs +++ b/sq/src/sq.rs @@ -9,14 +9,9 @@ use chrono::{DateTime, offset::Utc}; use buffered_reader::File; use sequoia_openpgp as openpgp; -use sequoia_net; use openpgp::{ Result, - Fingerprint, - KeyID, - KeyHandle, - packet::UserID, }; use crate::openpgp::{armor, Cert}; use sequoia_autocrypt as autocrypt; @@ -29,7 +24,6 @@ use crate::openpgp::serialize::{Serialize, stream::{Message, Armorer}}; use crate::openpgp::cert::prelude::*; use crate::openpgp::policy::StandardPolicy as P; use sequoia_net as net; -use sequoia_net::{KeyServer, wkd}; mod sq_cli; mod commands; @@ -269,13 +263,6 @@ fn main() -> Result<()> { network_policy, }; - - let mut rt = tokio::runtime::Builder::new() - .basic_scheduler() - .enable_io() - .enable_time() - .build()?; - match matches.subcommand() { ("decrypt", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; @@ -500,126 +487,16 @@ fn main() -> Result<()> { _ => unreachable!(), }, - ("keyserver", Some(m)) => { - let mut ks = if let Some(uri) = m.value_of("server") { - KeyServer::new(network_policy, &uri) - } else { - KeyServer::keys_openpgp_org(network_policy) - }.context("Malformed keyserver URI")?; - - match m.subcommand() { - ("get", Some(m)) => { - let query = m.value_of("query").unwrap(); - - let handle: Option = { - let q_fp = query.parse::(); - let q_id = query.parse::(); - if let Ok(Fingerprint::V4(_)) = q_fp { - q_fp.ok().map(Into::into) - } else if let Ok(KeyID::V4(_)) = q_id { - q_fp.ok().map(Into::into) - } else { - None - } - }; - - if let Some(handle) = handle { - let cert = rt.block_on(ks.get(handle)) - .context("Failed to retrieve cert")?; - - let mut output = - create_or_stdout(m.value_of("output"), force)?; - if ! m.is_present("binary") { - cert.armored().serialize(&mut output) - } else { - cert.serialize(&mut output) - }.context("Failed to serialize cert")?; - } else if let Ok(Some(addr)) = UserID::from(query).email() { - let certs = rt.block_on(ks.search(addr)) - .context("Failed to retrieve certs")?; - - let mut output = - create_or_stdout_pgp(m.value_of("output"), force, - m.is_present("binary"), - armor::Kind::PublicKey)?; - for cert in certs { - cert.serialize(&mut output) - .context("Failed to serialize cert")?; - } - output.finalize()?; - } else { - Err(anyhow::anyhow!( - "Query must be a fingerprint, a keyid, \ - or an email address: {:?}", query))?; - } - }, - ("send", Some(m)) => { - let mut input = open_or_stdin(m.value_of("input"))?; - let cert = Cert::from_reader(&mut input). - context("Malformed key")?; + ("keyserver", Some(m)) => + commands::net::dispatch_keyserver(config, m)?, - rt.block_on(ks.send(&cert)) - .context("Failed to send key to server")?; - }, - _ => unreachable!(), - } - }, ("key", Some(m)) => match m.subcommand() { ("generate", Some(m)) => commands::key::generate(m, force)?, ("adopt", Some(m)) => commands::key::adopt(m, policy)?, _ => unreachable!(), }, - ("wkd", Some(m)) => { - match m.subcommand() { - ("url", Some(m)) => { - let email_address = m.value_of("input").unwrap(); - let wkd_url = wkd::Url::from(email_address)?; - // XXX: Add other subcomand to specify whether it should be - // created with the advanced or the direct method. - let url = wkd_url.to_url(None)?; - println!("{}", url); - }, - ("get", Some(m)) => { - let email_address = m.value_of("input").unwrap(); - // XXX: EmailAddress could be created here to - // check it's a valid email address, print the error to - // stderr and exit. - // Because it might be created a WkdServer struct, not - // doing it for now. - let certs = rt.block_on(wkd::get(&email_address))?; - // ```text - // The HTTP GET method MUST return the binary representation of the - // OpenPGP key for the given mail address. - // [draft-koch]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-07 - // ``` - // But to keep the parallelism with `store export` and `keyserver get`, - // The output is armored if not `--binary` option is given. - let mut output = create_or_stdout(m.value_of("output"), force)?; - serialize_keyring(&mut output, &certs, - m.is_present("binary"))?; - }, - ("generate", Some(m)) => { - let domain = m.value_of("domain").unwrap(); - let f = open_or_stdin(m.value_of("input"))?; - let base_path = - m.value_of("base_directory").expect("required"); - let variant = if m.is_present("direct_method") { - wkd::Variant::Direct - } else { - wkd::Variant::Advanced - }; - let parser = CertParser::from_reader(f)?; - let certs: Vec = parser.filter_map(|cert| cert.ok()) - .collect(); - for cert in certs { - wkd::insert(&base_path, domain, variant, &cert) - .context(format!("Failed to generate the WKD in \ - {}.", base_path))?; - } - }, - _ => unreachable!(), - } - }, + + ("wkd", Some(m)) => commands::net::dispatch_wkd(config, m)?, _ => unreachable!(), } -- cgit v1.2.3