Skip to content

Commit 26f3eba

Browse files
authored
feat: add ResolveAttribute (#412)
* feat: add ResolveAttribute
1 parent a331844 commit 26f3eba

File tree

20 files changed

+706
-73
lines changed

20 files changed

+706
-73
lines changed

DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ structure BeaconVersion {
653653
@javadoc("The version of searchable encryption configured. This must be '1'.")
654654
version : VersionNumber,
655655
@required
656-
@javadoc("The Key Store that contains the Becon Keys to use with searchable encryption.")
656+
@javadoc("The Key Store that contains the Beacon Keys to use with searchable encryption.")
657657
keyStore : KeyStoreReference,
658658
@required
659659
@javadoc("The configuration for what beacon key(s) to use.")

DynamoDbEncryption/dafny/DynamoDbEncryption/src/DDBSupport.dfy

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,63 @@ module DynamoDBSupport {
331331
return Success(resp.(Items := Some(trimmedItems), Count := count));
332332
}
333333
}
334+
335+
function method {:tailrecursion} GetVirtualFieldsLoop(
336+
fields : seq<string>,
337+
bv : SearchableEncryptionInfo.BeaconVersion,
338+
item : DDB.AttributeMap,
339+
results : map<string, string> := map[])
340+
: (output : Result<map<string, string>, Error>)
341+
requires forall x <- fields :: x in bv.virtualFields
342+
requires forall x <- results :: x in bv.virtualFields
343+
ensures output.Success? ==> forall x <- output.value :: x in bv.virtualFields
344+
{
345+
if |fields| == 0 then
346+
Success(results)
347+
else
348+
var optValue :- GetVirtField(bv.virtualFields[fields[0]], item);
349+
if optValue.Some? then
350+
GetVirtualFieldsLoop(fields[1..], bv, item, results[fields[0] := optValue.value])
351+
else
352+
GetVirtualFieldsLoop(fields[1..], bv, item, results)
353+
}
354+
355+
method GetVirtualFields(beaconVersion : SearchableEncryptionInfo.BeaconVersion, item : DDB.AttributeMap)
356+
returns (output : Result<map<string, string>, Error>)
357+
{
358+
var fieldNames := SortedSets.ComputeSetToOrderedSequence2(beaconVersion.virtualFields.Keys, CharLess);
359+
output := GetVirtualFieldsLoop(fieldNames, beaconVersion, item);
360+
}
361+
362+
function method {:tailrecursion} GetCompoundBeaconsLoop(
363+
fields : seq<string>,
364+
bv : SearchableEncryptionInfo.BeaconVersion,
365+
item : DDB.AttributeMap,
366+
results : map<string, string> := map[])
367+
: (output : Result<map<string, string>, Error>)
368+
requires forall x <- fields :: x in bv.beacons
369+
requires forall x <- results :: x in bv.beacons
370+
ensures output.Success? ==> forall x <- output.value :: x in bv.beacons
371+
{
372+
if |fields| == 0 then
373+
Success(results)
374+
else
375+
var beacon := bv.beacons[fields[0]];
376+
if beacon.Compound? then
377+
var optValue :- beacon.cmp.getNaked(item, bv.virtualFields);
378+
if optValue.Some? then
379+
GetCompoundBeaconsLoop(fields[1..], bv, item, results[fields[0] := optValue.value])
380+
else
381+
GetCompoundBeaconsLoop(fields[1..], bv, item, results)
382+
else
383+
GetCompoundBeaconsLoop(fields[1..], bv, item, results)
384+
}
385+
386+
method GetCompoundBeacons(beaconVersion : SearchableEncryptionInfo.BeaconVersion, item : DDB.AttributeMap)
387+
returns (output : Result<map<string, string>, Error>)
388+
{
389+
var beaconNames := SortedSets.ComputeSetToOrderedSequence2(beaconVersion.beacons.Keys, CharLess);
390+
output := GetCompoundBeaconsLoop(beaconNames, beaconVersion, item);
391+
}
392+
334393
}

DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -393,38 +393,10 @@ module DdbVirtualFields {
393393
TermLoc.TermToString(loc, item)
394394
}
395395

396-
/*
397-
// return true if item has all of the terminals necessary for field
398-
predicate method ValueHasNeededAttrs(field : VirtualField, item : DDB.AttributeMap)
399-
{
400-
!field.examine(t => LacksAttribute(t, item))
401-
}
402-
*/
403396
// convert string to DynamoDB Attribute
404397
function method DS(s : string)
405398
: DDB.AttributeValue
406399
{
407400
DDB.AttributeValue.S(s)
408401
}
409-
/*
410-
// return an AttributeMap containing all of the virtual fields for which we have the appropriate attributes
411-
function method {:tailrecursion} GetVirtualAttrs (
412-
fields : seq<VirtualField>,
413-
item : DDB.AttributeMap,
414-
acc : DDB.AttributeMap := map[])
415-
: (ret : Result<DDB.AttributeMap, string>)
416-
requires AllStrings(acc)
417-
ensures ret.Success? ==> AllStrings(ret.value)
418-
{
419-
if |fields| == 0 then
420-
Success(acc)
421-
else
422-
:- Need(DDB.IsValid_AttributeName(fields[0].name), "Virtual field name '" + fields[0].name + "' is not a valid DynamoDB Attribute Name");
423-
if ValueHasNeededAttrs(fields[0], item) then
424-
var value :- fields[0].makeString(t => TermToString(t, item));
425-
GetVirtualAttrs(fields[1..], item, acc[fields[0].name := DDB.AttributeValue.S(value)])
426-
else
427-
GetVirtualAttrs(fields[1..], item, acc)
428-
}
429-
*/
430402
}

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
9898
BatchExecuteStatementOutputTransform := [];
9999
ExecuteTransactionInputTransform := [];
100100
ExecuteTransactionOutputTransform := [];
101+
ResolveAttributes := [];
101102
}
102103
ghost var PutItemInputTransform: seq<DafnyCallEvent<PutItemInputTransformInput, Result<PutItemInputTransformOutput, Error>>>
103104
ghost var PutItemOutputTransform: seq<DafnyCallEvent<PutItemOutputTransformInput, Result<PutItemOutputTransformOutput, Error>>>
@@ -125,6 +126,7 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
125126
ghost var BatchExecuteStatementOutputTransform: seq<DafnyCallEvent<BatchExecuteStatementOutputTransformInput, Result<BatchExecuteStatementOutputTransformOutput, Error>>>
126127
ghost var ExecuteTransactionInputTransform: seq<DafnyCallEvent<ExecuteTransactionInputTransformInput, Result<ExecuteTransactionInputTransformOutput, Error>>>
127128
ghost var ExecuteTransactionOutputTransform: seq<DafnyCallEvent<ExecuteTransactionOutputTransformInput, Result<ExecuteTransactionOutputTransformOutput, Error>>>
129+
ghost var ResolveAttributes: seq<DafnyCallEvent<ResolveAttributesInput, Result<ResolveAttributesOutput, Error>>>
128130
}
129131
trait {:termination false} IDynamoDbEncryptionTransformsClient
130132
{
@@ -542,6 +544,22 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
542544
&& ValidState()
543545
ensures ExecuteTransactionOutputTransformEnsuresPublicly(input, output)
544546
ensures History.ExecuteTransactionOutputTransform == old(History.ExecuteTransactionOutputTransform) + [DafnyCallEvent(input, output)]
547+
548+
predicate ResolveAttributesEnsuresPublicly(input: ResolveAttributesInput , output: Result<ResolveAttributesOutput, Error>)
549+
// The public method to be called by library consumers
550+
method ResolveAttributes ( input: ResolveAttributesInput )
551+
returns (output: Result<ResolveAttributesOutput, Error>)
552+
requires
553+
&& ValidState()
554+
modifies Modifies - {History} ,
555+
History`ResolveAttributes
556+
// Dafny will skip type parameters when generating a default decreases clause.
557+
decreases Modifies - {History}
558+
ensures
559+
&& ValidState()
560+
ensures ResolveAttributesEnsuresPublicly(input, output)
561+
ensures History.ResolveAttributes == old(History.ResolveAttributes) + [DafnyCallEvent(input, output)]
562+
545563
}
546564
datatype ExecuteStatementInputTransformInput = | ExecuteStatementInputTransformInput (
547565
nameonly sdkInput: ComAmazonawsDynamodbTypes.ExecuteStatementInput
@@ -608,6 +626,15 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
608626
datatype QueryOutputTransformOutput = | QueryOutputTransformOutput (
609627
nameonly transformedOutput: ComAmazonawsDynamodbTypes.QueryOutput
610628
)
629+
datatype ResolveAttributesInput = | ResolveAttributesInput (
630+
nameonly TableName: ComAmazonawsDynamodbTypes.TableName ,
631+
nameonly Item: ComAmazonawsDynamodbTypes.AttributeMap ,
632+
nameonly Version: Option<AwsCryptographyDbEncryptionSdkDynamoDbTypes.VersionNumber>
633+
)
634+
datatype ResolveAttributesOutput = | ResolveAttributesOutput (
635+
nameonly VirtualFields: StringMap ,
636+
nameonly CompoundBeacons: StringMap
637+
)
611638
datatype ScanInputTransformInput = | ScanInputTransformInput (
612639
nameonly sdkInput: ComAmazonawsDynamodbTypes.ScanInput
613640
)
@@ -621,6 +648,7 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
621648
datatype ScanOutputTransformOutput = | ScanOutputTransformOutput (
622649
nameonly transformedOutput: ComAmazonawsDynamodbTypes.ScanOutput
623650
)
651+
type StringMap = map<string, string>
624652
datatype TransactGetItemsInputTransformInput = | TransactGetItemsInputTransformInput (
625653
nameonly sdkInput: ComAmazonawsDynamodbTypes.TransactGetItemsInput
626654
)
@@ -1357,6 +1385,27 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
13571385
output := Operations.ExecuteTransactionOutputTransform(config, input);
13581386
History.ExecuteTransactionOutputTransform := History.ExecuteTransactionOutputTransform + [DafnyCallEvent(input, output)];
13591387
}
1388+
1389+
predicate ResolveAttributesEnsuresPublicly(input: ResolveAttributesInput , output: Result<ResolveAttributesOutput, Error>)
1390+
{Operations.ResolveAttributesEnsuresPublicly(input, output)}
1391+
// The public method to be called by library consumers
1392+
method ResolveAttributes ( input: ResolveAttributesInput )
1393+
returns (output: Result<ResolveAttributesOutput, Error>)
1394+
requires
1395+
&& ValidState()
1396+
modifies Modifies - {History} ,
1397+
History`ResolveAttributes
1398+
// Dafny will skip type parameters when generating a default decreases clause.
1399+
decreases Modifies - {History}
1400+
ensures
1401+
&& ValidState()
1402+
ensures ResolveAttributesEnsuresPublicly(input, output)
1403+
ensures History.ResolveAttributes == old(History.ResolveAttributes) + [DafnyCallEvent(input, output)]
1404+
{
1405+
output := Operations.ResolveAttributes(config, input);
1406+
History.ResolveAttributes := History.ResolveAttributes + [DafnyCallEvent(input, output)];
1407+
}
1408+
13601409
}
13611410
}
13621411
abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations {
@@ -1781,4 +1830,20 @@ include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy"
17811830
ensures
17821831
&& ValidInternalConfig?(config)
17831832
ensures ExecuteTransactionOutputTransformEnsuresPublicly(input, output)
1833+
1834+
1835+
predicate ResolveAttributesEnsuresPublicly(input: ResolveAttributesInput , output: Result<ResolveAttributesOutput, Error>)
1836+
// The private method to be refined by the library developer
1837+
1838+
1839+
method ResolveAttributes ( config: InternalConfig , input: ResolveAttributesInput )
1840+
returns (output: Result<ResolveAttributesOutput, Error>)
1841+
requires
1842+
&& ValidInternalConfig?(config)
1843+
modifies ModifiesInternalConfig(config)
1844+
// Dafny will skip type parameters when generating a default decreases clause.
1845+
decreases ModifiesInternalConfig(config)
1846+
ensures
1847+
&& ValidInternalConfig?(config)
1848+
ensures ResolveAttributesEnsuresPublicly(input, output)
17841849
}

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/DynamoDbEncryptionTransforms.smithy

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ namespace aws.cryptography.dbEncryptionSdk.dynamoDb.transforms
55
use aws.cryptography.dbEncryptionSdk.dynamoDb#DynamoDbTablesEncryptionConfig
66

