@@ -23,12 +23,14 @@ module DynamoDBSupport {
23
23
import opened DynamoDbEncryptionUtil
24
24
import opened DdbVirtualFields
25
25
import opened SearchableEncryptionInfo
26
+ import StandardLibrary. String
26
27
import UTF8
27
28
import SortedSets
28
29
import Seq
29
30
import Update = DynamoDbUpdateExpr
30
31
import Filter = DynamoDBFilterExpr
31
32
import SET = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes
33
+ import NN = DynamoDbNormalizeNumber
32
34
33
35
// IsWritable examines an AttributeMap and fails if it is unsuitable for writing.
34
36
// At the moment, this means that no attribute names starts with "aws_dbe_",
@@ -222,8 +224,51 @@ module DynamoDBSupport {
222
224
Success (DoRemoveBeacons(item))
223
225
}
224
226
227
+ // If filter expression is 'bucket=NN ...' return (..., NN)
228
+ // else return (None, 0)
229
+ function method ExtractBucketNumber (filterExpr : Option <string >) : (Option< string > , uint32)
230
+ {
231
+ if filterExpr. None? then
232
+ (None, 0)
233
+ else if "bucket=" < filterExpr. value then
234
+ var nFilter := filterExpr. value[7.. ];
235
+ var nDigits := NN. CountDigits (nFilter);
236
+ if nDigits == 0 then
237
+ (None, 0)
238
+ else
239
+ var num := NN. StrToInt (nFilter[..nDigits]);
240
+ if num. Failure? then
241
+ (None, 0)
242
+ else if INT32_MAX_LIMIT < num. value then
243
+ (None, 0)
244
+ else
245
+ (Some (nFilter[nDigits..]), num. value as uint32)
246
+ else
247
+ (None, 0)
248
+ }
249
+
250
+ // Extract aws_dbe_bucket = NN from filterExpr and return bucket
251
+ function method ExtractBucket (search : SearchableEncryptionInfo .BeaconVersion, filterExpr : Option <string >)
252
+ : Result< (Option< string > , seq < uint8> ), Error>
253
+ {
254
+ if search. numBuckets <= 1 then
255
+ Success ((filterExpr, []))
256
+ else
257
+ var (nFilter, bucket) := ExtractBucketNumber (filterExpr);
258
+ if nFilter. Some? then
259
+ :- Need (bucket < search.numBuckets, E("Bucket number specified in FilterExpression was " + String.Base10Int2String(bucket as int) + "but it must be less than the number of buckets " + String. Base10Int2String (search.numBuckets as int)));
260
+ :- Need (bucket < 256, E("Bucket must be less than 256")); // unreachable
261
+ if bucket == 0 then
262
+ Success ((nFilter, []))
263
+ else
264
+ Success ((nFilter, [bucket as uint8]))
265
+ else
266
+ // TODO - if no encrypted beacons then OK
267
+ Failure (E("When numberOfBuckets is greater than one, FilterExpression must start with 'aws_dbe_bucket = NN && '"))
268
+ }
269
+
225
270
// Transform a QueryInput object for searchable encryption.
226
- method QueryInputForBeacons (search : Option <ValidSearchInfo >, actions : AttributeActions , req : DDB .QueryInput, bucket : Bytes )
271
+ method QueryInputForBeacons (search : Option <ValidSearchInfo >, actions : AttributeActions , req : DDB .QueryInput)
227
272
returns (output : Result< DDB. QueryInput, Error> )
228
273
modifies if search. Some? then search. value. Modifies () else {}
229
274
{
@@ -237,7 +282,10 @@ module DynamoDBSupport {
237
282
return Success (req);
238
283
} else {
239
284
var keyId :- Filter. GetBeaconKeyId (search.value.curr(), req. KeyConditionExpression, req. FilterExpression, req. ExpressionAttributeValues, req. ExpressionAttributeNames);
240
- var oldContext := Filter. ExprContext (req.KeyConditionExpression, req.FilterExpression, req.ExpressionAttributeValues, req.ExpressionAttributeNames);
285
+
286
+ var foo :- ExtractBucket (search.value.curr(), req. FilterExpression);
287
+ var (newFilter, bucket) := foo;
288
+ var oldContext := Filter. ExprContext (req.KeyConditionExpression, newFilter, req.ExpressionAttributeValues, req.ExpressionAttributeNames);
241
289
var newContext :- Filter. Beaconize (search.value.curr(), oldContext, keyId, bucket);
242
290
return Success (req.(
243
291
KeyConditionExpression := newContext.keyExpr,
@@ -249,7 +297,7 @@ module DynamoDBSupport {
249
297
}
250
298
251
299
// Transform a QueryOutput object for searchable encryption.
252
- method QueryOutputForBeacons (search : Option <ValidSearchInfo >, req : DDB .QueryInput, resp : DDB .QueryOutput, bucket : Bytes )
300
+ method QueryOutputForBeacons (search : Option <ValidSearchInfo >, req : DDB .QueryInput, resp : DDB .QueryOutput)
253
301
returns (output : Result< DDB. QueryOutput, Error> )
254
302
requires resp. Items. Some?
255
303
ensures output. Success? ==> output. value. Items. Some?
@@ -259,11 +307,13 @@ module DynamoDBSupport {
259
307
var trimmedItems := Seq. Map (i => DoRemoveBeacons(i), resp. Items. value);
260
308
return Success (resp.(Items := Some(trimmedItems)));
261
309
} else {
310
+ var foo :- ExtractBucket (search.value.curr(), req. FilterExpression);
311
+ var (newFilter, bucket) := foo;
262
312
var newItems :- Filter. FilterResults (
263
313
search.value.curr(),
264
314
resp. Items. value,
265
315
req. KeyConditionExpression,
266
- req . FilterExpression ,
316
+ newFilter ,
267
317
req. ExpressionAttributeNames,
268
318
req. ExpressionAttributeValues,
269
319
bucket);
@@ -295,7 +345,7 @@ module DynamoDBSupport {
295
345
}
296
346
297
347
// Transform a ScanInput object for searchable encryption.
298
- method ScanInputForBeacons (search : Option <ValidSearchInfo >, actions : AttributeActions , req : DDB .ScanInput, bucket : Bytes )
348
+ method ScanInputForBeacons (search : Option <ValidSearchInfo >, actions : AttributeActions , req : DDB .ScanInput)
299
349
returns (output : Result< DDB. ScanInput, Error> )
300
350
modifies if search. Some? then search. value. Modifies () else {}
301
351
{
@@ -309,7 +359,9 @@ module DynamoDBSupport {
309
359
return Success (req);
310
360
} else {
311
361
var keyId :- Filter. GetBeaconKeyId (search.value.curr(), None, req. FilterExpression, req. ExpressionAttributeValues, req. ExpressionAttributeNames);
312
- var context := Filter. ExprContext (None, req.FilterExpression, req.ExpressionAttributeValues, req.ExpressionAttributeNames);
362
+ var foo :- ExtractBucket (search.value.curr(), req. FilterExpression);
363
+ var (newFilter, bucket) := foo;
364
+ var context := Filter. ExprContext (None, newFilter, req.ExpressionAttributeValues, req.ExpressionAttributeNames);
313
365
var newContext :- Filter. Beaconize (search.value.curr(), context, keyId, bucket);
314
366
return Success (req.(
315
367
FilterExpression := newContext.filterExpr,
@@ -320,7 +372,7 @@ module DynamoDBSupport {
320
372
}
321
373
322
374
// Transform a ScanOutput object for searchable encryption.
323
- method ScanOutputForBeacons (search : Option <ValidSearchInfo >, req : DDB .ScanInput, resp : DDB .ScanOutput, bucket : Bytes )
375
+ method ScanOutputForBeacons (search : Option <ValidSearchInfo >, req : DDB .ScanInput, resp : DDB .ScanOutput)
324
376
returns (ret : Result< DDB. ScanOutput, Error> )
325
377
requires resp. Items. Some?
326
378
ensures ret. Success? ==> ret. value. Items. Some?
@@ -330,11 +382,13 @@ module DynamoDBSupport {
330
382
var trimmedItems := Seq. Map (i => DoRemoveBeacons(i), resp. Items. value);
331
383
return Success (resp.(Items := Some(trimmedItems)));
332
384
} else {
385
+ var foo :- ExtractBucket (search.value.curr(), req. FilterExpression);
386
+ var (newFilter, bucket) := foo;
333
387
var newItems :- Filter. FilterResults (
334
388
search.value.curr(),
335
389
resp. Items. value,
336
390
None,
337
- req . FilterExpression ,
391
+ newFilter ,
338
392
req. ExpressionAttributeNames,
339
393
req. ExpressionAttributeValues,
340
394
bucket);
0 commit comments