Skip to content

Commit 57e56e5

Browse files
committed
refactor(spill): name the AEAD tag length constant
Replace the repeated literal 16 with SPILL_TAG_LEN, and read the tag into a fixed-size array by copying instead of a fallible slice conversion, since the split is already exact by construction.
1 parent 0f6a1a2 commit 57e56e5

1 file changed

Lines changed: 16 additions & 9 deletions

File tree

src/txn/write/spill.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ use super::txn::WriteTxn;
1717
pub struct ScratchOffset(u64);
1818

1919
/// Metadata for one ciphertext chunk in the per-txn spill scratch file.
20+
/// Length of the AEAD tag appended to every spill frame.
21+
pub(crate) const SPILL_TAG_LEN: usize = 16;
22+
2023
/// Stored in memory only; the tmp file is discarded at commit/abort.
2124
#[derive(Clone)]
2225
pub(crate) struct SpillSegmentMeta {
2326
/// Byte offset of this chunk (ciphertext body start) in the tmp file.
2427
pub offset: u64,
2528
/// Length of the original plaintext in bytes.
2629
pub plaintext_len: u32,
27-
/// Length of ciphertext body (without the 16-byte tag) in bytes.
28-
/// Total on-disk size for this chunk = `ciphertext_len + 16`.
30+
/// Length of ciphertext body (without the AEAD tag) in bytes. Total
31+
/// on-disk size for this chunk = `ciphertext_len + SPILL_TAG_LEN`.
2932
pub ciphertext_len: u32,
3033
/// The 12-byte nonce used to encrypt this chunk, stored verbatim so we
3134
/// can reconstruct a `Nonce` on read without an additional lookup.
@@ -106,8 +109,8 @@ impl<V: Vfs + Clone> SpillScope<'_, '_, V> {
106109
.expect("derived above")
107110
.encrypt(&nonce, &aad, &mut body)?;
108111

109-
// ciphertext body + 16-byte tag.
110-
let pers_len = body.len() as u64 + 16;
112+
// ciphertext body + AEAD tag.
113+
let pers_len = body.len() as u64 + SPILL_TAG_LEN as u64;
111114
let new_total = self.txn.spill_bytes_used.saturating_add(pers_len);
112115
if new_total > limit {
113116
return Err(PagedbError::quota(
@@ -124,6 +127,8 @@ impl<V: Vfs + Clone> SpillScope<'_, '_, V> {
124127
let mut file = self.txn.db.vfs.open(&path, OpenMode::CreateOrOpen).await?;
125128
let body_offset = self.txn.spill_bytes_used;
126129
let ciphertext_len = body.len();
130+
// Body and tag are one contiguous frame: a handle must never be
131+
// returned for a file holding only half of it.
127132
body.extend_from_slice(&tag);
128133
write_all_at(&mut file, body_offset, &body).await?;
129134
file.sync().await?;
@@ -162,15 +167,17 @@ impl<V: Vfs + Clone> SpillScope<'_, '_, V> {
162167
let mut file = self.txn.db.vfs.open(path, OpenMode::Read).await?;
163168

164169
let body_len = meta.ciphertext_len as usize;
170+
// `usize` is 32-bit on wasm32, where a maximal `ciphertext_len` plus
171+
// the tag genuinely overflows it.
165172
let frame_len = body_len
166-
.checked_add(16)
173+
.checked_add(SPILL_TAG_LEN)
167174
.ok_or_else(|| PagedbError::arithmetic_overflow("spill frame length"))?;
168175
let mut frame = vec![0u8; frame_len];
169176
read_exact_at(&mut file, meta.offset, &mut frame).await?;
177+
// The split is exact by construction, so the tail is the whole tag.
170178
let (body, tag_bytes) = frame.split_at_mut(body_len);
171-
let tag: &[u8; 16] = (&*tag_bytes)
172-
.try_into()
173-
.map_err(|_| PagedbError::ChecksumFailure)?;
179+
let mut tag = [0u8; SPILL_TAG_LEN];
180+
tag.copy_from_slice(tag_bytes);
174181

175182
let cipher = self.txn.spill_cipher_readonly()?;
176183
let nonce = Nonce::from_bytes(meta.nonce_bytes);
@@ -187,7 +194,7 @@ impl<V: Vfs + Clone> SpillScope<'_, '_, V> {
187194
segment_id: self.txn.db.file_id,
188195
});
189196

190-
cipher.decrypt(&nonce, &aad, body, tag)?;
197+
cipher.decrypt(&nonce, &aad, body, &tag)?;
191198
frame.truncate(meta.plaintext_len as usize);
192199
Ok(frame)
193200
}

0 commit comments

Comments
 (0)