diff options
Diffstat (limited to 'openpgp/src/packet/signature.rs')
-rw-r--r-- | openpgp/src/packet/signature.rs | 155 |
1 files changed, 132 insertions, 23 deletions
diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs index c5f3b019..d09ea346 100644 --- a/openpgp/src/packet/signature.rs +++ b/openpgp/src/packet/signature.rs @@ -154,7 +154,7 @@ use crate::types::Timestamp; #[cfg(test)] /// Like quickcheck::Arbitrary, but bounded. -trait ArbitraryBounded { +pub(crate) trait ArbitraryBounded { /// Generates an arbitrary value, but only recurses if `depth > /// 0`. fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self; @@ -178,6 +178,8 @@ macro_rules! impl_arbitrary_with_bound { } pub mod subpacket; +mod v6; +pub use v6::Signature6; /// How many seconds to backdate signatures. /// @@ -285,6 +287,20 @@ impl SignatureFields { } } +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub(crate) enum SBVersion { + V4 {}, + V6 { + salt: Vec<u8>, + }, +} + +impl Default for SBVersion { + fn default() -> Self { + SBVersion::V4 {} + } +} + /// A Signature builder. /// /// The `SignatureBuilder` is used to create [`Signature`]s. Although @@ -451,7 +467,8 @@ pub struct SignatureBuilder { reference_time: Option<SystemTime>, overrode_creation_time: bool, original_creation_time: Option<SystemTime>, - fields: SignatureFields, + pub(crate) fields: SignatureFields, + pub(crate) version: SBVersion, } assert_send_and_sync!(SignatureBuilder); @@ -482,7 +499,8 @@ impl SignatureBuilder { pk_algo: PublicKeyAlgorithm::Unknown(0), hash_algo: HashAlgorithm::default(), subpackets: SubpacketAreas::default(), - } + }, + version: SBVersion::default(), } } @@ -1702,7 +1720,24 @@ impl SignatureBuilder { /// # Ok(()) } /// ``` pub fn pre_sign(mut self, signer: &dyn Signer) -> Result<Self> { - self.pk_algo = signer.public().pk_algo(); + let pk = signer.public(); + self.pk_algo = pk.pk_algo(); + + // Set the version. A Key6 will create a Signature6, a key4 + // will create a Signature4. If necessary, generate a salt. + // If the salt has been explicitly set, it is not changed. + self.version = match (self.version, pk.version()) { + (SBVersion::V4 {}, 4) => SBVersion::V4 {}, + (SBVersion::V6 { .. }, 4) => SBVersion::V4 {}, + (SBVersion::V4 {}, 6) => { + let mut salt = vec![0; self.fields.hash_algo().salt_size()?]; + crate::crypto::random(&mut salt); + SBVersion::V6 { salt } + }, + (SBVersion::V6 { salt }, 6) => SBVersion::V6 { salt }, + (_, n) => return Err(Error::InvalidOperation( + format!("Unsupported key version {}", n)).into()), + }; // Set the creation time. if ! self.overrode_creation_time { @@ -1711,31 +1746,83 @@ impl SignatureBuilder { } } - // Make sure we have an issuer packet. - if self.issuers().next().is_none() - && self.issuer_fingerprints().next().is_none() - { - self = self.set_issuer(signer.public().keyid())? - .set_issuer_fingerprint(signer.public().fingerprint())?; - } + match &self.version { + SBVersion::V4 {} => { + // Make sure we have an issuer packet. + if self.issuers().next().is_none() + && self.issuer_fingerprints().next().is_none() + { + self = self.set_issuer(signer.public().keyid())? + .set_issuer_fingerprint(signer.public().fingerprint())?; + } + + // Add a salt to v4 signatures to make the signature + // unpredictable. + let mut salt = [0; 32]; + crate::crypto::random(&mut salt); + self = self.set_notation("salt@notations.sequoia-pgp.org", + salt, None, false)?; + }, + SBVersion::V6 { .. } => { + // Make sure we have an issuer fingerprint packet. + if self.issuer_fingerprints().next().is_none() { + self = self + .set_issuer_fingerprint(signer.public().fingerprint())?; + } - // Add a salt to make the signature unpredictable. - let mut salt = [0; 32]; - crate::crypto::random(&mut salt); - self = self.set_notation("salt@notations.sequoia-pgp.org", - salt, None, false)?; + // In v6 signatures, we have a proper prefix salt. + }, + } self.sort(); Ok(self) } + /// Returns the prefix salt. + /// + /// In OpenPGP v6 signatures, a salt is prefixed to the data + /// stream hashed in the signature. If a v6 signature is + /// generated by using a v6 key, then a salt will be added + /// automatically. If a salt has been set explicitly (see + /// [`SignatureBuilder::set_prefix_salt`]), this function will + /// return the salt. + pub fn prefix_salt(&self) -> Option<&[u8]> { + match &self.version { + SBVersion::V4 {} => None, + SBVersion::V6 { salt } => Some(salt), + } + } + + /// Explicitly sets the prefix salt. + /// + /// In OpenPGP v6 signatures, a salt is prefixed to the data + /// stream hashed in the signature. If a v6 signature is + /// generated by using a v6 key, then a salt will be added + /// automatically. In general, you do not need to call this + /// function. + /// + /// When streaming a signed message, it is useful to explicitly + /// set the salt using this function. + pub fn set_prefix_salt(mut self, new_salt: Vec<u8>) -> (Self, Option<Vec<u8>>) { + let mut old = None; + + self.version = match std::mem::take(&mut self.version) { + SBVersion::V4 {} => SBVersion::V6 { salt: new_salt }, + SBVersion::V6 { salt } => { + old = Some(salt); + SBVersion::V6 { salt: new_salt } + }, + }; + + (self, old) + } + fn sign(self, signer: &mut dyn Signer, digest: Vec<u8>) -> Result<Signature> { let mpis = signer.sign(self.hash_algo, &digest)?; - - Ok(Signature4 { + let v4 = Signature4 { common: Default::default(), fields: self.fields, digest_prefix: [digest[0], digest[1]], @@ -1743,7 +1830,13 @@ impl SignatureBuilder { computed_digest: Some(digest), level: 0, additional_issuers: Vec::with_capacity(0), - }.into()) + }; + + match self.version { + SBVersion::V4 {} => Ok(v4.into()), + SBVersion::V6 { salt } => + Ok(Signature6::from_common(v4, salt)?.into()) + } } } @@ -1752,6 +1845,7 @@ impl From<Signature> for SignatureBuilder { match sig { Signature::V3(sig) => sig.into(), Signature::V4(sig) => sig.into(), + Signature::V6(sig) => sig.into(), } } } @@ -1777,10 +1871,17 @@ impl From<Signature4> for SignatureBuilder { overrode_creation_time: false, original_creation_time: creation_time, fields, + version: Default::default(), } } } +impl From<Signature6> for SignatureBuilder { + fn from(sig: Signature6) -> Self { + SignatureBuilder::from(sig.common) + } +} + /// Holds a v4 Signature packet. /// /// This holds a [version 4] Signature packet. Normally, you won't @@ -2699,6 +2800,13 @@ impl Signature { "Signature has no creation time subpacket".into()).into()); } + // XXX if hash_algo.salt_size().map(|expected| expected != salt_len) + // XXX .unwrap_or(false) + // XXX { + // XXX return php.fail(format!("bad salt length, expected {} got {}", + // XXX ); + // XXX } + let result = key.verify(self.mpis(), self.hash_algo(), digest.as_ref()); if result.is_ok() { // Mark information in this signature as authenticated. @@ -3378,10 +3486,11 @@ impl From<Signature4> for super::Signature { #[cfg(test)] impl ArbitraryBounded for super::Signature { fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self { - if bool::arbitrary(g) { - Signature3::arbitrary_bounded(g, depth).into() - } else { - Signature4::arbitrary_bounded(g, depth).into() + match u8::arbitrary(g) % 3 { + 0 => Signature3::arbitrary_bounded(g, depth).into(), + 1 => Signature4::arbitrary_bounded(g, depth).into(), + 2 => Signature6::arbitrary_bounded(g, depth).into(), + _ => unreachable!(), } } } |