From 903d33f448c1d89db431017728afaf8ed7da01b9 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 20 Dec 2018 17:18:57 +0100 Subject: openpgp: Implement encrypting secret keys. --- openpgp/src/packet/key.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'openpgp') diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs index 57dcc8b6..a9240744 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key.rs @@ -342,6 +342,50 @@ impl SecretKey { Ok(()) } + /// Encrypts this secret key using `password`. + pub fn encrypt(&self, password: &Password) + -> Result<(S2K, SymmetricAlgorithm, Box<[u8]>)> { + use std::io::Write; + use crypto::symmetric::Encryptor; + use nettle::Yarrow; + + match self { + &SecretKey::Encrypted { .. } => + Err(Error::InvalidOperation("Key is already encrypted".into()) + .into()), + &SecretKey::Unencrypted { ref mpis } => { + let s2k = S2K::default(); + let cipher = SymmetricAlgorithm::AES256; + let key = s2k.derive_key(password, cipher.key_size()?)?; + + // Ciphertext is preceded by a random block. + let mut trash = vec![0u8; cipher.block_size()?]; + Yarrow::default().random(&mut trash); + + let mut esk = Vec::new(); + { + let mut encryptor = Encryptor::new(cipher, &key, &mut esk)?; + encryptor.write_all(&trash)?; + mpis.serialize_chksumd(&mut encryptor)?; + } + + Ok((s2k, cipher, esk.into_boxed_slice())) + }, + } + } + + /// Encrypts this secret key using `password`. + pub fn encrypt_in_place(&mut self, password: &Password) -> Result<()> { + let (s2k, cipher, esk) = self.encrypt(password)?; + *self = SecretKey::Encrypted { + s2k: s2k, + algorithm: cipher, + ciphertext: esk, + }; + + Ok(()) + } + /// Returns true if this secret key is encrypted. pub fn is_encrypted(&self) -> bool { match self { @@ -464,4 +508,27 @@ mod tests { assert_eq!(sk, sk_); } } + + #[test] + fn secret_encryption_roundtrip() { + for &pk_algo in &[PublicKeyAlgorithm::RSAEncryptSign, + PublicKeyAlgorithm::EdDSA, + PublicKeyAlgorithm::ECDH] { + let key = Key::new(pk_algo).unwrap(); + assert!(! key.secret().unwrap().is_encrypted()); + + let password = Password::from("foobarbaz"); + let mut encrypted_key = key.clone(); + + encrypted_key.secret_mut().unwrap() + .encrypt_in_place(&password).unwrap(); + assert!(encrypted_key.secret().unwrap().is_encrypted()); + + encrypted_key.secret_mut().unwrap() + .decrypt_in_place(pk_algo, &password).unwrap(); + assert!(! key.secret().unwrap().is_encrypted()); + assert_eq!(key, encrypted_key); + assert_eq!(key.secret(), encrypted_key.secret()); + } + } } -- cgit v1.2.3