summaryrefslogtreecommitdiffstats
path: root/openpgp/src/serialize/stream.rs
diff options
context:
space:
mode:
authorWiktor Kwapisiewicz <wiktor@metacode.biz>2021-12-29 15:31:25 +0100
committerWiktor Kwapisiewicz <wiktor@metacode.biz>2021-12-31 09:56:11 +0100
commitde8fab8d1b74fa87d3c20d7a2b9e49aea929e6ea (patch)
tree06265139ecc5295846bb507ddaecd90cfa586ebd /openpgp/src/serialize/stream.rs
parent0e23277b852edf1317df4aebaaabe5a404a6ad05 (diff)
openpgp: Add ability to restrict hash algorithms for signing.
Diffstat (limited to 'openpgp/src/serialize/stream.rs')
-rw-r--r--openpgp/src/serialize/stream.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs
index dfc49d0d..09abac17 100644
--- a/openpgp/src/serialize/stream.rs
+++ b/openpgp/src/serialize/stream.rs
@@ -1247,6 +1247,29 @@ impl<'a> Signer<'a> {
assert!(!self.signers.is_empty(), "The constructor adds a signer.");
assert!(self.inner.is_some(), "The constructor adds an inner writer.");
+ let mut acceptable_hashes = crate::crypto::hash::DEFAULT_HASHES.to_vec();
+
+ let is_sorted = |data: &[HashAlgorithm]| {
+ data.windows(2).all(|w| w[0] <= w[1])
+ };
+
+ for signer in &self.signers {
+ let mut signer_hashes = signer.acceptable_hashes();
+ let mut signer_hashes_;
+ if ! is_sorted(signer_hashes) {
+ signer_hashes_ = signer_hashes.to_vec();
+ signer_hashes_.sort();
+ signer_hashes = &signer_hashes_;
+ }
+ acceptable_hashes.retain(|hash| signer_hashes.binary_search(hash).is_ok());
+ }
+
+ if let Some(hash) = acceptable_hashes.first() {
+ self.hash = hash.context().unwrap();
+ } else {
+ return Err(Error::NoAcceptableHash.into());
+ }
+
match self.mode {
SignatureMode::Inline => {
// For every key we collected, build and emit a one pass
@@ -3662,4 +3685,88 @@ mod test {
Ok(())
}
+
+ struct BadSigner;
+
+ impl crypto::Signer for BadSigner {
+ fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
+ panic!("public not impl")
+ }
+
+ /// Returns a list of hashes that this signer accepts.
+ fn acceptable_hashes(&self) -> &[HashAlgorithm] {
+ &[]
+ }
+
+ fn sign(&mut self, _hash_algo: HashAlgorithm, _digest: &[u8])
+ -> Result<crypto::mpi::Signature> {
+ panic!("sign not impl")
+ }
+ }
+
+ struct GoodSigner(Vec<HashAlgorithm>, Key<key::PublicParts, key::UnspecifiedRole>);
+
+ impl crypto::Signer for GoodSigner {
+ fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
+ &self.1
+ }
+
+ /// Returns a list of hashes that this signer accepts.
+ fn acceptable_hashes(&self) -> &[HashAlgorithm] {
+ &self.0
+ }
+
+ fn sign(&mut self, _hash_algo: HashAlgorithm, _digest: &[u8])
+ -> Result<crypto::mpi::Signature> {
+ unimplemented!()
+ }
+ }
+
+ impl Default for GoodSigner {
+ fn default() -> Self {
+ let p = &P::new();
+
+ let (cert, _) = CertBuilder::new().generate().unwrap();
+
+ let ka = cert.keys().with_policy(p, None).next().unwrap();
+
+ Self(vec![HashAlgorithm::default()], ka.key().clone())
+ }
+ }
+
+ #[test]
+ fn overlapping_hashes() {
+ let mut signature = vec![];
+ let message = Message::new(&mut signature);
+
+ Signer::new(message, GoodSigner::default()).build().unwrap();
+ }
+
+ #[test]
+ fn no_overlapping_hashes() {
+ let mut signature = vec![];
+ let message = Message::new(&mut signature);
+ let signer = Signer::new(message, BadSigner);
+
+ if let Err(e) = signer.build() {
+ assert_eq!(e.downcast_ref::<Error>(), Some(&Error::NoAcceptableHash));
+ } else {
+ unreachable!();
+ };
+ }
+
+ #[test]
+ fn no_overlapping_hashes_for_new_signer() {
+ let mut signature = vec![];
+ let message = Message::new(&mut signature);
+
+ let mut signer = Signer::new(message, GoodSigner::default());
+ signer = signer.add_signer(BadSigner);
+
+ if let Err(e) = signer.build() {
+ assert_eq!(e.downcast_ref::<Error>(), Some(&Error::NoAcceptableHash));
+ } else {
+ unreachable!();
+ };
+ }
}