Skip to content

Commit 28976f4

Browse files
authored
Merge pull request #40 from tnull/2025-08-include-key-aad
Allow to set `aad` in `StorableBuilder::{build,desconstruct}`
2 parents e2aab92 + 6873274 commit 28976f4

File tree

1 file changed

+48
-15
lines changed

1 file changed

+48
-15
lines changed

src/util/storable_builder.rs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ use std::io;
66
use std::io::{Error, ErrorKind};
77

88
/// [`StorableBuilder`] is a utility to build and deconstruct [`Storable`] objects.
9+
///
910
/// It provides client-side Encrypt-then-MAC using ChaCha20-Poly1305.
1011
pub struct StorableBuilder<T: EntropySource> {
11-
data_encryption_key: [u8; 32],
1212
entropy_source: T,
1313
}
1414

1515
impl<T: EntropySource> StorableBuilder<T> {
1616
/// Constructs a new instance.
17-
pub fn new(data_encryption_key: [u8; 32], entropy_source: T) -> StorableBuilder<T> {
18-
Self { data_encryption_key, entropy_source }
17+
pub fn new(entropy_source: T) -> StorableBuilder<T> {
18+
Self { entropy_source }
1919
}
2020
}
2121

@@ -34,18 +34,21 @@ const CHACHA20_CIPHER_NAME: &'static str = "ChaCha20Poly1305";
3434
impl<T: EntropySource> StorableBuilder<T> {
3535
/// Creates a [`Storable`] that can be serialized and stored as `value` in [`PutObjectRequest`].
3636
///
37-
/// Uses ChaCha20 for encrypting `input` and Poly1305 for generating a mac/tag.
37+
/// Uses ChaCha20 for encrypting `input` and Poly1305 for generating a mac/tag with associated
38+
/// data `aad` (usually the storage key).
3839
///
3940
/// Refer to docs on [`Storable`] for more information.
4041
///
4142
/// [`PutObjectRequest`]: crate::types::PutObjectRequest
42-
pub fn build(&self, input: Vec<u8>, version: i64) -> Storable {
43+
pub fn build(
44+
&self, input: Vec<u8>, version: i64, data_encryption_key: &[u8; 32], aad: &[u8],
45+
) -> Storable {
4346
let mut nonce = vec![0u8; 12];
4447
self.entropy_source.fill_bytes(&mut nonce[4..]);
4548

4649
let mut data_blob = PlaintextBlob { value: input, version }.encode_to_vec();
4750

48-
let mut cipher = ChaCha20Poly1305::new(&self.data_encryption_key, &nonce, &[]);
51+
let mut cipher = ChaCha20Poly1305::new(data_encryption_key, &nonce, aad);
4952
let mut tag = vec![0u8; 16];
5053
cipher.encrypt_inplace(&mut data_blob, &mut tag);
5154
Storable {
@@ -62,10 +65,14 @@ impl<T: EntropySource> StorableBuilder<T> {
6265
/// corresponding version as stored at the time of [`PutObjectRequest`].
6366
///
6467
/// [`PutObjectRequest`]: crate::types::PutObjectRequest
65-
pub fn deconstruct(&self, mut storable: Storable) -> io::Result<(Vec<u8>, i64)> {
66-
let encryption_metadata = storable.encryption_metadata.unwrap();
68+
pub fn deconstruct(
69+
&self, mut storable: Storable, data_encryption_key: &[u8; 32], aad: &[u8],
70+
) -> io::Result<(Vec<u8>, i64)> {
71+
let encryption_metadata = storable
72+
.encryption_metadata
73+
.ok_or_else(|| Error::new(ErrorKind::InvalidData, "Invalid Metadata"))?;
6774
let mut cipher =
68-
ChaCha20Poly1305::new(&self.data_encryption_key, &encryption_metadata.nonce, &[]);
75+
ChaCha20Poly1305::new(data_encryption_key, &encryption_metadata.nonce, aad);
6976

7077
cipher
7178
.decrypt_inplace(&mut storable.data, encryption_metadata.tag.borrow())
@@ -97,16 +104,42 @@ mod tests {
97104
let test_entropy_provider = TestEntropyProvider;
98105
let mut data_key = [0u8; 32];
99106
test_entropy_provider.fill_bytes(&mut data_key);
100-
let storable_builder = StorableBuilder {
101-
data_encryption_key: data_key,
102-
entropy_source: test_entropy_provider,
103-
};
107+
let storable_builder = StorableBuilder::new(test_entropy_provider);
104108
let expected_data = b"secret".to_vec();
105109
let expected_version = 8;
106-
let storable = storable_builder.build(expected_data.clone(), expected_version);
110+
let aad = b"A";
111+
let storable =
112+
storable_builder.build(expected_data.clone(), expected_version, &data_key, aad);
107113

108-
let (actual_data, actual_version) = storable_builder.deconstruct(storable).unwrap();
114+
let (actual_data, actual_version) =
115+
storable_builder.deconstruct(storable, &data_key, aad).unwrap();
109116
assert_eq!(actual_data, expected_data);
110117
assert_eq!(actual_version, expected_version);
111118
}
119+
120+
#[test]
121+
fn decrypt_key_mismatch_fails() {
122+
let test_entropy_provider = TestEntropyProvider;
123+
let mut data_key = [0u8; 32];
124+
test_entropy_provider.fill_bytes(&mut data_key);
125+
let storable_builder = StorableBuilder::new(test_entropy_provider);
126+
127+
let expected_data_a = b"secret_a".to_vec();
128+
let expected_version_a = 8;
129+
let aad_a = b"A";
130+
let storable_a =
131+
storable_builder.build(expected_data_a.clone(), expected_version_a, &data_key, aad_a);
132+
133+
let expected_data_b = b"secret_b".to_vec();
134+
let expected_version_b = 8;
135+
let aad_b = b"B";
136+
let storable_b =
137+
storable_builder.build(expected_data_b.clone(), expected_version_b, &data_key, aad_b);
138+
139+
let (actual_data, actual_version) =
140+
storable_builder.deconstruct(storable_a, &data_key, aad_a).unwrap();
141+
assert_eq!(actual_data, expected_data_a);
142+
assert_eq!(actual_version, expected_version_a);
143+
assert!(storable_builder.deconstruct(storable_b, &data_key, aad_a).is_err());
144+
}
112145
}

0 commit comments

Comments
 (0)