diff options
author | Wiktor Kwapisiewicz <wiktor@metacode.biz> | 2021-12-29 15:32:08 +0100 |
---|---|---|
committer | Wiktor Kwapisiewicz <wiktor@metacode.biz> | 2021-12-31 11:07:31 +0100 |
commit | 01a1221255fee361136fcc85b99c75c113d6c916 (patch) | |
tree | b288419ccb6ae35ed5e2d8235008dcbb2ff14a23 /net | |
parent | de8fab8d1b74fa87d3c20d7a2b9e49aea929e6ea (diff) |
pks: Return correct acceptable hashes for the remote signer.
Diffstat (limited to 'net')
-rw-r--r-- | net/src/pks.rs | 89 |
1 files changed, 68 insertions, 21 deletions
diff --git a/net/src/pks.rs b/net/src/pks.rs index f0ce311a..271400f6 100644 --- a/net/src/pks.rs +++ b/net/src/pks.rs @@ -17,24 +17,62 @@ //! } //! ``` +use std::convert::{TryFrom, TryInto}; + use sequoia_openpgp as openpgp; use openpgp::packet::Key; use openpgp::packet::key::{PublicParts, UnspecifiedRole}; use openpgp::crypto::{Password, Decryptor, Signer, mpi, SessionKey, ecdh}; +use openpgp::types::HashAlgorithm; -use hyper::{Body, Client, Uri, client::HttpConnector, Request}; +use hyper::{Body, Client, Uri, client::HttpConnector, Request, HeaderMap, header::HeaderValue}; use hyper_tls::HttpsConnector; use super::Result; use url::Url; -/// Returns a capability URL for given key's capability. +/// Contains a description of the unlocked key. +struct KeyDescriptor { + /// URL of the endpoint that can be used to interact with the key. + url: Uri, + /// List of content types that this key accepts. This value is `None` if the key + /// did not indicate accepted content types. + accepted_types: Option<Vec<String>>, +} + +impl TryFrom<&HeaderMap<HeaderValue>> for KeyDescriptor { + type Error = anyhow::Error; + + fn try_from(headers: &HeaderMap<HeaderValue>) -> Result<Self> { + if let Some(location) = headers.get("Location") { + let accepted_types = if let Some(accepted_types) = headers.get("Accept-Post") { + Some( + accepted_types + .to_str()? + .split(',') + .map(|typ| typ.trim().to_string()) + .collect::<Vec<_>>(), + ) + } else { + None + }; + Ok(Self { + url: location.to_str()?.parse()?, + accepted_types, + }) + } else { + Err(anyhow::anyhow!("Key unlock did not return a Location header.")) + } + } +} + +/// Returns an unlocked key descriptor. /// -/// Unlocks a key using given password and on success returns a capability -/// URL that can be used for signing or decryption. -fn create_uri(store_uri: &str, key: &Key<PublicParts, UnspecifiedRole>, - p: &Password, capability: &str) -> Result<Uri> { +/// Unlocks a key using given password and on success returns a key descriptor +/// that can be used for signing or decryption. +fn create_descriptor(store_uri: &str, key: &Key<PublicParts, UnspecifiedRole>, + p: &Password, capability: &str) -> Result<KeyDescriptor> { let mut url = Url::parse(store_uri)?; let auth = if !url.username().is_empty() { let credentials = format!("{}:{}", url.username(), url.password().unwrap_or_default()); @@ -65,14 +103,10 @@ fn create_uri(store_uri: &str, key: &Key<PublicParts, UnspecifiedRole>, let response = rt.block_on(client.request(request))?; if !response.status().is_success() { - return Err(anyhow::anyhow!("PKS Key unlock failed.")); + return Err(anyhow::anyhow!("PKS Key unlock failed: {}", response.status())); } - if let Some(location) = response.headers().get("Location") { - Ok(location.to_str()?.parse()?) - } else { - Err(anyhow::anyhow!("Key unlock did not return a Location header.")) - } + response.headers().try_into() } /// Unlock a remote key for signing. @@ -100,8 +134,8 @@ fn create_uri(store_uri: &str, key: &Key<PublicParts, UnspecifiedRole>, /// ``` pub fn unlock_signer(store_uri: impl AsRef<str>, key: Key<PublicParts, UnspecifiedRole>, p: &Password) -> Result<Box<dyn Signer + Send + Sync>> { - let capability = create_uri(store_uri.as_ref(), &key, p, "sign")?; - Ok(Box::new(PksClient::new(key, capability)?)) + let description = create_descriptor(store_uri.as_ref(), &key, p, "sign")?; + Ok(Box::new(PksClient::new(key, description)?)) } /// Unlock a remote key for decryption. @@ -129,21 +163,22 @@ pub fn unlock_signer(store_uri: impl AsRef<str>, key: Key<PublicParts, Unspecifi /// ``` pub fn unlock_decryptor(store_uri: impl AsRef<str>, key: Key<PublicParts, UnspecifiedRole>, p: &Password) -> Result<Box<dyn Decryptor + Send + Sync>> { - let capability = create_uri(store_uri.as_ref(), &key, p, "decrypt")?; - Ok(Box::new(PksClient::new(key, capability)?)) + let description = create_descriptor(store_uri.as_ref(), &key, p, "decrypt")?; + Ok(Box::new(PksClient::new(key, description)?)) } struct PksClient { location: Uri, public: Key<PublicParts, UnspecifiedRole>, client: hyper::client::Client<HttpsConnector<HttpConnector>>, + acceptable_hashes: Vec<HashAlgorithm>, rt: tokio::runtime::Runtime, } impl PksClient { fn new( public: Key<PublicParts, UnspecifiedRole>, - location: Uri, + description: KeyDescriptor, ) -> Result<Self> { let client = Client::builder().build(HttpsConnector::new()); @@ -152,7 +187,17 @@ impl PksClient { .enable_time() .build()?; - Ok(Self { location, public, client, rt }) + let acceptable_types = description.accepted_types.unwrap_or_default(); + let mut acceptable_hashes = acceptable_types.iter().flat_map(|typ| match typ.as_ref() { + "application/vnd.pks.digest.sha1" => Some(HashAlgorithm::SHA1), + "application/vnd.pks.digest.sha256" => Some(HashAlgorithm::SHA256), + "application/vnd.pks.digest.sha384" => Some(HashAlgorithm::SHA384), + "application/vnd.pks.digest.sha512" => Some(HashAlgorithm::SHA512), + _ => None, + }).collect::<Vec<_>>(); + acceptable_hashes.sort(); + + Ok(Self { location: description.url, public, client, rt, acceptable_hashes }) } fn make_request(&mut self, body: Vec<u8>, content_type: &str) -> Result<Vec<u8>> { @@ -167,8 +212,7 @@ impl PksClient { return Err(anyhow::anyhow!("PKS operation failed: {}", response.status())); } - let response = self.rt.block_on(hyper::body::to_bytes(response))?.to_vec(); - Ok(response) + Ok(self.rt.block_on(hyper::body::to_bytes(response))?.to_vec()) } } @@ -206,13 +250,16 @@ impl Signer for PksClient { &self.public } + fn acceptable_hashes(&self) -> &[HashAlgorithm] { + &self.acceptable_hashes + } + fn sign( &mut self, hash_algo: openpgp::types::HashAlgorithm, digest: &[u8], ) -> openpgp::Result<openpgp::crypto::mpi::Signature> { use openpgp::types::PublicKeyAlgorithm; - use openpgp::types::HashAlgorithm; let content_type = match hash_algo { HashAlgorithm::SHA1 => "application/vnd.pks.digest.sha1", |