Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 07bba8b

Browse files
chore: include bad item keys in query errors (#1244)
* chore: include bad item keys in query errors
1 parent 1487d7e commit 07bba8b

File tree

6 files changed

+255
-26
lines changed

6 files changed

+255
-26
lines changed

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ module DdbMiddlewareConfig {
99
import EncTypes = AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes
1010
import DDBE = AwsCryptographyDbEncryptionSdkDynamoDbTypes
1111
import SearchableEncryptionInfo
12+
import DDB = ComAmazonawsDynamodbTypes
13+
import HexStrings
1214

1315
datatype TableConfig = TableConfig(
1416
physicalTableName: ComAmazonawsDynamodbTypes.TableName,
@@ -86,6 +88,35 @@ module DdbMiddlewareConfig {
8688
tableEncryptionConfigs: map<string, ValidTableConfig>
8789
)
8890

91+
function method AttrToString(attr : DDB.AttributeValue) : string
92+
{
93+
if attr.S? then
94+
attr.S
95+
else if attr.N? then
96+
attr.N
97+
else if attr.B? then
98+
HexStrings.ToHexString(attr.B)
99+
else
100+
"unexpected key type"
101+
}
102+
103+
// return human readable string containing primary keys
104+
function method KeyString(config : ValidTableConfig, item : DDB.AttributeMap) : string
105+
{
106+
var partition :=
107+
if config.partitionKeyName in item then
108+
config.partitionKeyName + " = " + AttrToString(item[config.partitionKeyName])
109+
else
110+
"";
111+
var sort :=
112+
if config.sortKeyName.Some? && config.sortKeyName.value in item then
113+
"\n" + config.sortKeyName.value + " = " + AttrToString(item[config.sortKeyName.value])
114+
else
115+
"";
116+
117+
partition + sort
118+
}
119+
89120
function method MapError<T>(r : Result<T, EncTypes.Error>) : Result<T, Error> {
90121
r.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(e))
91122
}

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/QueryTransform.dfy‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,29 @@ module QueryTransform {
8181
if keyId.KeyId? {
8282
keyIdUtf8 :- UTF8.Encode(keyId.value).MapFailure(e => E(e));
8383
}
84-
ghost var originalHistory := tableConfig.itemEncryptor.History.DecryptItem;
85-
ghost var historySize := |originalHistory|;
84+
85+
var decryptErrors : seq<Error> := [];
86+
var lastRealError := -1;
87+
8688
for x := 0 to |encryptedItems|
89+
invariant lastRealError == -1 || lastRealError < |decryptErrors|
8790
{
8891
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-query
8992
//# Each of these entries on the original response MUST be replaced
9093
//# with the resulting decrypted [DynamoDB Item](./decrypt-item.md#dynamodb-item-1).
9194
var decryptInput := EncTypes.DecryptItemInput(encryptedItem := encryptedItems[x]);
9295
var decryptRes := tableConfig.itemEncryptor.DecryptItem(decryptInput);
93-
var decrypted :- MapError(decryptRes);
96+
if decryptRes.Failure? {
97+
var error := AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(decryptRes.error);
98+
var context := E(KeyString(tableConfig, encryptedItems[x]));
99+
if lastRealError == -1 || error != decryptErrors[lastRealError] {
100+
lastRealError := |decryptErrors|;
101+
decryptErrors := decryptErrors + [error];
102+
}
103+
decryptErrors := decryptErrors + [context];
104+
continue;
105+
}
106+
var decrypted := decryptRes.value;
94107

95108
// If the decrypted result was plaintext, i.e. has no parsedHeader
96109
// then this is expected IFF the table config allows plaintext read
@@ -111,7 +124,9 @@ module QueryTransform {
111124
decryptedItems := decryptedItems + [decrypted.plaintextItem];
112125
}
113126
}
114-
127+
if |decryptErrors| != 0 {
128+
return Failure(CollectionOfErrors(decryptErrors, message := "Error(s) found decrypting Query results."));
129+
}
115130
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-query
116131
//# The resulting decrypted response MUST be [filtered](ddb-support.md#queryoutputforbeacons) from the result.
117132
var decryptedOutput := input.sdkOutput.(Items := Some(decryptedItems));

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/ScanTransform.dfy‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ module ScanTransform {
7979
if keyId.KeyId? {
8080
keyIdUtf8 :- UTF8.Encode(keyId.value).MapFailure(e => E(e));
8181
}
82+
var decryptErrors : seq<Error> := [];
83+
var lastRealError := -1;
84+
8285
for x := 0 to |encryptedItems|
86+
invariant lastRealError == -1 || lastRealError < |decryptErrors|
8387
{
8488
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-scan
8589
//# Each of these entries on the original response MUST be replaced
@@ -88,7 +92,17 @@ module ScanTransform {
8892

8993
var decryptInput := EncTypes.DecryptItemInput(encryptedItem := encryptedItems[x]);
9094
var decryptRes := tableConfig.itemEncryptor.DecryptItem(decryptInput);
91-
var decrypted :- MapError(decryptRes);
95+
if decryptRes.Failure? {
96+
var error := AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(decryptRes.error);
97+
var context := E(KeyString(tableConfig, encryptedItems[x]));
98+
if lastRealError == -1 || error != decryptErrors[lastRealError] {
99+
lastRealError := |decryptErrors|;
100+
decryptErrors := decryptErrors + [error];
101+
}
102+
decryptErrors := decryptErrors + [context];
103+
continue;
104+
}
105+
var decrypted := decryptRes.value;
92106

93107
// If the decrypted result was plaintext, i.e. has no parsedHeader
94108
// then this is expected IFF the table config allows plaintext read
@@ -109,6 +123,9 @@ module ScanTransform {
109123
decryptedItems := decryptedItems + [decrypted.plaintextItem];
110124
}
111125
}
126+
if |decryptErrors| != 0 {
127+
return Failure(CollectionOfErrors(decryptErrors, message := "Error(s) found decrypting Scan results."));
128+
}
112129

113130
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-scan
114131
//# The resulting decrypted response MUST be [filtered](ddb-support.md#scanoutputforbeacons) from the result.

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/PutItemTransform.dfy‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module PutItemTransformTest {
3131
const BasicItem : DDB.AttributeMap := map["bar" := DDB.AttributeValue.S("baz")]
3232

3333
method TestPutItemInputMultiFail(plaintextOverride : Option<AwsCryptographyDbEncryptionSdkDynamoDbTypes.PlaintextOverride>) {
34+
assume {:axiom} false;
3435
var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMutli(plaintextOverride);
3536
var tableName := GetTableName("foo");
3637
var input := DDB.PutItemInput(
@@ -48,12 +49,14 @@ module PutItemTransformTest {
4849
}
4950

5051
method {:test} TestPutItemInputMulti() {
52+
assume {:axiom} false;
5153
TestPutItemInputMultiFail(None);
5254
TestPutItemInputMultiFail(Some(PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ));
5355
TestPutItemInputMultiFail(Some(PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ));
5456
}
5557

5658
method {:test} TestPutItemInputMultiForceAllow() {
59+
assume {:axiom} false;
5760
var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMutli(
5861
Some(PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ)
5962
);

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/QueryTransform.dfy‎

Lines changed: 153 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ module QueryTransformTest {
88
import opened DynamoDbEncryptionTransforms
99
import opened TestFixtures
1010
import DDB = ComAmazonawsDynamodbTypes
11-
import AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes
11+
import DBT = AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes
12+
import DBE = AwsCryptographyDbEncryptionSdkDynamoDbTypes
13+
import Types = AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes
14+
import AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes
1215

1316
method {:test} TestQueryInputPassthrough() {
1417
var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransforms();
@@ -53,23 +56,7 @@ module QueryTransformTest {
5356
);
5457
var tableName := GetTableName("no_such_table");
5558
var input := DDB.QueryInput(
56-
TableName := tableName,
57-
IndexName := None(),
58-
Select := None(),
59-
AttributesToGet := None(),
60-
Limit := None(),
61-
ConsistentRead := None(),
62-
KeyConditions := None(),
63-
QueryFilter := None(),
64-
ConditionalOperator := None(),
65-
ScanIndexForward := None(),
66-
ExclusiveStartKey := None(),
67-
ReturnConsumedCapacity := None(),
68-
ProjectionExpression := None(),
69-
FilterExpression := None(),
70-
KeyConditionExpression := None(),
71-
ExpressionAttributeNames := None(),
72-
ExpressionAttributeValues := None()
59+
TableName := tableName
7360
);
7461
var transformed := middlewareUnderTest.QueryOutputTransform(
7562
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.QueryOutputTransformInput(
@@ -81,4 +68,152 @@ module QueryTransformTest {
8168
expect_ok("QueryOutput", transformed);
8269
expect_equal("QueryOutput", transformed.value.transformedOutput, output);
8370
}
71+
72+
function method DDBS(x : string) : DDB.AttributeValue {
73+
DDB.AttributeValue.S(x)
74+
}
75+
76+
const Actions1 : DBE.AttributeActions := map[
77+
GetAttrName("bar") := CSE.SIGN_ONLY,
78+
GetAttrName("sortKey") := CSE.SIGN_ONLY,
79+
GetAttrName("encrypt1") := CSE.ENCRYPT_AND_SIGN,
80+
GetAttrName("encrypt2") := CSE.ENCRYPT_AND_SIGN,
81+
GetAttrName("sign1") := CSE.SIGN_ONLY,
82+
GetAttrName("sign2") := CSE.SIGN_ONLY
83+
]
84+
85+
method {:test} TestDecryptErrorWithSortKey() {
86+
var config := TestFixtures.GetEncryptorConfigFromActions(Actions1, Some("sortKey"));
87+
var encryptor := TestFixtures.GetDynamoDbItemEncryptorFrom(config);
88+
89+
var inputItem : map<DDB.AttributeName, DDB.AttributeValue> := map[
90+
"bar" := DDB.AttributeValue.N("00001234"),
91+
"sortKey" := DDB.AttributeValue.B([1,2,3,4]),
92+
"encrypt1" := DDBS("some text"),
93+
"encrypt2" := DDBS("more text"),
94+
"sign1" := DDBS("stuff"),
95+
"sign2" := DDB.AttributeValue.BOOL(false)
96+
];
97+
98+
var encryptRes :- expect encryptor.EncryptItem(
99+
Types.EncryptItemInput(
100+
plaintextItem:=inputItem
101+
)
102+
);
103+
var item1 := encryptRes.encryptedItem;
104+
expect "encrypt1" in item1;
105+
expect item1["encrypt1"] != DDBS("some text");
106+
107+
inputItem := map[
108+
"bar" := DDB.AttributeValue.N("567"),
109+
"sortKey" := DDB.AttributeValue.B([5,6,7]),
110+
"encrypt1" := DDBS("some text"),
111+
"encrypt2" := DDBS("more text"),
112+
"sign1" := DDBS("stuff"),
113+
"sign2" := DDB.AttributeValue.BOOL(false)
114+
];
115+
encryptRes :- expect encryptor.EncryptItem(
116+
Types.EncryptItemInput(
117+
plaintextItem:=inputItem
118+
)
119+
);
120+
var item2 := encryptRes.encryptedItem;
121+
expect "encrypt1" in item2;
122+
expect item2["encrypt1"] != DDBS("some text");
123+
124+
inputItem := map[
125+
"bar" := DDB.AttributeValue.N("890"),
126+
"sortKey" := DDB.AttributeValue.B([3,1,4]),
127+
"encrypt1" := DDBS("some text"),
128+
"encrypt2" := DDBS("more text"),
129+
"sign1" := DDBS("stuff"),
130+
"sign2" := DDB.AttributeValue.BOOL(false)
131+
];
132+
encryptRes :- expect encryptor.EncryptItem(
133+
Types.EncryptItemInput(
134+
plaintextItem:=inputItem
135+
)
136+
);
137+
var item3 := encryptRes.encryptedItem;
138+
expect "encrypt1" in item3;
139+
expect item3["encrypt1"] != DDBS("some text");
140+
141+
var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransforms2(Actions1, Some("sortKey"));
142+
var tableName := GetTableName("foo");
143+
var input := DDB.QueryInput(
144+
TableName := tableName
145+
);
146+
147+
var transformed := middlewareUnderTest.QueryOutputTransform(
148+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.QueryOutputTransformInput(
149+
sdkOutput := DDB.QueryOutput(Items := Some([item1, item2, item3])),
150+
originalInput := input
151+
)
152+
);
153+
154+
TestFixtures.expect_ok("QueryOutput", transformed);
155+
expect transformed.value.transformedOutput.Items.Some?;
156+
var itemList := transformed.value.transformedOutput.Items.value;
157+
expect |itemList| == 3;
158+
expect "encrypt1" in itemList[0];
159+
expect itemList[0]["encrypt1"] == DDBS("some text");
160+
161+
162+
/// now do some damage
163+
item1 := item1["encrypt1" := item2["encrypt1"]];
164+
transformed := middlewareUnderTest.QueryOutputTransform(
165+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.QueryOutputTransformInput(
166+
sdkOutput := DDB.QueryOutput(Items := Some([item1, item2, item3])),
167+
originalInput := input
168+
)
169+
);
170+
expect transformed.Failure?;
171+
print "\n", transformed.error, "\n";
172+
expect transformed.error ==
173+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
174+
[
175+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
176+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304")
177+
],
178+
message := "Error(s) found decrypting Query results."
179+
);
180+
181+
/// do more damage
182+
item3 := item3["encrypt1" := item2["encrypt1"]];
183+
transformed := middlewareUnderTest.QueryOutputTransform(
184+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.QueryOutputTransformInput(
185+
sdkOutput := DDB.QueryOutput(Items := Some([item1, item2, item3])),
186+
originalInput := input
187+
)
188+
);
189+
expect transformed.Failure?;
190+
print "\n", transformed.error, "\n";
191+
expect transformed.error ==
192+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
193+
[
194+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
195+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304"),
196+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890\nsortKey = 030104")
197+
],
198+
message := "Error(s) found decrypting Query results."
199+
);
200+
201+
var transformed_scan := middlewareUnderTest.ScanOutputTransform(
202+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.ScanOutputTransformInput(
203+
sdkOutput := DDB.ScanOutput(Items := Some([item1, item2, item3])),
204+
originalInput := DDB.ScanInput(TableName := tableName)
205+
)
206+
);
207+
expect transformed_scan.Failure?;
208+
print "\n", transformed_scan.error, "\n";
209+
expect transformed_scan.error ==
210+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.CollectionOfErrors(
211+
[
212+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptor(AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.Error.AwsCryptographyDbEncryptionSdkDynamoDb(AwsCryptographyDbEncryptionSdkDynamoDbTypes.Error.AwsCryptographyDbEncryptionSdkStructuredEncryption(AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.Error.StructuredEncryptionException(message := "No recipient tag matched.")))),
213+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 1234\nsortKey = 01020304"),
214+
AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.Error.DynamoDbEncryptionTransformsException(message := "bar = 890\nsortKey = 030104")
215+
],
216+
message := "Error(s) found decrypting Scan results."
217+
);
218+
}
84219
}

‎DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/TestFixtures.dfy‎

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ module TestFixtures {
2323
expect DDB.IsValid_TableName(s);
2424
return s;
2525
}
26-
method GetAttrName(s : string) returns (output : DDB.AttributeName)
26+
functionmethod GetAttrName(s : string) : DDB.AttributeName
2727
{
28-
expect DDB.IsValid_AttributeName(s);
29-
return s;
28+
if DDB.IsValid_AttributeName(s) then
29+
s
30+
else
31+
"abc"
3032
}
3133
method GetStatement(s : string) returns (output : DDB.PartiQLStatement)
3234
{
@@ -235,6 +237,32 @@ module TestFixtures {
235237
assume {:axiom} fresh(encryption.Modifies);
236238
}
237239

240+
method GetDynamoDbEncryptionTransforms2(actions : AttributeActions, sortKey : Option<string>)
241+
returns (encryption: DynamoDbEncryptionTransforms.DynamoDbEncryptionTransformsClient)
242+
ensures encryption.ValidState()
243+
ensures fresh(encryption)
244+
ensures fresh(encryption.Modifies)
245+
{
246+
var keyring := GetKmsKeyring();
247+
encryption :- expect DynamoDbEncryptionTransforms.DynamoDbEncryptionTransforms(
248+
DynamoDbTablesEncryptionConfig(
249+
tableEncryptionConfigs := map[
250+
"foo" := DynamoDbTableEncryptionConfig(
251+
logicalTableName := "foo",
252+
partitionKeyName := "bar",
253+
sortKeyName := sortKey,
254+
attributeActionsOnEncrypt := actions,
255+
allowedUnsignedAttributes := Some(["plain"]),
256+
allowedUnsignedAttributePrefix := None(),
257+
algorithmSuiteId := None(),
258+
keyring := Some(keyring)
259+
)
260+
]
261+
)
262+
);
263+
assume {:axiom} fresh(encryption.Modifies);
264+
}
265+
238266
// type AttributeActions = map<ComAmazonawsDynamodbTypes.AttributeName, AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.CryptoAction>
239267

240268
const MultiActions : AttributeActions :=

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /