Skip to content

Commit 295c2ae

Browse files
committed
m
1 parent fd33688 commit 295c2ae

File tree

43 files changed

+2417
-718
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2417
-718
lines changed

DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald
4747
nameonly virtualFields: Option<VirtualFieldList> := Option.None ,
4848
nameonly encryptedParts: Option<EncryptedPartsList> := Option.None ,
4949
nameonly signedParts: Option<SignedPartsList> := Option.None ,
50-
nameonly numberOfBuckets: Option<BucketCount> := Option.None
50+
nameonly maximumNumberOfBuckets: Option<BucketCount> := Option.None ,
51+
nameonly defaultNumberOfBuckets: Option<BucketCount> := Option.None ,
52+
nameonly bucketSelector: Option<IBucketSelector> := Option.None
5153
)
5254
type BeaconVersionList = x: seq<BeaconVersion> | IsValid_BeaconVersionList(x) witness *
5355
predicate method IsValid_BeaconVersionList(x: seq<BeaconVersion>) {
@@ -166,9 +168,11 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald
166168
ghost constructor() {
167169
CreateDynamoDbEncryptionBranchKeyIdSupplier := [];
168170
GetEncryptedDataKeyDescription := [];
171+
GetNumberOfQueries := [];
169172
}
170173
ghost var CreateDynamoDbEncryptionBranchKeyIdSupplier: seq<DafnyCallEvent<CreateDynamoDbEncryptionBranchKeyIdSupplierInput, Result<CreateDynamoDbEncryptionBranchKeyIdSupplierOutput, Error>>>
171174
ghost var GetEncryptedDataKeyDescription: seq<DafnyCallEvent<GetEncryptedDataKeyDescriptionInput, Result<GetEncryptedDataKeyDescriptionOutput, Error>>>
175+
ghost var GetNumberOfQueries: seq<DafnyCallEvent<GetNumberOfQueriesInput, Result<GetNumberOfQueriesOutput, Error>>>
172176
}
173177
trait {:termination false} IDynamoDbEncryptionClient
174178
{
@@ -238,6 +242,21 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald
238242
ensures GetEncryptedDataKeyDescriptionEnsuresPublicly(input, output)
239243
ensures History.GetEncryptedDataKeyDescription == old(History.GetEncryptedDataKeyDescription) + [DafnyCallEvent(input, output)]
240244

245+
predicate GetNumberOfQueriesEnsuresPublicly(input: GetNumberOfQueriesInput , output: Result<GetNumberOfQueriesOutput, Error>)
246+
// The public method to be called by library consumers
247+
method GetNumberOfQueries ( input: GetNumberOfQueriesInput )
248+
returns (output: Result<GetNumberOfQueriesOutput, Error>)
249+
requires
250+
&& ValidState()
251+
modifies Modifies - {History} ,
252+
History`GetNumberOfQueries
253+
// Dafny will skip type parameters when generating a default decreases clause.
254+
decreases Modifies - {History}
255+
ensures
256+
&& ValidState()
257+
ensures GetNumberOfQueriesEnsuresPublicly(input, output)
258+
ensures History.GetNumberOfQueries == old(History.GetNumberOfQueries) + [DafnyCallEvent(input, output)]
259+
241260
}
242261
datatype DynamoDbEncryptionConfig = | DynamoDbEncryptionConfig (
243262

@@ -319,8 +338,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald
319338
nameonly keyring: Option<AwsCryptographyMaterialProvidersTypes.IKeyring> := Option.None ,
320339
nameonly cmm: Option<AwsCryptographyMaterialProvidersTypes.ICryptographicMaterialsManager> := Option.None ,
321340
nameonly legacyOverride: Option<LegacyOverride> := Option.None ,
322-
nameonly plaintextOverride: Option<PlaintextOverride> := Option.None ,
323-
nameonly bucketSelector: Option<IBucketSelector> := Option.None
341+
nameonly plaintextOverride: Option<PlaintextOverride> := Option.None
324342
)
325343
type DynamoDbTableEncryptionConfigList = map<ComAmazonawsDynamodbTypes.TableName, DynamoDbTableEncryptionConfig>
326344
datatype DynamoDbTablesEncryptionConfig = | DynamoDbTablesEncryptionConfig (
@@ -363,6 +381,12 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald
363381
datatype GetEncryptedDataKeyDescriptionUnion =
364382
| header(header: seq<uint8>)
365383
| item(item: ComAmazonawsDynamodbTypes.AttributeMap)
384+
datatype GetNumberOfQueriesInput = | GetNumberOfQueriesInput (
385+
nameonly input: ComAmazonawsDynamodbTypes.QueryInput
386+
)
387+
datatype GetNumberOfQueriesOutput = | GetNumberOfQueriesOutput (
388+
nameonly numberOfQueries: BucketCount
389+
)
366390
datatype GetPrefix = | GetPrefix (
367391
nameonly length: int32
368392
)
@@ -662,6 +686,26 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbService
662686
History.GetEncryptedDataKeyDescription := History.GetEncryptedDataKeyDescription + [DafnyCallEvent(input, output)];
663687
}
664688

689+
predicate GetNumberOfQueriesEnsuresPublicly(input: GetNumberOfQueriesInput , output: Result<GetNumberOfQueriesOutput, Error>)
690+
{Operations.GetNumberOfQueriesEnsuresPublicly(input, output)}
691+
// The public method to be called by library consumers
692+
method GetNumberOfQueries ( input: GetNumberOfQueriesInput )
693+
returns (output: Result<GetNumberOfQueriesOutput, Error>)
694+
requires
695+
&& ValidState()
696+
modifies Modifies - {History} ,
697+
History`GetNumberOfQueries
698+
// Dafny will skip type parameters when generating a default decreases clause.
699+
decreases Modifies - {History}
700+
ensures
701+
&& ValidState()
702+
ensures GetNumberOfQueriesEnsuresPublicly(input, output)
703+
ensures History.GetNumberOfQueries == old(History.GetNumberOfQueries) + [DafnyCallEvent(input, output)]
704+
{
705+
output := Operations.GetNumberOfQueries(config, input);
706+
History.GetNumberOfQueries := History.GetNumberOfQueries + [DafnyCallEvent(input, output)];
707+
}
708+
665709
}
666710
}
667711
abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbOperations {
@@ -711,4 +755,20 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbOperations {
711755
ensures
712756
&& ValidInternalConfig?(config)
713757
ensures GetEncryptedDataKeyDescriptionEnsuresPublicly(input, output)
758+
759+
760+
predicate GetNumberOfQueriesEnsuresPublicly(input: GetNumberOfQueriesInput , output: Result<GetNumberOfQueriesOutput, Error>)
761+
// The private method to be refined by the library developer
762+
763+
764+
method GetNumberOfQueries ( config: InternalConfig , input: GetNumberOfQueriesInput )
765+
returns (output: Result<GetNumberOfQueriesOutput, Error>)
766+
requires
767+
&& ValidInternalConfig?(config)
768+
modifies ModifiesInternalConfig(config)
769+
// Dafny will skip type parameters when generating a default decreases clause.
770+
decreases ModifiesInternalConfig(config)
771+
ensures
772+
&& ValidInternalConfig?(config)
773+
ensures GetNumberOfQueriesEnsuresPublicly(input, output)
714774
}

DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use aws.cryptography.dbEncryptionSdk.structuredEncryption#CryptoAction
2121

2222
use com.amazonaws.dynamodb#DynamoDB_20120810
2323
use com.amazonaws.dynamodb#AttributeMap
24+
use com.amazonaws.dynamodb#QueryInput
2425
use com.amazonaws.dynamodb#TableName
2526
use com.amazonaws.dynamodb#AttributeName
2627
use com.amazonaws.dynamodb#Key
@@ -45,7 +46,7 @@ use aws.cryptography.materialProviders#AwsCryptographicMaterialProviders
4546
)
4647
service DynamoDbEncryption {
4748
version: "2024-04-02",
48-
operations: [ CreateDynamoDbEncryptionBranchKeyIdSupplier, GetEncryptedDataKeyDescription],
49+
operations: [ CreateDynamoDbEncryptionBranchKeyIdSupplier, GetEncryptedDataKeyDescription, GetNumberOfQueries],
4950
errors: [ DynamoDbEncryptionException ]
5051
}
5152

@@ -73,6 +74,20 @@ structure GetBucketNumberOutput {
7374
bucketNumber: BucketNumber
7475
}
7576

77+
@javadoc("Return the necessary number of query operations for this query, based on bucket usage.")
78+
operation GetNumberOfQueries {
79+
input: GetNumberOfQueriesInput,
80+
output: GetNumberOfQueriesOutput,
81+
}
82+
structure GetNumberOfQueriesInput {
83+
@required
84+
input: QueryInput
85+
}
86+
structure GetNumberOfQueriesOutput {
87+
@required
88+
numberOfQueries: BucketCount
89+
}
90+
7691
@javadoc("Returns encrypted data key description.")
7792
operation GetEncryptedDataKeyDescription {
7893
input: GetEncryptedDataKeyDescriptionInput,
@@ -231,9 +246,6 @@ structure DynamoDbTableEncryptionConfig {
231246
legacyOverride: LegacyOverride,
232247
@javadoc("A configuration that override encryption and/or decryption to instead passthrough and write and/or read plaintext. Used to update plaintext tables to fully use client-side encryption.")
233248
plaintextOverride: PlaintextOverride,
234-
235-
@javadoc("How to choose the bucket for an item. Default behavior is a random between 0 and numberOfBuckets.")
236-
bucketSelector: BucketSelectorReference,
237249
}
238250

239251
map AttributeActions {
@@ -274,7 +286,7 @@ structure LegacyDynamoDbEncryptorReference {}
274286
@javadoc("A configuration for overriding encryption and/or decryption to instead perform legacy encryption and decryption.")
275287
structure LegacyOverride {
276288
@required
277-
@javadoc("A policy which configurates whether legacy behavior overrides encryption and/or decryption.")
289+
@javadoc("A policy which configures whether legacy behavior overrides encryption and/or decryption.")
278290
policy: LegacyPolicy,
279291
@required
280292
@javadoc("A configuration for the legacy DynamoDB Encryption Client's Encryptor.")
@@ -509,7 +521,7 @@ structure GetSegment {
509521
@javadoc("The characters to split on.")
510522
split : Char,
511523
@required
512-
@javadoc("The index of the split string result to return. 0 represents the segment before the first split character. -1 respresents the segment after the last split character.")
524+
@javadoc("The index of the split string result to return. 0 represents the segment before the first split character. -1 represents the segment after the last split character.")
513525
index : Integer
514526
}
515527

@@ -669,7 +681,7 @@ structure Constructor {
669681
//# - A name -- a string
670682
//# - A required flag -- a boolean
671683

672-
@javadoc("A part of a Compound Becaon Construction.")
684+
@javadoc("A part of a Compound Beacon Construction.")
673685
structure ConstructorPart {
674686
@required
675687
@javadoc("The name of the Encrypted Part or Signed Part for which this constructor part gets a value.")
@@ -824,15 +836,22 @@ structure BeaconVersion {
824836

825837
@javadoc("The Compound Beacons to be written with items.")
826838
compoundBeacons : CompoundBeaconList,
827-
@javadoc("The Virtual Fields to be calculated, supporting other searchable enryption configurations.")
839+
@javadoc("The Virtual Fields to be calculated, supporting other searchable encryption configurations.")
828840
virtualFields : VirtualFieldList,
829841

830842
@javadoc("The list of Encrypted Parts that may be included in any compound beacon.")
831843
encryptedParts : EncryptedPartsList,
832844
@javadoc("The list of Signed Parts that may be included in any compound beacon.")
833845
signedParts : SignedPartsList,
846+
834847
@javadoc("The number of separate buckets across which beacons should be divided.")
835-
numberOfBuckets : BucketCount
848+
maximumNumberOfBuckets : BucketCount,
849+
850+
@javadoc("The number of buckets for any beacon that doesn't specify a numberOfBuckets")
851+
defaultNumberOfBuckets : BucketCount,
852+
853+
@javadoc("How to choose the bucket for an item. Default behavior is a random between 0 and maximumNumberOfBuckets.")
854+
bucketSelector: BucketSelectorReference,
836855
}
837856

838857
//= specification/searchable-encryption/search-config.md#initialization

DynamoDbEncryption/dafny/DynamoDbEncryption/src/AwsCryptographyDbEncryptionSdkDynamoDbOperations.dfy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ module AwsCryptographyDbEncryptionSdkDynamoDbOperations refines AbstractAwsCrypt
4343
)
4444
);
4545
}
46+
predicate GetNumberOfQueriesEnsuresPublicly(input: GetNumberOfQueriesInput , output: Result<GetNumberOfQueriesOutput, Error>)
47+
{true}
48+
49+
method GetNumberOfQueries(config: InternalConfig, input: GetNumberOfQueriesInput)
50+
returns (output: Result<GetNumberOfQueriesOutput, Error>)
51+
ensures GetNumberOfQueriesEnsuresPublicly(input, output)
52+
{
53+
return Success(GetNumberOfQueriesOutput(numberOfQueries := 1));
54+
}
55+
56+
4657
predicate GetEncryptedDataKeyDescriptionEnsuresPublicly(input: GetEncryptedDataKeyDescriptionInput , output: Result<GetEncryptedDataKeyDescriptionOutput, Error>)
4758
{true}
4859

DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module SearchConfigToInfo {
2626
import opened StandardLibrary.MemoryMath
2727
import MaterialProviders
2828
import SortedSets
29+
import Random
2930

3031
import I = SearchableEncryptionInfo
3132
import V = DdbVirtualFields
@@ -266,6 +267,47 @@ module SearchConfigToInfo {
266267
output := ConvertVersionWithSource(outer, config, source);
267268
}
268269

270+
class DefaultBucketSelector extends IBucketSelector
271+
{
272+
predicate ValidState()
273+
ensures ValidState() ==> History in Modifies
274+
{ History in Modifies }
275+
276+
constructor ()
277+
ensures ValidState() && fresh(History) && fresh(Modifies)
278+
{
279+
History := new IBucketSelectorCallHistory();
280+
Modifies := { History };
281+
}
282+
283+
predicate GetBucketNumberEnsuresPublicly (
284+
input: GetBucketNumberInput ,
285+
output: Result<GetBucketNumberOutput, Error> )
286+
: (outcome: bool)
287+
{
288+
true
289+
}
290+
291+
method GetBucketNumber'(input: GetBucketNumberInput)
292+
returns (output: Result<GetBucketNumberOutput, Error> )
293+
requires ValidState()
294+
modifies Modifies - {History}
295+
decreases Modifies - {History}
296+
ensures ValidState()
297+
ensures GetBucketNumberEnsuresPublicly(input, output)
298+
ensures unchanged(History)
299+
{
300+
if input.numberOfBuckets == 1 {
301+
return Success(GetBucketNumberOutput(bucketNumber := 0));
302+
} else {
303+
var randR := Random.GenerateBytes(1);
304+
var rand : seq<uint8> :- randR.MapFailure(e => DynamoDbEncryptionException(message := "Failed to get random byte"));
305+
var bucket := (rand[0] % (input.numberOfBuckets as uint8)) as BucketNumber;
306+
return Success(GetBucketNumberOutput(bucketNumber := bucket));
307+
}
308+
}
309+
}
310+
269311
// convert configured BeaconVersion to internal BeaconVersion
270312
method ConvertVersionWithSource(
271313
outer : DynamoDbTableEncryptionConfig,
@@ -280,8 +322,19 @@ module SearchConfigToInfo {
280322
&& output.value.ValidState()
281323
&& output.value.keySource == source
282324
{
325+
var maxBuckets : BucketCount := config.maximumNumberOfBuckets.UnwrapOr(1);
326+
var defaultBuckets : BucketCount := config.defaultNumberOfBuckets.UnwrapOr(maxBuckets);
327+
:- Need(0 <= maxBuckets as nat < MAX_BUCKET_COUNT, E("Invalid number of buckets specified, " + Base10Int2String(maxBuckets as int) + ", must be 0 < maximumNumberOfBuckets <= 255."));
328+
// Zero is invalid, but in Java we can't distinguish None from Some(0)
329+
if maxBuckets == 0 {
330+
maxBuckets := 1;
331+
}
332+
if defaultBuckets == 0 {
333+
defaultBuckets := maxBuckets;
334+
}
335+
283336
var virtualFields :- ConvertVirtualFields(outer, config.virtualFields);
284-
var std :- AddStandardBeacons(config.standardBeacons, outer, source.client, virtualFields);
337+
var std :- AddStandardBeacons(config.standardBeacons, outer, source.client, virtualFields, maxBuckets, defaultBuckets);
285338

286339
var signed := if config.signedParts.Some? then config.signedParts.value else [];
287340

@@ -315,20 +368,22 @@ module SearchConfigToInfo {
315368
return Failure(E("A beacon key field name of " + name + " was configured, but there's also a virtual field of that name."));
316369
}
317370
}
371+
var bucketSelector;
372+
if outer.search.Some? && outer.search.value.versions[0].bucketSelector.Some? {
373+
bucketSelector := outer.search.value.versions[0].bucketSelector.value;
374+
assume {:axiom} bucketSelector.ValidState();
375+
} else {
376+
bucketSelector := new DefaultBucketSelector();
377+
}
318378

319-
var numBuckets : int := config.numberOfBuckets.UnwrapOr(1) as int;
320-
:- Need(0 <= numBuckets < MAX_BUCKET_COUNT, E("Invalid number of buckets specified, " + Base10Int2String(numBuckets) + ", must be 0 < numberOfBuckets <= 255."));
321-
// Zero is invalid, but in Java we can't distinguish None from Some(0)
322-
if numBuckets == 0 {
323-
numBuckets := 1;
324-
}
325379
return I.MakeBeaconVersion(
326380
config.version as I.VersionNumber,
327381
source,
328382
beacons,
329383
virtualFields,
330384
outer.attributeActionsOnEncrypt,
331-
numBuckets as BucketCount
385+
bucketSelector,
386+
maxBuckets
332387
);
333388
}
334389

@@ -526,31 +581,28 @@ module SearchConfigToInfo {
526581
Failure(E("Beacon " + name + " is shared to " + share + " which is not defined."))
527582
}
528583

529-
function method GetBucketCount(outer : DynamoDbTableEncryptionConfig, inner : Option<BucketCount>, name : string) :
584+
function method GetBucketCount(outer : DynamoDbTableEncryptionConfig, inner : Option<BucketCount>, name : string, maxBuckets : BucketCount, defaultBuckets : BucketCount) :
530585
Result<BucketCount,Error>
531586
{
532587
if outer.search.None? || |outer.search.value.versions| == 0 then
533588
Success(1)
534589
else
535-
var num := outer.search.value.versions[0].numberOfBuckets;
536-
if BucketCountNone(num) then
537-
if !BucketCountNone(inner) then
538-
Failure(E("Constrained numberOfBuckets for " + name + " is " + Base10Int2String(inner.value as int) + " but there is not global numberOfBuckets set."))
539-
else
540-
Success(1)
541-
else if BucketCountNone(inner) then
542-
Success(num.value)
543-
else if inner.value < num.value then
590+
if BucketCountNone(inner) then
591+
Success(defaultBuckets)
592+
else if inner.value < maxBuckets then
544593
Success(inner.value)
545594
else
546-
Failure(E("Constrained numberOfBuckets for " + name + " is " + Base10Int2String(inner.value as int) + " but it must be less than the global numberOfBuckets " + Base10Int2String(num.value as int)))
595+
Failure(E("Constrained numberOfBuckets for " + name + " is " + Base10Int2String(inner.value as int) + " but it must be less than the maximumNumberOfBuckets " + Base10Int2String(maxBuckets as int)))
547596
}
597+
548598
// convert configured StandardBeacons to internal Beacons
549599
method {:tailrecursion} AddStandardBeacons(
550600
beacons : seq<StandardBeacon>,
551601
outer : DynamoDbTableEncryptionConfig,
552602
client: Primitives.AtomicPrimitivesClient,
553603
virtualFields : V.VirtualFieldMap,
604+
maxBuckets : BucketCount,
605+
defaultBuckets : BucketCount,
554606
converted : I.BeaconMap := map[])
555607
returns (output : Result<I.BeaconMap, Error>)
556608
modifies client.Modifies
@@ -608,7 +660,7 @@ module SearchConfigToInfo {
608660
case sharedSet(t) => share := Some(t.other); isAsSet := true;
609661
}
610662
}
611-
var bucketCount :- GetBucketCount(outer, beacons[0].numberOfBuckets, beacons[0].name);
663+
var bucketCount :- GetBucketCount(outer, beacons[0].numberOfBuckets, beacons[0].name, maxBuckets, defaultBuckets);
612664
var newBeacon :- B.MakeStandardBeacon(client, beacons[0].name, beacons[0].length as B.BeaconLength, locString,
613665
isPartOnly, isAsSet, share, bucketCount);
614666

@@ -628,7 +680,7 @@ module SearchConfigToInfo {
628680
+ ", but virtual field " + badField.value + " is already defined on that single location."));
629681
}
630682

631-
output := AddStandardBeacons(beacons[1..], outer, client, virtualFields, converted[beacons[0].name := I.Standard(newBeacon)]);
683+
output := AddStandardBeacons(beacons[1..], outer, client, virtualFields, maxBuckets, defaultBuckets, converted[beacons[0].name := I.Standard(newBeacon)]);
632684
}
633685

634686
// optional location, defaults to name

0 commit comments

Comments
 (0)