Skip to content

Commit 511256a

Browse files
author
Bennett Hardwick
committed
Change to prepared record
1 parent e6d6b1f commit 511256a

File tree

7 files changed

+225
-114
lines changed

7 files changed

+225
-114
lines changed

cipherstash-dynamodb-derive/src/encryptable.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ pub(crate) fn derive_encryptable(input: DeriveInput) -> Result<TokenStream, syn:
3737
let attr_ident = format_ident!("{attr}");
3838

3939
quote! {
40-
.add_protected(#attr, |x| cipherstash_dynamodb::traits::Plaintext::from(x.#attr_ident.to_owned()))
40+
unsealed.add_protected(#attr, cipherstash_dynamodb::traits::Plaintext::from(self.#attr_ident.to_owned()));
4141
}
4242
})
4343
.chain(plaintext_attributes.iter().map(|attr| {
4444
let attr_ident = format_ident!("{attr}");
4545

4646
quote! {
47-
.add_plaintext(#attr, |x| cipherstash_dynamodb::traits::TableAttribute::from(x.#attr_ident.clone()))
47+
unsealed.add_unprotected(#attr, cipherstash_dynamodb::traits::TableAttribute::from(self.#attr_ident.clone()));
4848
}
4949
}));
5050

@@ -70,9 +70,12 @@ pub(crate) fn derive_encryptable(input: DeriveInput) -> Result<TokenStream, syn:
7070
}
7171

7272
#[allow(clippy::needless_question_mark)]
73-
fn into_sealer(self) -> Result<cipherstash_dynamodb::crypto::Sealer<Self>, cipherstash_dynamodb::crypto::SealError> {
74-
Ok(cipherstash_dynamodb::crypto::Sealer::new_with_descriptor(self, <Self as cipherstash_dynamodb::traits::Encryptable>::type_name())
75-
#(#into_unsealed_impl?)*)
73+
fn into_unsealed(self) -> cipherstash_dynamodb::crypto::Unsealed {
74+
let mut unsealed = cipherstash_dynamodb::crypto::Unsealed::new_with_descriptor(<Self as cipherstash_dynamodb::traits::Encryptable>::type_name());
75+
76+
#(#into_unsealed_impl)*
77+
78+
unsealed
7679
}
7780
}
7881
};

src/crypto/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ mod sealed;
33
mod sealer;
44
mod unsealed;
55

6+
use std::borrow::Cow;
7+
68
use crate::{
79
traits::{
810
Encryptable, PrimaryKeyError, PrimaryKeyParts, ReadConversionError, Searchable,
@@ -66,8 +68,12 @@ pub fn format_term_key(
6668
format!("{sort_key}#{index_name}#{index_type}#{counter}")
6769
}
6870

69-
pub(crate) fn all_index_keys<E: Searchable + Encryptable>(sort_key: &str) -> Vec<String> {
70-
E::protected_indexes()
71+
pub(crate) fn all_index_keys<'a>(
72+
sort_key: &str,
73+
protected_indexes: impl AsRef<[(Cow<'a, str>, IndexType)]>,
74+
) -> Vec<String> {
75+
protected_indexes
76+
.as_ref()
7177
.iter()
7278
.flat_map(|(index_name, index_type)| {
7379
(0..)

src/crypto/sealer.rs

Lines changed: 118 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
2-
b64_encode, encrypt_primary_key, format_term_key, hmac, SealError, SealedTableEntry, Unsealed,
3-
MAX_TERMS_PER_INDEX,
2+
b64_encode, encrypt_primary_key, format_term_key, hmac, SealError, SealedTableEntry,
3+
UnsealSpec, Unsealed, MAX_TERMS_PER_INDEX,
44
};
55
use crate::{
66
encrypted_table::{TableAttribute, TableEntry},
@@ -9,15 +9,32 @@ use crate::{
99
};
1010
use cipherstash_client::{
1111
credentials::{service_credentials::ServiceToken, Credentials},
12-
encryption::{compound_indexer::CompoundIndex, Encryption, IndexTerm, Plaintext},
12+
encryption::{
13+
compound_indexer::{ComposableIndex, ComposablePlaintext, CompoundIndex},
14+
Encryption, IndexTerm, Plaintext,
15+
},
1316
};
1417
use itertools::Itertools;
1518
use std::{borrow::Cow, collections::HashMap, ops::Deref};
1619

1720
/// Builder pattern for sealing a record of type, `T`.
18-
pub struct Sealer<T> {
19-
inner: T,
20-
unsealed: Unsealed,
21+
pub struct Sealer {
22+
pub(crate) pk: String,
23+
pub(crate) sk: String,
24+
25+
pub(crate) is_pk_encrypted: bool,
26+
pub(crate) is_sk_encrypted: bool,
27+
28+
pub(crate) type_name: Cow<'static, str>,
29+
30+
pub(crate) unsealed_indexes: Vec<(
31+
ComposablePlaintext,
32+
Box<dyn ComposableIndex>,
33+
Cow<'static, str>,
34+
IndexType,
35+
)>,
36+
37+
pub(crate) unsealed: Unsealed,
2138
}
2239

2340
struct Term {
@@ -82,94 +99,110 @@ impl Sealed {
8299
}
83100
}
84101

85-
impl<T> Sealer<T> {
86-
pub fn new(inner: T) -> Self {
87-
Self {
88-
inner,
89-
unsealed: Unsealed::new(),
90-
}
91-
}
102+
impl Sealer {
103+
// pub fn new<T>(inner: T) -> Self {
104+
// Self {
105+
// inner,
106+
// unsealed: Unsealed::new(),
107+
// }
108+
// }
109+
110+
// pub fn new_with_descriptor<T>(inner: T, descriptor: impl Into<String>) -> Self {
111+
// Self {
112+
// inner,
113+
// unsealed: Unsealed::new_with_descriptor(descriptor),
114+
// }
115+
// }
116+
117+
// pub fn add_protected<F>(mut self, name: impl Into<String>, f: F) -> Result<Self, SealError>
118+
// where
119+
// F: FnOnce(&T) -> Plaintext,
120+
// {
121+
// let name: String = name.into();
122+
123+
// self.unsealed.add_protected(name, f(&self.inner));
124+
// Ok(self)
125+
// }
126+
127+
// pub fn add_plaintext<F>(mut self, name: impl Into<String>, f: F) -> Result<Self, SealError>
128+
// where
129+
// F: FnOnce(&T) -> TableAttribute,
130+
// {
131+
// let name: String = name.into();
132+
// self.unsealed.add_unprotected(name, f(&self.inner));
133+
// Ok(self)
134+
// }
135+
136+
pub(crate) async fn seal_all<'a>(
137+
records: impl IntoIterator<Item = Sealer>,
138+
protected_attributes: impl AsRef<[Cow<'a, str>]>,
139+
cipher: &Encryption<impl Credentials<Token = ServiceToken>>,
140+
term_length: usize,
141+
) -> Result<Vec<Sealed>, SealError> {
142+
let records = records.into_iter();
143+
let records_len = records.size_hint().1.unwrap_or(1);
92144

93-
pub fn new_with_descriptor(inner: T, descriptor: impl Into<String>) -> Self {
94-
Self {
95-
inner,
96-
unsealed: Unsealed::new_with_descriptor(descriptor),
97-
}
98-
}
145+
let protected_attributes = protected_attributes.as_ref();
99146

100-
pub fn add_protected<F>(mut self, name: impl Into<String>, f: F) -> Result<Self, SealError>
101-
where
102-
F: FnOnce(&T) -> Plaintext,
103-
{
104-
let name: String = name.into();
147+
let mut protected = Vec::with_capacity(records_len * protected_attributes.len());
148+
let mut table_entries = Vec::with_capacity(records_len);
105149

106-
self.unsealed.add_protected(name, f(&self.inner));
107-
Ok(self)
108-
}
150+
for mut record in records {
151+
let mut pk = record.pk;
152+
let mut sk = record.sk;
109153

110-
pub fn add_plaintext<F>(mut self, name: impl Into<String>, f: F) -> Result<Self, SealError>
111-
where
112-
F: FnOnce(&T) -> TableAttribute,
113-
{
114-
let name: String = name.into();
115-
self.unsealed.add_unprotected(name, f(&self.inner));
116-
Ok(self)
117-
}
154+
if record.is_pk_encrypted {
155+
pk = b64_encode(hmac(&pk, None, cipher)?);
156+
}
118157

119-
pub(crate) async fn seal_all<S: Searchable + Identifiable>(
120-
records: impl AsRef<[Sealer<S>]>,
121-
cipher: &Encryption<impl Credentials<Token = ServiceToken>>,
122-
term_length: usize,
123-
) -> Result<Vec<Sealed>, SealError> {
124-
let records = records.as_ref();
125-
let protected_attributes = S::protected_attributes();
126-
let protected_indexes = S::protected_indexes();
158+
if record.is_sk_encrypted {
159+
sk = b64_encode(hmac(&sk, Some(pk.as_str()), cipher)?);
160+
}
127161

128-
let mut protected = Vec::with_capacity(records.len() * protected_attributes.len());
129-
let mut table_entries = Vec::with_capacity(records.len());
162+
let type_name = &record.type_name;
130163

131-
for record in records.iter() {
132-
let PrimaryKeyParts { pk, sk } = encrypt_primary_key::<S>(
133-
record.inner.get_primary_key(),
134-
&S::type_name(),
135-
S::sort_key_prefix().as_deref(),
136-
cipher,
137-
)?;
164+
// let PrimaryKeyParts { pk, sk } = encrypt_primary_key::<S>(
165+
// record.inner.get_primary_key(),
166+
// &S::type_name(),
167+
// S::sort_key_prefix().as_deref(),
168+
// cipher,
169+
// )?;
138170

139171
for attr in protected_attributes.iter() {
140-
protected.push(record.unsealed.protected_with_descriptor(attr)?);
172+
protected.push(record.unsealed.remove_protected_with_descriptor(attr)?);
141173
}
142174

143-
let terms: Vec<(&Cow<'_, str>, IndexType, Vec<u8>)> = protected_indexes
144-
.iter()
145-
.map(|(index_name, index_type)| {
146-
record
147-
.inner
148-
.attribute_for_index(index_name, *index_type)
149-
.and_then(|attr| {
150-
S::index_by_name(index_name, *index_type)
151-
.map(|index| (attr, index, index_name, index_type))
152-
})
153-
.ok_or(SealError::MissingAttribute(index_name.to_string()))
154-
.and_then(|(attr, index, index_name, index_type)| {
155-
let term = cipher.compound_index(
156-
&CompoundIndex::new(index),
157-
attr,
158-
Some(format!("{}#{}", S::type_name(), index_name)),
159-
term_length,
160-
)?;
161-
162-
Ok::<_, SealError>((index_name, index_type, term))
163-
})
175+
let terms: Vec<(Cow<'_, str>, IndexType, Vec<u8>)> = record
176+
.unsealed_indexes
177+
.into_iter()
178+
// .iter()
179+
// .map(|(index_name, index_type)| {
180+
// record
181+
// .inner
182+
// .attribute_for_index(index_name, *index_type)
183+
// .and_then(|attr| {
184+
// S::index_by_name(index_name, *index_type)
185+
// .map(|index| (attr, index, index_name, index_type))
186+
// })
187+
// .ok_or(SealError::MissingAttribute(index_name.to_string()))
188+
.map(|(attr, index, index_name, index_type)| {
189+
let term = cipher.compound_index(
190+
&CompoundIndex::new(index),
191+
attr,
192+
Some(format!("{}#{}", type_name, index_name)),
193+
term_length,
194+
)?;
195+
196+
Ok::<_, SealError>((index_name, index_type, term))
164197
})
165198
.map(|index_term| match index_term {
166199
Ok((index_name, index_type, IndexTerm::Binary(x))) => {
167-
Ok(vec![(index_name, *index_type, x)])
200+
Ok(vec![(index_name, index_type, x)])
168201
}
169202
Ok((index_name, index_type, IndexTerm::BinaryVec(x))) => Ok(x
170203
.into_iter()
171204
.take(MAX_TERMS_PER_INDEX)
172-
.map(|x| (index_name, *index_type, x))
205+
.map(|x| (index_name.clone(), index_type, x))
173206
.collect()),
174207
_ => Err(SealError::InvalidCiphertext("Invalid index term".into())),
175208
})
@@ -183,7 +216,9 @@ impl<T> Sealer<T> {
183216
));
184217
}
185218

186-
let encrypted = cipher.encrypt(protected).await?;
219+
let encrypted = cipher
220+
.encrypt(protected.iter().map(|(a, b)| (a, b.as_str())))
221+
.await?;
187222

188223
for (encrypted, (_, attributes, _)) in encrypted
189224
.chunks_exact(protected_attributes.len())
@@ -207,7 +242,7 @@ impl<T> Sealer<T> {
207242
}
208243
}
209244

210-
let mut output = Vec::with_capacity(records.len());
245+
let mut output = Vec::with_capacity(table_entries.len());
211246

212247
for (PrimaryKeyParts { pk, sk }, attributes, terms) in table_entries.into_iter() {
213248
let terms = terms
@@ -235,16 +270,13 @@ impl<T> Sealer<T> {
235270
Ok(output)
236271
}
237272

238-
pub(crate) async fn seal<C>(
273+
pub(crate) async fn seal<'a>(
239274
self,
240-
cipher: &Encryption<C>,
275+
protected_attributes: impl AsRef<[Cow<'a, str>]>,
276+
cipher: &Encryption<impl Credentials<Token = ServiceToken>>,
241277
term_length: usize,
242-
) -> Result<Sealed, SealError>
243-
where
244-
C: Credentials<Token = ServiceToken>,
245-
T: Searchable + Identifiable,
246-
{
247-
let mut vec = Self::seal_all([self], cipher, term_length).await?;
278+
) -> Result<Sealed, SealError> {
279+
let mut vec = Self::seal_all([self], protected_attributes, cipher, term_length).await?;
248280

249281
if vec.len() != 1 {
250282
let actual = vec.len();

src/crypto/unsealed.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ pub struct Unsealed {
1515
}
1616

1717
impl Unsealed {
18-
pub(super) fn new() -> Self {
18+
pub fn new() -> Self {
1919
Self {
2020
descriptor: None,
2121
protected: Default::default(),
2222
unprotected: Default::default(),
2323
}
2424
}
2525

26-
pub(super) fn new_with_descriptor(descriptor: impl Into<String>) -> Self {
26+
pub fn new_with_descriptor(descriptor: impl Into<String>) -> Self {
2727
Self {
2828
descriptor: Some(descriptor.into()),
2929
protected: Default::default(),
@@ -47,30 +47,31 @@ impl Unsealed {
4747
.ok_or_else(|| SealError::MissingAttribute(name.to_string()))
4848
}
4949

50-
pub(super) fn add_protected(&mut self, name: impl Into<String>, plaintext: Plaintext) {
50+
pub fn add_protected(&mut self, name: impl Into<String>, plaintext: Plaintext) {
5151
let name = name.into();
5252
let descriptor = format!("{}/{}", self.descriptor.as_deref().unwrap_or(""), &name);
5353
self.protected.insert(name, (plaintext, descriptor));
5454
}
5555

56-
pub(super) fn add_unprotected(&mut self, name: impl Into<String>, attribute: TableAttribute) {
56+
pub fn add_unprotected(&mut self, name: impl Into<String>, attribute: TableAttribute) {
5757
self.unprotected.insert(name.into(), attribute);
5858
}
5959

6060
pub(crate) fn unprotected(&self) -> HashMap<String, TableAttribute> {
6161
self.unprotected.clone()
6262
}
6363

64-
/// Returns a reference to the protected value along with its descriptor.
65-
pub(crate) fn protected_with_descriptor(
66-
&self,
64+
/// Remove and return a protected value along with its descriptor.
65+
pub(crate) fn remove_protected_with_descriptor(
66+
&mut self,
6767
name: &str,
68-
) -> Result<(&Plaintext, &str), SealError> {
68+
) -> Result<(Plaintext, String), SealError> {
6969
let out = self
7070
.protected
71-
.get(name)
71+
.remove(name)
7272
.ok_or(SealError::MissingAttribute(name.to_string()))?;
73-
Ok((&out.0, &out.1))
73+
74+
Ok(out)
7475
}
7576

7677
pub fn into_value<T: Decryptable>(self) -> Result<T, SealError> {

0 commit comments

Comments
 (0)