summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-08-21 13:47:05 +0200
committerJustus Winter <justus@sequoia-pgp.org>2023-11-09 16:11:21 +0100
commitb59876e066cf6ab87a5d44af2725eee84fc3cb93 (patch)
tree83e7368f2bc850c29fd9caee0566b14fbe9acfdd
parent535aa58576b7c5642ec1ca09c046a52884223e7e (diff)
fix hashing inline-signed and detached v6 signatures.crypto-refresh-seip-v2
- Fixes #1042.
-rw-r--r--openpgp/src/parse.rs71
-rw-r--r--openpgp/src/parse/hashed_reader.rs147
-rw-r--r--openpgp/src/parse/stream.rs4
-rw-r--r--openpgp/src/serialize/stream.rs64
4 files changed, 183 insertions, 103 deletions
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index fe58c488..c69c986b 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -841,14 +841,13 @@ pub(crate) struct SignatureGroup {
/// optimization: to verify two signatures using the same hash
/// algorithm, the hash must be computed just once. We implement
/// this optimization for v4 signatures.
- pub(crate) hashes: Vec<(Vec<u8>,
- HashingMode<Box<dyn crypto::hash::Digest>>)>,
+ pub(crate) hashes: Vec<HashingMode<Box<dyn crypto::hash::Digest>>>,
}
impl fmt::Debug for SignatureGroup {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let algos = self.hashes.iter()
- .map(|(salt, mode)| (salt, mode.map(|ctx| ctx.algo())))
+ .map(|mode| mode.map(|ctx| ctx.algo()))
.collect::<Vec<_>>();
f.debug_struct("Cookie")
@@ -1400,7 +1399,7 @@ impl Signature {
/// When parsing an inline-signed message, attaches the digest to
/// the signature.
fn parse_finish(indent: isize, mut pp: PacketParser,
- typ: SignatureType, hash_algo: HashAlgorithm)
+ hash_algo: HashAlgorithm)
-> Result<PacketParser>
{
tracer!(TRACE, "Signature::parse_finish", indent);
@@ -1419,13 +1418,8 @@ impl Signature {
return Ok(pp);
}
- let need_hash = HashingMode::for_signature(hash_algo, typ);
- let need_salt =
- if let Packet::Signature(Signature::V6(sig)) = &pp.packet {
- sig.salt()
- } else {
- &[]
- };
+ let need_hash = HashingMode::for_signature(hash_algo, sig);
+ t!("Need a {:?}", need_hash);
// Locate the corresponding HashedReader and extract the
// computed hash.
@@ -1465,11 +1459,14 @@ impl Signature {
if cookie.hashes_for == HashesFor::Signature
|| cookie.hashes_for == HashesFor::CleartextSignature
{
+ t!("Have: {:?}",
+ cookie.sig_group().hashes.iter()
+ .map(|h| h.map(|h| h.algo()))
+ .collect::<Vec<_>>());
if let Some(hash) =
cookie.sig_group().hashes.iter().find_map(
- |(salt, mode)|
- if salt == need_salt
- && mode.map(|ctx| ctx.algo()) == need_hash
+ |mode|
+ if mode.map(|ctx| ctx.algo()) == need_hash
{
Some(mode.as_ref())
} else {
@@ -1552,7 +1549,7 @@ impl Signature6 {
mpis));
let pp = php.ok(sig.into())?;
- Signature::parse_finish(indent, pp, typ, hash_algo)
+ Signature::parse_finish(indent, pp, hash_algo)
}
}
@@ -1596,7 +1593,7 @@ impl Signature4 {
[digest_prefix1, digest_prefix2],
mpis).into()))?;
- Signature::parse_finish(indent, pp, typ, hash_algo)
+ Signature::parse_finish(indent, pp, hash_algo)
}
/// Returns whether the data appears to be a signature (no promises).
@@ -1684,7 +1681,7 @@ impl Signature3 {
[digest_prefix1, digest_prefix2],
mpis).into()))?;
- Signature::parse_finish(indent, pp, typ, hash_algo)
+ Signature::parse_finish(indent, pp, hash_algo)
}
}
@@ -2099,7 +2096,7 @@ impl OnePassSig3 {
sig.set_pk_algo(pk_algo.into());
sig.set_issuer(KeyID::from_bytes(&issuer));
sig.set_last_raw(last);
- let need_hash = HashingMode::for_signature(hash_algo, typ);
+ let need_hash = HashingMode::for_salt_and_type(hash_algo, &[], typ);
let recursion_depth = php.recursion_depth();
@@ -2137,16 +2134,14 @@ impl OnePassSig3 {
// hash algorithm.
if php.state.settings.automatic_hashing
&& ! cookie.sig_group().hashes.iter()
- .any(|(salt, mode)| {
+ .any(|mode| {
mode.map(|ctx| ctx.algo()) == need_hash
- && salt.is_empty()
})
{
if let Ok(ctx) = hash_algo.context() {
cookie.sig_group_mut().hashes.push(
- (vec![],
- HashingMode::for_signature(
- Box::new(ctx), typ))
+ HashingMode::for_salt_and_type(
+ Box::new(ctx), &[], typ)
);
}
}
@@ -2182,7 +2177,7 @@ impl OnePassSig3 {
// it explicitly.
let mut algos = Vec::new();
if pp.state.settings.automatic_hashing && hash_algo.is_supported() {
- algos.push(need_hash);
+ algos.push(HashingMode::for_salt_and_type(hash_algo, &[], typ));
}
// We can't push the HashedReader on the BufferedReader stack:
@@ -2203,7 +2198,7 @@ impl OnePassSig3 {
assert!(! fake_eof);
let mut reader = HashedReader::new(
- reader, want_hashes_for, vec![], algos)?;
+ reader, want_hashes_for, algos)?;
reader.cookie_mut().level = Some(recursion_depth - 1);
// Account for this OPS packet.
reader.cookie_mut().sig_group_mut().ops_count += 1;
@@ -2280,7 +2275,8 @@ impl PacketParser<'_> {
let hash_algo = ops.hash_algo();
let typ = ops.typ();
let salt = ops.salt();
- let need_hash = HashingMode::for_signature(hash_algo, typ);
+ let need_hash = HashingMode::for_salt_and_type(
+ hash_algo, salt.unwrap_or(&[]), typ);
let recursion_depth = self.recursion_depth();
let want_hashes_for = if Cookie::processing_csf_message(&self.reader) {
HashesFor::CleartextSignature
@@ -2306,13 +2302,13 @@ impl PacketParser<'_> {
// hash algorithm.
if ! cookie.sig_group().hashes.iter()
.any(|mode| {
- mode.1.map(|ctx| ctx.algo()) == need_hash
+ mode.map(|ctx| ctx.algo()) == need_hash
})
{
- cookie.sig_group_mut().hashes.push((
- salt.map(|s| s.into()).unwrap_or_default(),
- HashingMode::for_signature(
- Box::new(hash_algo.context()?), typ)));
+ cookie.sig_group_mut().hashes.push(
+ HashingMode::for_salt_and_type(
+ Box::new(hash_algo.context()?),
+ salt.unwrap_or(&[]), typ));
}
break;
}
@@ -2399,7 +2395,7 @@ impl OnePassSig6 {
// against when we get to the Signature packet.
let mut algos = Vec::new();
if hash_algo.is_supported() {
- algos.push(HashingMode::for_signature(hash_algo, typ));
+ algos.push(HashingMode::for_salt_and_type(hash_algo, &salt, typ));
}
// Commit here after potentially pushing a signature group.
@@ -2423,7 +2419,7 @@ impl OnePassSig6 {
assert!(! fake_eof);
let mut reader = HashedReader::new(
- reader, want_hashes_for, salt, algos)?;
+ reader, want_hashes_for, algos)?;
reader.cookie_mut().level = Some(recursion_depth - 1);
// Account for this OPS packet.
reader.cookie_mut().sig_group_mut().ops_count += 1;
@@ -3424,9 +3420,9 @@ impl MDC {
if !state.sig_group().hashes.is_empty() {
let h = state.sig_group_mut().hashes
.iter_mut().find_map(
- |(_salt, mode)|
- if mode.map(|ctx| ctx.algo()) ==
- HashingMode::Binary(HashAlgorithm::SHA1)
+ |mode|
+ if matches!(mode.map(|ctx| ctx.algo()),
+ HashingMode::Binary(_, HashAlgorithm::SHA1))
{
Some(mode.as_mut())
} else {
@@ -6066,8 +6062,7 @@ impl<'a> PacketParser<'a> {
// And the hasher.
let mut reader = HashedReader::new(
reader, HashesFor::MDC,
- vec![],
- vec![HashingMode::Binary(HashAlgorithm::SHA1)])?;
+ vec![HashingMode::Binary(vec![], HashAlgorithm::SHA1)])?;
reader.cookie_mut().level = Some(self.recursion_depth());
t!("Pushing HashedReader, level {:?}.",
diff --git a/openpgp/src/parse/hashed_reader.rs b/openpgp/src/parse/hashed_reader.rs
index 51549060..1335c7c0 100644
--- a/openpgp/src/parse/hashed_reader.rs
+++ b/openpgp/src/parse/hashed_reader.rs
@@ -7,6 +7,8 @@ use buffered_reader::BufferedReader;
use buffered_reader::buffered_reader_generic_read_impl;
use crate::crypto::hash::Digest;
+use crate::fmt::hex;
+use crate::packet::Signature;
use crate::parse::{Cookie, HashesFor, Hashing};
use crate::Result;
use crate::types::HashAlgorithm;
@@ -23,25 +25,34 @@ pub(crate) enum HashingMode<T> {
/// Hash for a binary signature.
///
/// The data is hashed as-is.
- Binary(T),
+ Binary(Vec<u8>, T),
/// Hash for a text signature.
///
/// The data is hashed with line endings normalized to `\r\n`.
- Text(T),
+ Text(Vec<u8>, T),
/// Like Text, but the last character that we hashed was a '\r'
/// that we converted to a '\r\n'.
- TextLastWasCr(T),
+ TextLastWasCr(Vec<u8>, T),
}
impl<T: std::fmt::Debug> std::fmt::Debug for HashingMode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use self::HashingMode::*;
match self {
- Binary(t) => write!(f, "Binary({:?})", t),
- Text(t) => write!(f, "Text({:?})", t),
- TextLastWasCr(t) => write!(f, "Text(last was CR, {:?})", t),
+ Binary(salt, t) if salt.is_empty() =>
+ write!(f, "Binary({:?})", t),
+ Binary(salt, t) =>
+ write!(f, "Binary({}, {:?})", hex::encode(salt), t),
+ Text(salt, t) if salt.is_empty() =>
+ write!(f, "Text({:?})", t),
+ Text(salt, t) =>
+ write!(f, "Text({}, {:?})", hex::encode(salt), t),
+ TextLastWasCr(salt, t) if salt.is_empty() =>
+ write!(f, "Text(last was CR, {:?})", t),
+ TextLastWasCr(salt, t) =>
+ write!(f, "Text(last was CR, {}, {:?})", hex::encode(salt), t),
}
}
}
@@ -50,12 +61,17 @@ impl<T: PartialEq> PartialEq for HashingMode<T> {
fn eq(&self, other: &Self) -> bool {
use self::HashingMode::*;
match (self, other) {
- (Binary(s), Binary(o)) => s.eq(o),
-
- (Text(s), Text(o)) => s.eq(o),
- (TextLastWasCr(s), Text(o)) => s.eq(o),
- (Text(s), TextLastWasCr(o)) => s.eq(o),
- (TextLastWasCr(s), TextLastWasCr(o)) => s.eq(o),
+ (Binary(salt_s, s), Binary(salt_o, o)) =>
+ salt_s == salt_o && s == o,
+
+ (Text(salt_s, s), Text(salt_o, o)) =>
+ salt_s == salt_o && s == o,
+ (TextLastWasCr(salt_s, s), Text(salt_o, o)) =>
+ salt_s == salt_o && s == o,
+ (Text(salt_s, s), TextLastWasCr(salt_o, o)) =>
+ salt_s == salt_o && s == o,
+ (TextLastWasCr(salt_s, s), TextLastWasCr(salt_o, o)) =>
+ salt_s == salt_o && s == o,
_ => false,
}
@@ -66,9 +82,9 @@ impl<T> HashingMode<T> {
pub(crate) fn map<U, F: Fn(&T) -> U>(&self, f: F) -> HashingMode<U> {
use self::HashingMode::*;
match self {
- Binary(t) => Binary(f(t)),
- Text(t) => Text(f(t)),
- TextLastWasCr(t) => TextLastWasCr(f(t)),
+ Binary(salt, t) => Binary(salt.clone(), f(t)),
+ Text(salt, t) => Text(salt.clone(), f(t)),
+ TextLastWasCr(salt, t) => TextLastWasCr(salt.clone(), f(t)),
}
}
@@ -76,44 +92,62 @@ impl<T> HashingMode<T> {
-> Result<HashingMode<U>> {
use self::HashingMode::*;
match self {
- Binary(t) => Ok(Binary(f(t)?)),
- Text(t) => Ok(Text(f(t)?)),
- TextLastWasCr(t) => Ok(TextLastWasCr(f(t)?)),
+ Binary(salt, t) => Ok(Binary(salt.clone(), f(t)?)),
+ Text(salt, t) => Ok(Text(salt.clone(), f(t)?)),
+ TextLastWasCr(salt, t) => Ok(TextLastWasCr(salt.clone(), f(t)?)),
+ }
+ }
+
+ pub(crate) fn salt(&self) -> &[u8] {
+ use self::HashingMode::*;
+ match self {
+ Binary(salt, _t) => salt,
+ Text(salt, _t) => salt,
+ TextLastWasCr(salt, _t) => salt,
}
}
pub(crate) fn as_ref(&self) -> &T {
use self::HashingMode::*;
match self {
- Binary(t) => t,
- Text(t) => t,
- TextLastWasCr(t) => t,
+ Binary(_salt, t) => t,
+ Text(_salt, t) => t,
+ TextLastWasCr(_salt, t) => t,
}
}
pub(crate) fn as_mut(&mut self) -> &mut T {
use self::HashingMode::*;
match self {
- Binary(t) => t,
- Text(t) => t,
- TextLastWasCr(t) => t,
+ Binary(_salt, t) => t,
+ Text(_salt, t) => t,
+ TextLastWasCr(_salt, t) => t,
}
}
- pub(crate) fn for_signature(t: T, typ: SignatureType) -> Self {
+ pub(crate) fn for_signature(t: T, s: &Signature) -> Self {
+ match s {
+ Signature::V3(s) => Self::for_salt_and_type(t, &[], s.typ()),
+ Signature::V4(s) => Self::for_salt_and_type(t, &[], s.typ()),
+ Signature::V6(s) => Self::for_salt_and_type(t, s.salt(), s.typ()),
+ }
+ }
+ pub(crate) fn for_salt_and_type(t: T, salt: &[u8], typ: SignatureType)
+ -> Self
+ {
if typ == SignatureType::Text {
- HashingMode::Text(t)
+ HashingMode::Text(salt.into(), t)
} else {
- HashingMode::Binary(t)
+ HashingMode::Binary(salt.into(), t)
}
}
pub(crate) fn into_inner(self) -> T {
use self::HashingMode::*;
match self {
- Binary(t) => t,
- Text(t) => t,
- TextLastWasCr(t) => t,
+ Binary(_salt, t) => t,
+ Text(_salt, t) => t,
+ TextLastWasCr(_salt, t) => t,
}
}
}
@@ -132,9 +166,9 @@ impl<D> HashingMode<D>
}
let (h, mut last_was_cr) = match self {
- HashingMode::Text(h) => (h, false),
- HashingMode::TextLastWasCr(h) => (h, true),
- HashingMode::Binary(h) => return h.update(data),
+ HashingMode::Text(_salt, h) => (h, false),
+ HashingMode::TextLastWasCr(_salt, h) => (h, true),
+ HashingMode::Binary(_salt, h) => return h.update(data),
};
let mut line = data;
@@ -173,19 +207,21 @@ impl<D> HashingMode<D>
}
match (&mut *self, last_is_cr) {
- (&mut HashingMode::Text(_), false) => {
+ (&mut HashingMode::Text(_, _), false) => {
// This is the common case. Getting a crlf that is
// split across two chunks is extremely rare. Hence,
// the clones used to change the variant are rarely
// needed.
},
- (&mut HashingMode::Text(ref mut h), true) => {
- *self = HashingMode::TextLastWasCr(h.clone());
+ (&mut HashingMode::Text(ref mut salt, ref mut h), true) => {
+ *self =
+ HashingMode::TextLastWasCr(std::mem::take(salt), h.clone());
}
- (&mut HashingMode::TextLastWasCr(ref mut h), false) => {
- *self = HashingMode::Text(h.clone());
+ (&mut HashingMode::TextLastWasCr(ref mut salt, ref mut h), false) =>
+ {
+ *self = HashingMode::Text(std::mem::take(salt), h.clone());
},
- (&mut HashingMode::TextLastWasCr(_), true) => (),
+ (&mut HashingMode::TextLastWasCr(_, _), true) => (),
_ => unreachable!("handled above"),
}
@@ -217,19 +253,19 @@ impl<R: BufferedReader<Cookie>> HashedReader<R> {
/// purpose. `algos` is a list of algorithms for which we should
/// compute the hash.
pub fn new(reader: R, hashes_for: HashesFor,
- salt: Vec<u8>,
algos: Vec<HashingMode<HashAlgorithm>>)
-> Result<Self> {
let mut cookie = Cookie::default();
for mode in algos {
+ let salt = mode.salt().to_vec();
let mode = mode.mapf(|algo| {
let mut ctx = algo.context()?;
ctx.update(&salt);
Ok(ctx)
})?;
- cookie.sig_group_mut().hashes.push((salt.clone(), mode));
+ cookie.sig_group_mut().hashes.push(mode);
}
cookie.hashes_for = hashes_for;
@@ -267,13 +303,12 @@ impl Cookie {
assert!(ngroups > 1);
for h in self.sig_groups[ngroups-2].hashes.iter_mut()
{
- t!("({:?}): group {} {:?} (salted: {:?} hashing {} stashed bytes)",
+ t!("({:?}): group {} {:?} hashing {} stashed bytes.",
hashes_for, ngroups-2,
- h.1.map(|ctx| ctx.algo()),
- ! h.0.is_empty(),
+ h.map(|ctx| ctx.algo()),
data.len());
- h.1.update(&stashed_data);
+ h.update(&stashed_data);
}
}
@@ -298,8 +333,8 @@ impl Cookie {
for h in sig_group.hashes.iter_mut() {
t!("{:?}: group {} {:?} hashing {} bytes.",
- hashes_for, i, h.1.map(|ctx| ctx.algo()), data.len());
- h.1.update(data);
+ hashes_for, i, h.map(|ctx| ctx.algo()), data.len());
+ h.update(data);
}
}
}
@@ -333,8 +368,8 @@ impl Cookie {
// Hash the data.
for h in self.sig_groups[0].hashes.iter_mut() {
t!("{:?}: {:?} hashing {} bytes.",
- hashes_for, h.1.map(|ctx| ctx.algo()), data.len());
- h.1.update(data);
+ hashes_for, h.map(|ctx| ctx.algo()), data.len());
+ h.update(data);
}
}
}
@@ -463,14 +498,14 @@ pub(crate) fn hash_buffered_reader<R>(reader: R,
where R: BufferedReader<crate::parse::Cookie>,
{
let mut reader
- = HashedReader::new(reader, HashesFor::Signature, vec![], algos.to_vec())?;
+ = HashedReader::new(reader, HashesFor::Signature, algos.to_vec())?;
// Hash all of the data.
reader.drop_eof()?;
let hashes =
mem::take(&mut reader.cookie_mut().sig_group_mut().hashes);
- Ok(hashes.into_iter().map(|h| h.1).collect())
+ Ok(hashes)
}
#[cfg(test)]
@@ -518,9 +553,8 @@ mod test {
test.data, None, Default::default());
let mut reader
= HashedReader::new(reader, HashesFor::MDC,
- vec![],
test.expected.keys().cloned()
- .map(HashingMode::Binary)
+ .map(|v| HashingMode::Binary(vec![], v))
.collect()).unwrap();
assert_eq!(reader.steal_eof().unwrap(), test.data);
@@ -528,7 +562,7 @@ mod test {
let cookie = reader.cookie_mut();
let mut hashes = std::mem::take(&mut cookie.sig_group_mut().hashes);
- for (_salt, mode) in hashes.iter_mut() {
+ for mode in hashes.iter_mut() {
let hash = mode.as_mut();
let algo = hash.algo();
let mut digest = vec![0u8; hash.digest_size()];
@@ -553,7 +587,8 @@ mod test {
] {
for chunk_size in &[ text.len(), 1 ] {
let mut ctx
- = HashingMode::Text(HashAlgorithm::SHA256.context()?);
+ = HashingMode::Text(vec![],
+ HashAlgorithm::SHA256.context()?);
for chunk in text.as_bytes().chunks(*chunk_size) {
ctx.update(chunk);
}
@@ -589,7 +624,7 @@ mod test {
hash_buffered_reader(
reader,
&expected.keys().cloned()
- .map(HashingMode::Binary).
+ .map(|v| HashingMode::Binary(vec![], v)).
collect::<Vec<_>>())
.unwrap();
diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs
index 446c2941..b06b64fd 100644
--- a/openpgp/src/parse/stream.rs
+++ b/openpgp/src/parse/stream.rs
@@ -2537,7 +2537,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> {
// Compute the necessary hashes.
let algos: Vec<_> = sigs.iter().map(|s| {
- HashingMode::for_signature(s.hash_algo(), s.typ())
+ HashingMode::for_signature(s.hash_algo(), s)
}).collect();
let hashes =
crate::parse::hashed_reader::hash_buffered_reader(data, &algos)?;
@@ -2545,7 +2545,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> {
// Attach the digests.
for sig in sigs.iter_mut() {
let need_hash =
- HashingMode::for_signature(sig.hash_algo(), sig.typ());
+ HashingMode::for_signature(sig.hash_algo(), sig);
// Note: |hashes| < 10, most likely 1.
for mode in hashes.iter()
.filter(|m| m.map(|c| c.algo()) == need_hash)
diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs
index 8dddcc4b..0e13e627 100644
--- a/openpgp/src/serialize/stream.rs
+++ b/openpgp/src/serialize/stream.rs
@@ -1245,7 +1245,7 @@ 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.");
- for (keypair, signer_hash, _signer_salt) in self.signers.iter_mut() {
+ for (keypair, signer_hash, signer_salt) in self.signers.iter_mut() {
// First, compute a suitable hash algorithm, starting with
// the one configured using Self::hash_algo.
let mut acceptable_hashes =
@@ -1273,7 +1273,7 @@ impl<'a> Signer<'a> {
}
let algo = acceptable_hashes[0];
*signer_hash = algo;
- let hash = algo.context()?;
+ let mut hash = algo.context()?;
match keypair.public().version() {
4 => {
@@ -1281,11 +1281,32 @@ impl<'a> Signer<'a> {
if self.template.typ() == SignatureType::Text
|| self.mode == SignatureMode::Cleartext
{
- HashingMode::Text(hash)
+ HashingMode::Text(vec![], hash)
} else {
- HashingMode::Binary(hash)
+ HashingMode::Binary(vec![], hash)
});
},
+ 6 => {
+ // Version 6 signatures are salted, and we
+ // need to include it in the OPS packet.
+ // Generate and remember the salt here.
+ let mut salt = vec![0; algo.salt_size()?];
+ crate::crypto::random(&mut salt);
+
+ // Add the salted context.
+ hash.update(&salt);
+ self.hashes.push(
+ if self.template.typ() == SignatureType::Text
+ || self.mode == SignatureMode::Cleartext
+ {
+ HashingMode::Text(salt.clone(), hash)
+ } else {
+ HashingMode::Binary(salt.clone(), hash)
+ });
+
+ // And remember which signer used which salt.
+ *signer_salt = salt;
+ },
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
}
@@ -1296,7 +1317,7 @@ impl<'a> Signer<'a> {
// For every key we collected, build and emit a one pass
// signature packet.
let signers_count = self.signers.len();
- for (i, (keypair, hash_algo, _salt)) in
+ for (i, (keypair, hash_algo, salt)) in
self.signers.iter().enumerate()
{
let last = i == signers_count - 1;
@@ -1312,6 +1333,18 @@ impl<'a> Signer<'a> {
Packet::from(ops)
.serialize(self.inner.as_mut().unwrap())?;
},
+ 6 => {
+ // Version 6 signatures are salted, and we
+ // need to include it in the OPS packet.
+ let mut ops = OnePassSig6::new(
+ self.template.typ(), key.fingerprint());
+ ops.set_pk_algo(key.pk_algo());
+ ops.set_hash_algo(*hash_algo);
+ ops.set_salt(salt.clone());
+ ops.set_last(last);
+ Packet::from(ops)
+ .serialize(self.inner.as_mut().unwrap())?;
+ },
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
}
@@ -1381,14 +1414,15 @@ impl<'a> Signer<'a> {
// Emit the signatures in reverse, so that the
// one-pass-signature and signature packets "bracket" the
// message.
- for (signer, algo, _signer_salt) in self.signers.iter_mut().rev() {
+ for (signer, algo, signer_salt) in self.signers.iter_mut().rev() {
let (mut sig, hash) = match signer.public().version() {
4 => {
// V4 signature.
let hash = self.hashes.iter()
.find_map(|hash| {
- if hash.as_ref().algo() == *algo
+ if hash.salt().is_empty()
+ && hash.as_ref().algo() == *algo
{
Some(hash.clone())
} else {
@@ -1402,6 +1436,22 @@ impl<'a> Signer<'a> {
(sig, hash)
},
+ 6 => {
+ // V6 signature.
+ let hash = self.hashes.iter()
+ .find_map(|hash| if signer_salt == hash.salt() {
+ Some(hash.clone())
+ } else {
+ None
+ })
+ .expect("we put it in there");
+
+ // Make and hash a signature packet.
+ let sig = self.template.clone()
+ .set_prefix_salt(signer_salt.clone()).0;
+
+ (sig, hash)
+ },
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
};