summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-03-07 11:31:01 +0100
committerJustus Winter <justus@sequoia-pgp.org>2023-03-07 11:52:54 +0100
commitce18f70b90a56439230442b02afb21838bac3e5d (patch)
treeb3137b9028c6db0c9ab26e6c159edbb7925a3af2 /openpgp/src/crypto
parent7917e31af5f89e3ea7474f3bdd386607c5fccb16 (diff)
openpgp: Prevent leaking secrets accessing encrypted memory.
- Track the length of the plaintext data. This makes it possible to use unchunked AEAD and decrypt the data without copying it into a growing vector. Also, avoid io::copy, as this leaks secrets into its buffer.
Diffstat (limited to 'openpgp/src/crypto')
-rw-r--r--openpgp/src/crypto/mem.rs24
1 files changed, 14 insertions, 10 deletions
diff --git a/openpgp/src/crypto/mem.rs b/openpgp/src/crypto/mem.rs
index 8a517ac0..dd63dfbd 100644
--- a/openpgp/src/crypto/mem.rs
+++ b/openpgp/src/crypto/mem.rs
@@ -230,6 +230,7 @@ impl fmt::Debug for Protected {
pub struct Encrypted {
ciphertext: Protected,
salt: [u8; 32],
+ plaintext_len: usize,
}
assert_send_and_sync!(Encrypted);
@@ -262,7 +263,7 @@ const ENCRYPTED_MEMORY_PAGE_SIZE: usize = 4096;
///
/// Code outside of it cannot access it, because `PREKEY` is private.
mod has_access_to_prekey {
- use std::io::{self, Write};
+ use std::io::{self, Read, Write};
use buffered_reader::Memory;
use crate::types::{AEADAlgorithm, HashAlgorithm, SymmetricAlgorithm};
use crate::crypto::{aead, SessionKey};
@@ -297,7 +298,7 @@ mod has_access_to_prekey {
.expect("Mandatory algorithm unsupported");
ctx.update(salt);
PREKEY.iter().for_each(|page| ctx.update(page));
- let mut sk: SessionKey = vec![0; 256/8].into();
+ let mut sk: SessionKey = Protected::new(256/8).into();
let _ = ctx.digest(&mut sk);
sk
}
@@ -306,6 +307,7 @@ mod has_access_to_prekey {
pub fn new(p: Protected) -> Self {
if DANGER_DISABLE_ENCRYPTED_MEMORY {
return Encrypted {
+ plaintext_len: p.len(),
ciphertext: p,
salt: Default::default(),
};
@@ -313,22 +315,25 @@ mod has_access_to_prekey {
let mut salt = [0; 32];
crate::crypto::random(&mut salt);
- let mut ciphertext = Vec::new();
+ let mut ciphertext = Protected::new(
+ p.len() + 2 * AEAD_ALGO.digest_size().expect("supported"));
+
{
let mut encryptor =
aead::Encryptor::new(SYMMETRIC_ALGO,
AEAD_ALGO,
- 4096,
+ p.len(),
CounterSchedule::default(),
Self::sealing_key(&salt),
- &mut ciphertext)
+ io::Cursor::new(&mut ciphertext[..]))
.expect("Mandatory algorithm unsupported");
encryptor.write_all(&p).unwrap();
encryptor.finish().unwrap();
}
Encrypted {
- ciphertext: ciphertext.into(),
+ plaintext_len: p.len(),
+ ciphertext,
salt,
}
}
@@ -344,21 +349,20 @@ mod has_access_to_prekey {
let ciphertext =
Memory::with_cookie(&self.ciphertext, Default::default());
- let mut plaintext = Vec::new();
+ let mut plaintext = Protected::new(self.plaintext_len);
let mut decryptor =
aead::Decryptor::from_buffered_reader(
SYMMETRIC_ALGO,
AEAD_ALGO,
- 4096,
+ self.plaintext_len,
CounterSchedule::default(),
Self::sealing_key(&self.salt),
Box::new(ciphertext))
.expect("Mandatory algorithm unsupported");
// Be careful not to leak partially decrypted plain text.
- let r = io::copy(&mut decryptor, &mut plaintext);
- let plaintext: Protected = plaintext.into();
+ let r = decryptor.read_exact(&mut plaintext);
if r.is_err() {
drop(plaintext); // Securely erase partial plaintext.
panic!("Encrypted memory modified or corrupted");