summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-10-06 13:24:04 +0200
committerJustus Winter <justus@sequoia-pgp.org>2020-10-06 14:56:16 +0200
commit48d18fdff5f24526c3e83789f1ebfad763fa94af (patch)
treed0aa424d9589f28a2ead700a7f5b59efe600bd4d
parent93d84877304486bfcb2be664a1565f835bd60cf3 (diff)
openpgp: Implement two-octet checksums over secret key material.
- Also, rename methods to be more explicit.
-rw-r--r--openpgp/src/crypto/mpi.rs21
-rw-r--r--openpgp/src/packet/key.rs7
-rw-r--r--openpgp/src/parse/mpis.rs54
-rw-r--r--openpgp/src/serialize.rs27
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(())
}