77
use com.amazonaws.dynamodb#DynamoDB_20120810
8+
use com.amazonaws.dynamodb#TableName
9+
use com.amazonaws.dynamodb#AttributeMap
10+
811
use aws.cryptography.dbEncryptionSdk.dynamoDb#DynamoDbEncryption
912
use aws.cryptography.dbEncryptionSdk.dynamoDb.itemEncryptor#DynamoDbItemEncryptor
13+
use aws.cryptography.dbEncryptionSdk.dynamoDb#VersionNumber
1014

1115
use aws.polymorph#localService
16+
use aws.polymorph#javadoc
1217

1318
@localService(
1419
sdkId: "DynamoDbEncryptionTransforms",
@@ -48,10 +53,43 @@ service DynamoDbEncryptionTransforms {
4853
BatchExecuteStatementOutputTransform,
4954
ExecuteTransactionInputTransform,
5055
ExecuteTransactionOutputTransform,
56+
ResolveAttributes,
5157
],
5258
errors: [ DynamoDbEncryptionTransformsException ]
5359
}
5460

61+
@javadoc("Given an Item, show the intermediate values (e.g. compound beacons, virtual fields).")
62+
operation ResolveAttributes {
63+
input: ResolveAttributesInput,
64+
output: ResolveAttributesOutput,
65+
}
66+
67+
map StringMap {
68+
key : String,
69+
value : String
70+
}
71+
72+
structure ResolveAttributesInput {
73+
@required
74+
@javadoc("Use the config for this Table.")
75+
TableName: TableName,
76+
@required
77+
@javadoc("The Item to be examined.")
78+
Item: AttributeMap,
79+
@javadoc("The beacon version to use. Defaults to 'writeVersion'.")
80+
Version : VersionNumber
81+
}
82+
83+
structure ResolveAttributesOutput {
84+
@required
85+
@javadoc("Full plaintext of all calculable virtual fields.")
86+
VirtualFields: StringMap,
87+
@required
88+
@javadoc("Full plaintext of all calculable compound beacons.")
89+
CompoundBeacons: StringMap,
90+
}
91+
92+
5593
@aws.polymorph#reference(service: aws.cryptography.dbEncryptionSdk.dynamoDb.itemEncryptor#DynamoDbItemEncryptor)
5694
structure DynamoDbItemEncryptorReference {}
5795

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
include "DynamoDbMiddlewareSupport.dfy"
5+
6+
module AttributeResolver {
7+
import opened DdbMiddlewareConfig
8+
import opened DynamoDbMiddlewareSupport
9+
import opened Wrappers
10+
import DDB = ComAmazonawsDynamodbTypes
11+
import opened AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes
12+
import EncTypes = AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes
13+
import Seq
14+
import DynamoDBSupport
15+
16+
method Resolve(config: Config, input: ResolveAttributesInput)
17+
returns (output: Result<ResolveAttributesOutput, Error>)
18+
requires ValidConfig?(config)
19+
ensures ValidConfig?(config)
20+
modifies ModifiesConfig(config)
21+
22+
{
23+
if || input.TableName !in config.tableEncryptionConfigs
24+
|| config.tableEncryptionConfigs[input.TableName].search.None?
25+
{
26+
return Success(
27+
ResolveAttributesOutput(
28+
VirtualFields := map[],
29+
CompoundBeacons := map[]
30+
)
31+
);
32+
} else {
33+
var tableConfig := config.tableEncryptionConfigs[input.TableName];
34+
var vf :- GetVirtualFields(tableConfig.search.value, input.Item, input.Version);
35+
var cb :- GetCompoundBeacons(tableConfig.search.value, input.Item, input.Version);
36+
return Success(
37+
ResolveAttributesOutput(
38+
VirtualFields := vf,
39+
CompoundBeacons := cb
40+
)
41+
);
42+
}
43+
}
44+
45+
}

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/AwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations.dfy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ include "DeleteItemTransform.dfy"
1616
include "ExecuteStatementTransform.dfy"
1717
include "BatchExecuteStatementTransform.dfy"
1818
include "ExecuteTransactionTransform.dfy"
19+
include "AttributeResolver.dfy"
1920

