diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-10-06 14:50:45 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-10-06 14:56:16 +0200 |
commit | e980facce477e7bbe5de5d5e970124a9203c0004 (patch) | |
tree | 03d96d095939570b6e4788a39e711842b3dc1fdc | |
parent | ad127e721f512d3c77733db309cbe74956729069 (diff) |
openpgp: Support encrypted keys protected by two octet sum.
- Encrypted key material can be protected by either SHA1 or a two
octet sum over the key material. The latter is not used anymore,
but GnuPG 2.1 emits this S2K usage octet when emitting TSKs with
detached keys.
- Fixes #569.
-rw-r--r-- | openpgp/src/packet/key.rs | 21 | ||||
-rw-r--r-- | openpgp/src/parse.rs | 11 | ||||
-rw-r--r-- | openpgp/src/serialize.rs | 8 | ||||
-rw-r--r-- | openpgp/src/serialize/cert.rs | 9 |
4 files changed, 36 insertions, 13 deletions
diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs index 8ba4d5d2..3384738b 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key.rs @@ -1363,15 +1363,16 @@ impl Unencrypted { let mut trash = vec![0u8; algo.block_size()?]; crypto::random(&mut trash); + let checksum = Default::default(); let mut esk = Vec::new(); { let mut encryptor = Encryptor::new(algo, &key, &mut esk)?; encryptor.write_all(&trash)?; self.map(|mpis| mpis.serialize_with_checksum(&mut encryptor, - Default::default()))?; + checksum))?; } - Ok(Encrypted::new(s2k, algo, esk.into_boxed_slice())) + Ok(Encrypted::new(s2k, algo, Some(checksum), esk.into_boxed_slice())) } } @@ -1386,6 +1387,8 @@ pub struct Encrypted { s2k: S2K, /// Symmetric algorithm used to encrypt the secret key material. algo: SymmetricAlgorithm, + /// Checksum method. + checksum: Option<mpi::SecretKeyChecksum>, /// Encrypted MPIs prefixed with the IV. /// /// If we recognized the S2K object during parsing, we can @@ -1433,19 +1436,21 @@ impl std::hash::Hash for Encrypted { impl Encrypted { /// Creates a new encrypted key object. - pub fn new(s2k: S2K, algo: SymmetricAlgorithm, ciphertext: Box<[u8]>) + pub fn new(s2k: S2K, algo: SymmetricAlgorithm, + checksum: Option<mpi::SecretKeyChecksum>, ciphertext: Box<[u8]>) -> Self { - Self::new_raw(s2k, algo, Ok(ciphertext)) + Self::new_raw(s2k, algo, checksum, Ok(ciphertext)) } /// Creates a new encrypted key object. pub(crate) fn new_raw(s2k: S2K, algo: SymmetricAlgorithm, + checksum: Option<mpi::SecretKeyChecksum>, ciphertext: std::result::Result<Box<[u8]>, Box<[u8]>>) -> Self { - Encrypted { s2k, algo, ciphertext } + Encrypted { s2k, algo, checksum, ciphertext } } /// Returns the key derivation mechanism. @@ -1459,6 +1464,12 @@ impl Encrypted { self.algo } + /// Returns the checksum method used to protect the encrypted + /// secret key material, if any. + pub fn checksum(&self) -> Option<mpi::SecretKeyChecksum> { + self.checksum + } + /// Returns the encrypted secret key material. /// /// If the [`S2K`] mechanism is not supported by Sequoia, this diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index 9adf0d01..2d333705 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -2120,7 +2120,7 @@ impl Key4<key::UnspecifiedParts, key::UnspecifiedRole> return php.fail("unsupported secret key encryption"); } // Encrypted, S2K & SHA-1 checksum - 254 => { + 254 | 255 => { let sk: SymmetricAlgorithm = php_try!(php.parse_u8("sym_algo")).into(); let s2k = php_try!(S2K::parse(&mut php)); let s2k_supported = s2k.is_supported(); @@ -2130,6 +2130,11 @@ impl Key4<key::UnspecifiedParts, key::UnspecifiedRole> crate::packet::key::Encrypted::new_raw( s2k, sk, + if s2k_usage == 254 { + Some(mpi::SecretKeyChecksum::SHA1) + } else { + Some(mpi::SecretKeyChecksum::Sum16) + }, if s2k_supported { Ok(cipher) } else { @@ -2137,10 +2142,6 @@ impl Key4<key::UnspecifiedParts, key::UnspecifiedRole> }, ).into() } - // Encrypted, S2K & mod 65536 checksum: unsupported - 255 => { - return php.fail("unsupported secret key encryption"); - } }; Some(sec) diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 032cc70b..0ebecf42 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -1831,7 +1831,13 @@ impl<P, R> Key4<P, R> })?, SecretKeyMaterial::Encrypted(ref e) => { // S2K usage. - write_byte(o, 254)?; + write_byte(o, match e.checksum() { + Some(SecretKeyChecksum::SHA1) => 254, + Some(SecretKeyChecksum::Sum16) => 255, + None => return Err(Error::InvalidOperation( + "In Key4 packets, encrypted secret keys must be \ + checksummed".into()).into()), + })?; write_byte(o, e.algo().into())?; e.s2k().serialize(o)?; o.write_all(e.raw_ciphertext())?; diff --git a/openpgp/src/serialize/cert.rs b/openpgp/src/serialize/cert.rs index 7489983e..177f9b8b 100644 --- a/openpgp/src/serialize/cert.rs +++ b/openpgp/src/serialize/cert.rs @@ -495,8 +495,13 @@ impl<'a> TSK<'a> { }; let key_with_stub = key.clone() .add_secret(key::SecretKeyMaterial::Encrypted( - key::Encrypted::new(stub, 0.into(), - vec![].into()))).0; + key::Encrypted::new( + stub, 0.into(), + // Mirrors more closely what GnuPG 2.1 + // does (oddly, GnuPG 1.4 emits 0xfe + // here). + Some(crate::crypto::mpi::SecretKeyChecksum::Sum16), + vec![].into()))).0; return match tag { Tag::PublicKey => crate::Packet::SecretKey(key_with_stub.into()) |