Skip to content

Commit 61bf2d9

Browse files
committed
Added AttributeName type to handle storage naming correctly
1 parent 3142867 commit 61bf2d9

14 files changed

Lines changed: 314 additions & 173 deletions
Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
use cipherstash_client::{credentials::{service_credentials::ServiceToken, Credentials}, encryption::{Encryption, EncryptionError}, zero_kms::EncryptedRecord};
22
use itertools::Itertools;
3-
use crate::{crypto::{attrs::flattened_protected_attributes::FlattenedKey, SealError}, encrypted_table::TableAttributes, traits::TableAttribute};
3+
use crate::{crypto::{attrs::flattened_protected_attributes::FlattenedAttrName, SealError}, encrypted_table::TableAttributes, traits::TableAttribute};
44

55
use super::FlattenedProtectedAttributes;
66

77
// TODO: Move this elsewhere
88
/// Represents a set of encrypted records that have not yet been normalized into an output type.
99
// TODO: Remove the Debug derive
1010
#[derive(Debug)]
11-
pub(crate) struct FlattenedEncryptedAttributes(Vec<EncryptedRecord>);
11+
pub(crate) struct FlattenedEncryptedAttributes {
12+
attrs: Vec<EncryptedRecord>,
13+
}
1214

1315
impl FlattenedEncryptedAttributes {
1416
pub(crate) fn with_capacity(capacity: usize) -> Self {
15-
Self(Vec::with_capacity(capacity))
17+
Self { attrs: Vec::with_capacity(capacity) }
1618
}
1719

1820
pub(crate) fn is_empty(&self) -> bool {
19-
self.0.is_empty()
21+
self.attrs.is_empty()
2022
}
2123

2224
// TODO: REmove this
2325
pub(crate) fn len(&self) -> usize {
24-
self.0.len()
26+
self.attrs.len()
2527
}
2628

2729
// TODO: Test this
@@ -30,10 +32,10 @@ impl FlattenedEncryptedAttributes {
3032
self,
3133
cipher: &Encryption<impl Credentials<Token = ServiceToken>>,
3234
) -> Result<FlattenedProtectedAttributes, SealError> {
33-
let descriptors = self.0.iter().map(|record| record.descriptor.clone()).collect_vec();
35+
let descriptors = self.attrs.iter().map(|record| record.descriptor.clone()).collect_vec();
3436

3537
cipher
36-
.decrypt(self.0.into_iter())
38+
.decrypt(self.attrs.into_iter())
3739
.await
3840
.map(|records| {
3941
records
@@ -48,24 +50,24 @@ impl FlattenedEncryptedAttributes {
4850
/// The descriptor is parsed into a [FlattenedKey] which is used to determine the key and subkey.
4951
/// If a subkey is present, the attribute is inserted to a map with the key and subkey.
5052
pub(crate) fn denormalize(self) -> Result<TableAttributes, SealError> {
51-
self.0
53+
self.attrs
5254
.into_iter()
5355
.map(|record| {
5456
record
5557
.to_vec()
56-
.map(|data| (FlattenedKey::parse(&record.descriptor), data))
58+
.map(|data| (FlattenedAttrName::parse(&record.descriptor), data))
5759
.map_err(EncryptionError::from)
5860
})
59-
.fold_ok(Ok(TableAttributes::new()), |acc, (flattened_key, bytes)| {
60-
let (key, subkey) = flattened_key.into_key_parts();
61+
.fold_ok(Ok(TableAttributes::new()), |acc, (flattened_attr_name, bytes)| {
62+
let (name, subkey) = flattened_attr_name.into_parts();
6163
if let Some(subkey) = subkey {
6264
acc
6365
.and_then(|mut acc| acc
64-
.try_insert_map(key, subkey, bytes)
66+
.try_insert_map(name, subkey, bytes)
6567
.map(|_| acc))
6668
} else {
6769
acc.map(|mut acc| {
68-
acc.insert(key, bytes);
70+
acc.insert(name, bytes);
6971
acc
7072
})
7173
}
@@ -74,36 +76,48 @@ impl FlattenedEncryptedAttributes {
7476

7577
// TODO: Test this
7678
/// Normalize the TableAttributes into a set of encrypted records.
77-
pub(crate) fn try_extend(&mut self, attributes: TableAttributes) {
78-
for (key, value) in attributes.into_iter() {
79+
/// An error will be returned if the TableAttributes contain an unsupported attribute type
80+
/// (only `Bytes` and `Map` are currently supported).
81+
///
82+
/// Bytes data is converted to an [EncryptedRecord] using [TableAttribute::as_encrypted_record]
83+
/// which validates that the descriptor matches the key and subkey.
84+
///
85+
/// This method is used during decrypt and load operations.
86+
pub(crate) fn try_extend(&mut self, attributes: TableAttributes, prefix: String) -> Result<(), SealError> {
87+
for (name, value) in attributes.into_iter() {
7988
match value {
8089
TableAttribute::Map(map) => {
8190
for (subkey, value) in map.into_iter() {
82-
let descriptor = FlattenedKey::from(key.as_str()).with_subkey(subkey);
83-
// TODO: This is where we check that attr names match the descriptor to prevent confused deputy attacks
84-
// TODO: The prefix is ideally included here while doing this check
85-
self.0.push(value.as_encrypted_record().unwrap()); // TODO: throw an error
91+
let attr_key = FlattenedAttrName::new(Some(prefix.clone()), name.clone()).with_subkey(subkey);
92+
// Load the bytes and check for a confused deputy attack
93+
let record = value.as_encrypted_record(&attr_key.descriptor())?;
94+
self.attrs.push(record);
8695
}
8796
}
8897
TableAttribute::Bytes(_) => {
89-
let descriptor = FlattenedKey::from(key.as_str());
90-
// TODO: Confused deputy attack check
91-
self.0.push(value.as_encrypted_record().unwrap()); // TODO: throw an error
98+
let attr_key = FlattenedAttrName::new(Some(prefix.clone()), name);
99+
// Load the bytes and check for a confused deputy attack
100+
let record = value.as_encrypted_record(&attr_key.descriptor())?;
101+
self.attrs.push(record);
102+
}
103+
_ => {
104+
Err(SealError::AssertionFailed("Unsupported attribute type".to_string()))?;
92105
}
93-
_ => todo!(), // TODO: throw an error
94106
}
95107
}
108+
109+
Ok(())
96110
}
97111
}
98112

99113
impl From<Vec<EncryptedRecord>> for FlattenedEncryptedAttributes {
100-
fn from(records: Vec<EncryptedRecord>) -> Self {
101-
Self(records)
114+
fn from(attrs: Vec<EncryptedRecord>) -> Self {
115+
Self { attrs }
102116
}
103117
}
104118

105119
impl FromIterator<EncryptedRecord> for FlattenedEncryptedAttributes {
106120
fn from_iter<T: IntoIterator<Item = EncryptedRecord>>(iter: T) -> Self {
107-
Self(iter.into_iter().collect())
121+
Self { attrs: iter.into_iter().collect() }
108122
}
109123
}

src/crypto/attrs/flattened_protected_attributes.rs

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{
22
flattened_encrypted_attributes::FlattenedEncryptedAttributes, normalized_protected_attributes::NormalizedKey
33
};
4-
use crate::crypto::SealError;
4+
use crate::{crypto::SealError, encrypted_table::AttributeName};
55
use cipherstash_client::{
66
credentials::{service_credentials::ServiceToken, Credentials},
77
encryption::{BytesWithDescriptor, Encryption, Plaintext},
@@ -35,7 +35,6 @@ impl FlattenedProtectedAttributes {
3535
chunk_into: usize,
3636
) -> Result<Vec<FlattenedEncryptedAttributes>, SealError> {
3737
let chunk_size = self.0.len() / chunk_into;
38-
println!("Encrypting all attributes, chunk into: {}, chunk_size = {}", chunk_into, chunk_size);
3938

4039
cipher
4140
.encrypt(self.0.into_iter())
@@ -68,11 +67,11 @@ impl FromIterator<(Plaintext, String)> for FlattenedProtectedAttributes {
6867
#[derive(PartialEq, Debug)]
6968
pub(crate) struct FlattenedProtectedAttribute {
7069
plaintext: Plaintext,
71-
key: FlattenedKey,
70+
key: FlattenedAttrName,
7271
}
7372

7473
impl FlattenedProtectedAttribute {
75-
pub(super) fn new(plaintext: impl Into<Plaintext>, key: impl Into<FlattenedKey>) -> Self {
74+
pub(super) fn new(plaintext: impl Into<Plaintext>, key: impl Into<FlattenedAttrName>) -> Self {
7675
Self {
7776
plaintext: plaintext.into(),
7877
key: key.into(),
@@ -85,7 +84,10 @@ impl FlattenedProtectedAttribute {
8584
(self.plaintext, normalized, subkey)
8685
}
8786

88-
fn descriptor(&self) -> String {
87+
/// Returns the name of the attribute used to store the plaintext in the encrypted table.
88+
/// This is used as the descriptor when encrypting the attribute and
89+
/// is intended to tie the encrypted value to its location in a table.
90+
fn storage_descriptor(&self) -> String {
8991
self.key.descriptor()
9092
}
9193
}
@@ -94,7 +96,7 @@ impl Into<BytesWithDescriptor> for FlattenedProtectedAttribute {
9496
fn into(self) -> BytesWithDescriptor {
9597
BytesWithDescriptor {
9698
bytes: self.plaintext.to_vec(),
97-
descriptor: self.descriptor(),
99+
descriptor: self.storage_descriptor(),
98100
}
99101
}
100102
}
@@ -105,17 +107,19 @@ impl Into<BytesWithDescriptor> for FlattenedProtectedAttribute {
105107
/// A Map would have a key and a subkey, while a scalar would only have a key.
106108
// TODO: Only implement Debug in tests
107109
#[derive(PartialEq, Hash, Eq, Clone, Debug)]
108-
pub(super) struct FlattenedKey {
110+
pub(super) struct FlattenedAttrName {
111+
// TODO: Use a Cow to avoid copies during decryption
112+
// We may also never set this to None in which can we can remove the Option
109113
prefix: Option<String>,
110-
key: String,
114+
name: AttributeName,
111115
subkey: Option<String>,
112116
}
113117

114-
impl FlattenedKey {
115-
pub(super) fn new(prefix: Option<String>, key: impl Into<String>) -> Self {
118+
impl FlattenedAttrName {
119+
pub(super) fn new(prefix: Option<String>, name: impl Into<AttributeName>) -> Self {
116120
Self {
117121
prefix,
118-
key: key.into(),
122+
name: name.into(),
119123
subkey: None,
120124
}
121125
}
@@ -126,18 +130,18 @@ impl FlattenedKey {
126130
/// Prefix is discarded as it is not needed after decryption.
127131
pub(super) fn normalize(self) -> (NormalizedKey, Option<String>) {
128132
match self.subkey {
129-
Some(_) => (NormalizedKey::new_map(self.key), self.subkey),
130-
None => (NormalizedKey::new_scalar(self.key), None),
133+
Some(_) => (NormalizedKey::new_map(self.name.as_external_name()), self.subkey),
134+
None => (NormalizedKey::new_scalar(self.name.as_external_name()), None),
131135
}
132136
}
133137

134138
// TODO: Rename this to try_parse
135139
/// Parse a descriptor into a [FlattenedKey].
136140
pub(super) fn parse(descriptor: &str) -> Self {
137-
fn split_subkey(prefix: Option<String>, key: &str) -> FlattenedKey {
141+
fn split_subkey(prefix: Option<String>, key: &str) -> FlattenedAttrName {
138142
match key.split_once(".") {
139-
None => FlattenedKey::new(prefix, key),
140-
Some((key, subkey)) => FlattenedKey::new(prefix, key).with_subkey(subkey),
143+
None => FlattenedAttrName::new(prefix, key),
144+
Some((key, subkey)) => FlattenedAttrName::new(prefix, key).with_subkey(subkey),
141145
}
142146
}
143147
match descriptor.split_once("/") {
@@ -153,40 +157,40 @@ impl FlattenedKey {
153157

154158
pub(crate) fn descriptor(&self) -> String {
155159
match (self.prefix.as_ref(), self.subkey.as_ref()) {
156-
(Some(prefix), Some(subkey)) => format!("{}/{}.{}", prefix, self.key, subkey),
157-
(Some(prefix), None) => format!("{}/{}", prefix, self.key),
158-
(None, Some(subkey)) => format!("{}.{}", self.key, subkey),
159-
(None, None) => self.key.to_string(),
160+
(Some(prefix), Some(subkey)) => format!("{}/{}.{}", prefix, self.name.as_stored_name(), subkey),
161+
(Some(prefix), None) => format!("{}/{}", prefix, self.name.as_stored_name()),
162+
(None, Some(subkey)) => format!("{}.{}", self.name.as_stored_name(), subkey),
163+
(None, None) => self.name.as_stored_name().to_string(),
160164
}
161165
}
162166

163167
/// Consume and return the parts of the key (not including the prefix).
164-
pub fn into_key_parts(self) -> (String, Option<String>) {
165-
(self.key, self.subkey)
168+
pub fn into_parts(self) -> (AttributeName, Option<String>) {
169+
(self.name, self.subkey)
166170
}
167171
}
168172

169173
// TODO: Change to TryFrom
170-
impl From<String> for FlattenedKey {
174+
impl From<String> for FlattenedAttrName {
171175
fn from(key: String) -> Self {
172176
Self::parse(key.as_str())
173177
}
174178
}
175179

176-
impl From<&str> for FlattenedKey {
180+
impl From<&str> for FlattenedAttrName {
177181
fn from(key: &str) -> Self {
178182
Self::parse(key)
179183
}
180184
}
181185

182-
impl From<(String, String)> for FlattenedKey {
186+
impl From<(String, String)> for FlattenedAttrName {
183187
fn from((prefix, key): (String, String)) -> Self {
184188
// TODO: Check that neither string is empty
185189
Self::new(Some(prefix), key)
186190
}
187191
}
188192

189-
impl From<(&str, &str)> for FlattenedKey {
193+
impl From<(&str, &str)> for FlattenedAttrName {
190194
fn from((prefix, key): (&str, &str)) -> Self {
191195
Self::new(Some(prefix.to_string()), key)
192196
}
@@ -198,37 +202,38 @@ mod tests {
198202

199203
#[test]
200204
fn test_flattened_key_from_string() {
201-
assert_eq!(FlattenedKey::new(None, "foo"), "foo".into());
205+
assert_eq!(FlattenedAttrName::new(None, "foo"), "foo".into());
202206
}
203207

204208
#[test]
205209
fn test_flattened_key_from_tuple() {
206210
assert_eq!(
207-
FlattenedKey::new(Some("prefix".to_string()), "foo"),
211+
FlattenedAttrName::new(Some("prefix".to_string()), "foo"),
208212
("prefix", "foo").into()
209213
);
210214
}
211215

216+
// TODO: Test that pk and sk are renamed to __pk and __sk respectively
212217
#[test]
213218
fn test_flattened_key_descriptor() {
214-
assert_eq!(FlattenedKey::new(None, "foo").descriptor(), "foo");
219+
assert_eq!(FlattenedAttrName::new(None, "foo").descriptor(), "foo");
215220
assert_eq!(
216-
FlattenedKey::new(Some("pref".to_string()), "foo").descriptor(),
221+
FlattenedAttrName::new(Some("pref".to_string()), "foo").descriptor(),
217222
"pref/foo"
218223
);
219224
assert_eq!(
220-
FlattenedKey::new(None, "foo").with_subkey("x").descriptor(),
225+
FlattenedAttrName::new(None, "foo").with_subkey("x").descriptor(),
221226
"foo.x"
222227
);
223228
assert_eq!(
224-
FlattenedKey::new(Some("pref".to_string()), "foo")
229+
FlattenedAttrName::new(Some("pref".to_string()), "foo")
225230
.with_subkey("x")
226231
.descriptor(),
227232
"pref/foo.x"
228233
);
229234
}
230235

231-
// TODO: Test normalize
236+
// TODO: Test normalize the FlattenedAttrName
232237

233238
#[test]
234239
fn test_into_iter() {
@@ -319,9 +324,9 @@ mod tests {
319324

320325
#[test]
321326
fn test_flattened_key_parse() {
322-
assert_eq!(FlattenedKey::parse("key"), "key".into());
323-
assert_eq!(FlattenedKey::parse("prefix/key"), ("prefix", "key").into());
324-
assert_eq!(FlattenedKey::parse("key.subkey"), FlattenedKey::from("key").with_subkey("subkey"));
325-
assert_eq!(FlattenedKey::parse("prefix/key.subkey"), FlattenedKey::from(("prefix", "key")).with_subkey("subkey"));
327+
assert_eq!(FlattenedAttrName::parse("key"), "key".into());
328+
assert_eq!(FlattenedAttrName::parse("prefix/key"), ("prefix", "key").into());
329+
assert_eq!(FlattenedAttrName::parse("key.subkey"), FlattenedAttrName::from("key").with_subkey("subkey"));
330+
assert_eq!(FlattenedAttrName::parse("prefix/key.subkey"), FlattenedAttrName::from(("prefix", "key")).with_subkey("subkey"));
326331
}
327332
}

0 commit comments

Comments
 (0)