summaryrefslogtreecommitdiffstats
path: root/openpgp/src/parse.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-07-05 13:13:56 +0200
committerJustus Winter <justus@sequoia-pgp.org>2023-07-07 15:29:29 +0200
commit49914bbce890c2b9d2ae18905a306999103c28b6 (patch)
treee0de3b0984a81555de1e631cdc022eba41b3f062 /openpgp/src/parse.rs
parenta30a2cf128feb0ac8ee763c5b80d348900cca6b0 (diff)
openpgp: Explicitly and selectively enable hashing.
- When we opt out of automatic hashing, it is useful to selectively opt in to hashing on a per-one-pass-signature basis. Add PacketParser::start_hashing to do this. - This is somewhat similar to PacketParser::decrypt in that they are invoked while the packet is in the packet parser, and they communicate intent to act upon that packet. - Fixes #1034.
Diffstat (limited to 'openpgp/src/parse.rs')
-rw-r--r--openpgp/src/parse.rs108
1 files changed, 104 insertions, 4 deletions
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index c4193446..4df99c26 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -1954,7 +1954,7 @@ impl OnePassSig3 {
// Walk up the reader chain to see if there is already a
// hashed reader on level recursion_depth - 1.
- let done = ! php.state.settings.automatic_hashing || {
+ let done = {
let mut done = false;
let mut reader : Option<&mut dyn BufferedReader<Cookie>>
= Some(&mut php.reader);
@@ -1977,7 +1977,8 @@ impl OnePassSig3 {
// Make sure that it uses the required
// hash algorithm.
- if ! cookie.sig_group().hashes.iter()
+ if php.state.settings.automatic_hashing
+ && ! cookie.sig_group().hashes.iter()
.any(|mode| {
mode.map(|ctx| ctx.algo()) == need_hash
})
@@ -2015,9 +2016,11 @@ impl OnePassSig3 {
// We create an empty hashed reader even if we don't support
// the hash algorithm so that we have something to match
- // against when we get to the Signature packet.
+ // against when we get to the Signature packet. Or, automatic
+ // hashing may be disabled, and we want to be able to enable
+ // it explicitly.
let mut algos = Vec::new();
- if hash_algo.is_supported() {
+ if pp.state.settings.automatic_hashing && hash_algo.is_supported() {
algos.push(need_hash);
}
@@ -2064,6 +2067,103 @@ impl OnePassSig3 {
}
}
+impl PacketParser<'_> {
+ /// Starts hashing for the current [`OnePassSig`] packet.
+ ///
+ /// If automatic hashing is disabled using
+ /// [`PacketParserBuilder::automatic_hashing`], then hashing can
+ /// be explicitly enabled while parsing a [`OnePassSig`] packet.
+ ///
+ /// If this function is called on a packet other than a
+ /// [`OnePassSig`] packet, it returns [`Error::InvalidOperation`].
+ ///
+ /// [`Error::InvalidOperation`]: crate::Error::InvalidOperation
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # fn main() -> sequoia_openpgp::Result<()> {
+ /// # use sequoia_openpgp as openpgp;
+ /// # use openpgp::{Cert, Packet};
+ /// # use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};
+ /// // Parse a signed message, verify using the signer's key.
+ /// let message_data: &[u8] = // ...
+ /// # include_bytes!("../tests/data/messages/signed-1-sha256-testy.gpg");
+ /// # let cert: Cert = // ...
+ /// # Cert::from_bytes(include_bytes!("../tests/data/keys/testy.pgp"))?;
+ /// let signer = // ...
+ /// # cert.primary_key().key();
+ /// let mut good = false;
+ /// let mut ppr = PacketParserBuilder::from_bytes(message_data)?
+ /// .automatic_hashing(false)
+ /// .build()?;
+ /// while let PacketParserResult::Some(mut pp) = ppr {
+ /// if let Packet::OnePassSig(_) = &pp.packet {
+ /// pp.start_hashing()?;
+ /// }
+ /// if let Packet::Signature(sig) = &mut pp.packet {
+ /// good |= sig.verify(signer).is_ok();
+ /// }
+ /// // Start parsing the next packet, recursing.
+ /// ppr = pp.recurse()?.1;
+ /// }
+ /// assert!(good);
+ /// # Ok(()) }
+ /// ```
+ pub fn start_hashing(&mut self) -> Result<()> {
+ let ops: &OnePassSig = self.packet.downcast_ref()
+ .ok_or_else(|| Error::InvalidOperation(
+ "Must only be invoked on one-pass-signature packets".into())
+ )?;
+
+ let hash_algo = ops.hash_algo();
+ let typ = ops.typ();
+ let need_hash = HashingMode::for_signature(hash_algo, typ);
+ let recursion_depth = self.recursion_depth();
+ let want_hashes_for = if Cookie::processing_csf_message(&self.reader) {
+ HashesFor::CleartextSignature
+ } else {
+ HashesFor::Signature
+ };
+
+ // Walk up the reader chain to find the hashed reader on level
+ // recursion_depth - 1.
+ let mut reader : Option<&mut dyn BufferedReader<Cookie>>
+ = Some(&mut self.reader);
+ while let Some(r) = reader {
+ {
+ let cookie = r.cookie_mut();
+ if let Some(br_level) = cookie.level {
+ if br_level < recursion_depth - 1 {
+ break;
+ }
+ if br_level == recursion_depth - 1
+ && cookie.hashes_for == want_hashes_for {
+ // We found a suitable hashed reader.
+ // Make sure that it uses the required
+ // hash algorithm.
+ if ! cookie.sig_group().hashes.iter()
+ .any(|mode| {
+ mode.map(|ctx| ctx.algo()) == need_hash
+ })
+ {
+ cookie.sig_group_mut().hashes.push(
+ HashingMode::for_signature(
+ Box::new(hash_algo.context()?), typ));
+ }
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ reader = r.get_mut();
+ }
+
+ Ok(())
+ }
+}
+
#[test]
fn one_pass_sig_parser_test () {
use crate::SignatureType;