2021
module AwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations refines AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations {
2122
import opened DdbMiddlewareConfig
@@ -41,6 +42,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations refines Abstra
4142
import ExecuteStatementTransform
4243
import BatchExecuteStatementTransform
4344
import ExecuteTransactionTransform
45+
import AttributeResolver
4446

4547
predicate ValidInternalConfig?(config: InternalConfig)
4648
{
@@ -288,4 +290,13 @@ module AwsCryptographyDbEncryptionSdkDynamoDbTransformsOperations refines Abstra
288290
{
289291
output := ExecuteTransactionTransform.Output(config, input);
290292
}
293+
294+
predicate ResolveAttributesEnsuresPublicly(input: ResolveAttributesInput, output: Result<ResolveAttributesOutput, Error>)
295+
{true}
296+
297+
method ResolveAttributes(config: InternalConfig, input: ResolveAttributesInput)
298+
returns (output: Result<ResolveAttributesOutput, Error>)
299+
{
300+
output := AttributeResolver.Resolve(config, input);
301+
}
291302
}

DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ module DynamoDbMiddlewareSupport {
1919
import opened BS = DynamoDBSupport
2020
import opened DdbMiddlewareConfig
2121
import AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations
22+
import ET = AwsCryptographyDbEncryptionSdkDynamoDbTypes
2223
import Util = DynamoDbEncryptionUtil
24+
import SI = SearchableEncryptionInfo
2325

2426
// IsWritable examines an AttributeMap and fails if it is unsuitable for writing.
2527
// Generally this means that no attribute names starts with "aws_dbe_"
@@ -202,4 +204,25 @@ module DynamoDbMiddlewareSupport {
202204
var ret := BS.ScanOutputForBeacons(config.search, req, resp);
203205
return ret.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e));
204206
}
207+
208+
method GetVirtualFields(search : SearchableEncryptionInfo.ValidSearchInfo, item : DDB.AttributeMap, version : Option<ET.VersionNumber>)
209+
returns (output : Result<map<string, string>, Error>)
210+
{
211+
if version.Some? && version.value != 1 {
212+
return Failure(E("Beacon Version Number must be '1'"));
213+
}
214+
var ret := BS.GetVirtualFields(search.curr(), item);
215+
return ret.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e));
216+
}
217+
218+
method GetCompoundBeacons(search : SearchableEncryptionInfo.ValidSearchInfo, item : DDB.AttributeMap, version : Option<ET.VersionNumber>)
219+
returns (output : Result<map<string, string>, Error>)
220+
{
221+
if version.Some? && version.value != 1 {
222+
return Failure(E("Beacon Version Number must be '1'"));
223+
}
224+
var ret := BS.GetCompoundBeacons(search.curr(), item);
225+
return ret.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e));
226+
}
227+
205228
}

0 commit comments

Comments
 (0)