summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorWiktor Kwapisiewicz <wiktor@metacode.biz>2021-12-29 15:32:08 +0100
committerWiktor Kwapisiewicz <wiktor@metacode.biz>2021-12-31 11:07:31 +0100
commit01a1221255fee361136fcc85b99c75c113d6c916 (patch)
treeb288419ccb6ae35ed5e2d8235008dcbb2ff14a23 /net
parentde8fab8d1b74fa87d3c20d7a2b9e49aea929e6ea (diff)
pks: Return correct acceptable hashes for the remote signer.
Diffstat (limited to 'net')
-rw-r--r--net/src/pks.rs89
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",