use crate::crypto::KeyRing;
use crate::error::{Result, WalError};
use crate::preamble::{PREAMBLE_SIZE, SegmentPreamble};
use crate::record::{RecordHeader, WalRecord};
struct SegmentAad {
epoch: [u8; 4],
preamble_bytes: [u8; PREAMBLE_SIZE],
}
pub struct SegmentDecryptor {
ring: Option<KeyRing>,
aad: Option<SegmentAad>,
}
impl SegmentDecryptor {
pub fn new(preamble: Option<&SegmentPreamble>, ring: Option<&KeyRing>) -> Self {
Self {
ring: ring.cloned(),
aad: preamble.map(|p| SegmentAad {
epoch: *p.epoch(),
preamble_bytes: p.to_bytes(),
}),
}
}
pub fn decrypt_record(&self, record: WalRecord) -> Result<WalRecord> {
if !record.is_encrypted() {
return Ok(record);
}
let (ring, aad) = self.require_keys(record.header.lsn)?;
record.into_decrypted(&aad.epoch, Some(&aad.preamble_bytes), Some(ring))
}
pub fn decrypt_payload(&self, header: &RecordHeader, payload: Vec<u8>) -> Result<Vec<u8>> {
let record = WalRecord {
header: *header,
payload,
};
if !record.is_encrypted() {
return Ok(record.payload);
}
let (ring, aad) = self.require_keys(header.lsn)?;
record.decrypt_payload_ring(&aad.epoch, Some(&aad.preamble_bytes), Some(ring))
}
fn require_keys(&self, lsn: u64) -> Result<(&KeyRing, &SegmentAad)> {
const SITE: &str = "WAL segment replay";
let ring = match self.ring.as_ref() {
Some(ring) => ring,
None => {
let err = WalError::EncryptedRecordWithoutKey { lsn, context: SITE };
crate::diag::encrypted_record_without_key(&err, lsn, SITE);
return Err(err);
}
};
let aad = self.aad.as_ref().ok_or_else(|| WalError::CorruptRecord {
lsn,
detail: "encrypted record in a segment with no preamble — the epoch \
needed to decrypt it is unrecoverable"
.into(),
})?;
Ok((ring, aad))
}
}