@@ -70,7 +70,7 @@ impl SealedTableEntry {
7070 ///
7171 /// This should be used over [`Sealed::unseal`] when multiple values need to be unsealed.
7272 pub ( crate ) async fn unseal_all (
73- items : Vec < SealedTableEntry > ,
73+ items : Vec < Self > ,
7474 spec : UnsealSpec < ' _ > ,
7575 cipher : & Encryption < impl Credentials < Token = ServiceToken > > ,
7676 ) -> Result < Vec < Unsealed > , SealError > {
@@ -79,6 +79,11 @@ impl SealedTableEntry {
7979 sort_key_prefix,
8080 } = spec;
8181
82+ let items_len = items. len ( ) ;
83+ if items_len == 0 {
84+ return Ok ( Vec :: new ( ) ) ;
85+ }
86+
8287 let mut protected_items = {
8388 let capacity = items. len ( ) * protected_attributes. len ( ) ;
8489 FlattenedEncryptedAttributes :: with_capacity ( capacity)
@@ -98,16 +103,16 @@ impl SealedTableEntry {
98103 if protected_items. is_empty ( ) {
99104 unprotected_items
100105 . into_iter ( )
101- . map ( |unprotected| {
102- // TODO: Create a new_from_unprotected method
103- Ok ( Unsealed :: new_from_parts (
104- NormalizedProtectedAttributes :: new ( ) ,
105- unprotected,
106- ) )
107- } )
106+ . map ( |unprotected| Ok ( Unsealed :: new_from_unprotected ( unprotected) ) )
108107 . collect ( )
109108 } else {
110- let chunk_size = protected_items. len ( ) / unprotected_items. len ( ) ;
109+ let chunk_size =
110+ protected_items
111+ . len ( )
112+ . checked_div ( items_len)
113+ . ok_or ( SealError :: AssertionFailed (
114+ "Division by zero when calculating chunk size" . to_string ( ) ,
115+ ) ) ?;
111116
112117 protected_items
113118 . decrypt_all ( cipher)
@@ -199,3 +204,52 @@ impl TryFrom<SealedTableEntry> for HashMap<String, AttributeValue> {
199204 Ok ( map)
200205 }
201206}
207+
208+ #[ cfg( test) ]
209+ mod tests {
210+ use super :: SealedTableEntry ;
211+ use cipherstash_client:: {
212+ credentials:: { auto_refresh:: AutoRefresh , service_credentials:: ServiceCredentials } ,
213+ encryption:: Encryption ,
214+ ConsoleConfig , ZeroKMS , ZeroKMSConfig ,
215+ } ;
216+ use miette:: IntoDiagnostic ;
217+ use std:: borrow:: Cow ;
218+
219+ type Cipher = Encryption < AutoRefresh < ServiceCredentials > > ;
220+
221+ // FIXME: Use the test cipher from CipherStash Client when that's ready
222+ async fn get_cipher ( ) -> Result < Cipher , Box < dyn std:: error:: Error > > {
223+ let console_config = ConsoleConfig :: builder ( ) . with_env ( ) . build ( ) ?;
224+ let zero_kms_config = ZeroKMSConfig :: builder ( )
225+ . decryption_log ( true )
226+ . with_env ( )
227+ . console_config ( & console_config)
228+ . build_with_client_key ( ) ?;
229+
230+ let zero_kms_client = ZeroKMS :: new_with_client_key (
231+ & zero_kms_config. base_url ( ) ,
232+ AutoRefresh :: new ( zero_kms_config. credentials ( ) ) ,
233+ zero_kms_config. decryption_log_path ( ) . as_deref ( ) ,
234+ zero_kms_config. client_key ( ) ,
235+ ) ;
236+
237+ let config = zero_kms_client. load_dataset_config ( ) . await ?;
238+ Ok ( Encryption :: new ( config. index_root_key , zero_kms_client) )
239+ }
240+
241+ #[ tokio:: test]
242+ async fn test_unseal_all_empty ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
243+ let spec = super :: UnsealSpec {
244+ protected_attributes : Cow :: Borrowed ( & [ ] ) ,
245+ sort_key_prefix : "test" . to_string ( ) ,
246+ } ;
247+ let cipher = get_cipher ( ) . await ?;
248+ let results = SealedTableEntry :: unseal_all ( vec ! [ ] , spec, & cipher)
249+ . await
250+ . into_diagnostic ( ) ?;
251+ assert ! ( results. is_empty( ) ) ;
252+
253+ Ok ( ( ) )
254+ }
255+ }
0 commit comments