diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-10-06 13:24:04 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-10-06 14:56:16 +0200 |
commit | 48d18fdff5f24526c3e83789f1ebfad763fa94af (patch) | |
tree | d0aa424d9589f28a2ead700a7f5b59efe600bd4d | |
parent | 93d84877304486bfcb2be664a1565f835bd60cf3 (diff) |
openpgp: Implement two-octet checksums over secret key material.
- Also, rename methods to be more explicit.
-rw-r--r-- | openpgp/src/crypto/mpi.rs | 21 | ||||
-rw-r--r-- | openpgp/src/packet/key.rs | 7 | ||||
-rw-r--r-- | openpgp/src/parse/mpis.rs | 54 | ||||
-rw-r--r-- | openpgp/src/serialize.rs | 27 |
4 files changed, 81 insertions, 28 deletions
diff --git a/openpgp/src/crypto/mpi.rs b/openpgp/src/crypto/mpi.rs index 1c6757b2..4964425e 100644 --- a/openpgp/src/crypto/mpi.rs +++ b/openpgp/src/crypto/mpi.rs @@ -859,6 +859,27 @@ impl Arbitrary for SecretKeyMaterial { } } +/// Checksum method for secret key material. +/// +/// Secret key material may be protected by a checksum. See [Section +/// 5.5.3 of RFC 4880] for details. +/// +/// [Section 5.5.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.5.3 +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +pub enum SecretKeyChecksum { + /// SHA1 over the decrypted secret key. + SHA1, + + /// Sum of the decrypted secret key octets modulo 65536. + Sum16, +} + +impl Default for SecretKeyChecksum { + fn default() -> Self { + SecretKeyChecksum::SHA1 + } +} + /// An encrypted session key. /// /// Provides a typed and structured way of storing multiple MPIs in diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs index e6bb1031..8ba4d5d2 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key.rs @@ -1367,7 +1367,8 @@ impl Unencrypted { { let mut encryptor = Encryptor::new(algo, &key, &mut esk)?; encryptor.write_all(&trash)?; - self.map(|mpis| mpis.serialize_chksumd(&mut encryptor))?; + self.map(|mpis| mpis.serialize_with_checksum(&mut encryptor, + Default::default()))?; } Ok(Encrypted::new(s2k, algo, esk.into_boxed_slice())) @@ -1502,7 +1503,9 @@ impl Encrypted { let mut trash = vec![0u8; self.algo.block_size()?]; dec.read_exact(&mut trash)?; - mpi::SecretKeyMaterial::parse_chksumd(pk_algo, &mut dec).map(|m| m.into()) + mpi::SecretKeyMaterial::parse_with_checksum(pk_algo, &mut dec, + Default::default()) + .map(|m| m.into()) } } diff --git a/openpgp/src/parse/mpis.rs b/openpgp/src/parse/mpis.rs index b1af3c22..8613a138 100644 --- a/openpgp/src/parse/mpis.rs +++ b/openpgp/src/parse/mpis.rs @@ -155,12 +155,14 @@ impl mpi::PublicKey { } impl mpi::SecretKeyMaterial { - /// Parses secret key MPIs for `algo` plus their SHA1 checksum. Fails if the - /// checksum is wrong. - pub fn parse_chksumd<T: Read>(algo: PublicKeyAlgorithm, cur: T) - -> Result<Self> { - use std::io::Cursor; - use crate::serialize::Marshal; + /// Parses secret key MPIs for `algo` plus their SHA1 checksum. + /// + /// Fails if the checksum is wrong. + pub fn parse_with_checksum<T: Read>(algo: PublicKeyAlgorithm, + cur: T, + checksum: mpi::SecretKeyChecksum) + -> Result<Self> { + use crate::serialize::{Marshal, MarshalInto}; // read mpis let bio = buffered_reader::Generic::with_cookie( @@ -168,23 +170,37 @@ impl mpi::SecretKeyMaterial { let mut php = PacketHeaderParser::new_naked(bio); let mpis = Self::_parse(algo, &mut php)?; - // read expected sha1 hash of the mpis - let their_chksum = php.parse_bytes("checksum", 20)?; - let mut cur = Cursor::new(vec![]); + let good = match checksum { + mpi::SecretKeyChecksum::SHA1 => { + // Read expected SHA1 hash of the MPIs. + let their_chksum = php.parse_bytes("checksum", 20)?; - // compute sha1 hash - mpis.serialize(&mut cur)?; - let buf = cur.into_inner(); - let mut hsh = HashAlgorithm::SHA1.context().unwrap(); + // Compute SHA1 hash. + let mut hsh = HashAlgorithm::SHA1.context().unwrap(); + mpis.serialize(&mut hsh)?; + let mut our_chksum = [0u8; 20]; + hsh.digest(&mut our_chksum); - hsh.update(&buf); - let mut our_chksum = [0u8; 20]; - hsh.digest(&mut our_chksum); + our_chksum == their_chksum[..] + }, - if our_chksum != their_chksum[..] { - Err(Error::MalformedMPI("checksum wrong".to_string()).into()) - } else { + mpi::SecretKeyChecksum::Sum16 => { + // Read expected sum of the MPIs. + let their_chksum = php.parse_bytes("checksum", 2)?; + + // Compute sum. + let our_chksum = mpis.to_vec()?.iter() + .fold(0u16, |acc, v| acc.wrapping_add(*v as u16)) + .to_be_bytes(); + + our_chksum == their_chksum[..] + }, + }; + + if good { Ok(mpis) + } else { + Err(Error::MalformedMPI("checksum wrong".to_string()).into()) } } diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 800dceb7..a7caab66 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -1094,16 +1094,29 @@ impl MarshalInto for crypto::mpi::SecretKeyMaterial { impl crypto::mpi::SecretKeyMaterial { /// Writes this secret key with a checksum to `w`. - pub fn serialize_chksumd<W: io::Write>(&self, w: &mut W) -> Result<()> { + pub fn serialize_with_checksum<W: io::Write>( + &self, w: &mut W, + checksum: crypto::mpi::SecretKeyChecksum) + -> Result<()> + { // First, the MPIs. self.serialize(w)?; - // The checksum is SHA1 over the serialized MPIs. - let mut hash = HashAlgorithm::SHA1.context().unwrap(); - self.serialize(&mut hash)?; - let mut digest = [0u8; 20]; - hash.digest(&mut digest); - w.write_all(&digest)?; + match checksum { + crypto::mpi::SecretKeyChecksum::SHA1 => { + // The checksum is SHA1 over the serialized MPIs. + let mut hash = HashAlgorithm::SHA1.context().unwrap(); + self.serialize(&mut hash)?; + let mut digest = [0u8; 20]; + hash.digest(&mut digest); + w.write_all(&digest)?; + }, + crypto::mpi::SecretKeyChecksum::Sum16 => { + w.write_all(&self.to_vec()?.iter() + .fold(0u16, |acc, v| acc.wrapping_add(*v as u16)) + .to_be_bytes())?; + }, + } Ok(()) } |