-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathScanTransform.dfy
119 lines (105 loc) · 5.9 KB
/
ScanTransform.dfy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
include "DynamoDbMiddlewareSupport.dfy"
module ScanTransform {
import opened DdbMiddlewareConfig
import opened DynamoDbMiddlewareSupport
import opened Wrappers
import DDB = ComAmazonawsDynamodbTypes
import opened AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes
import EncTypes = AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes
import EncOps = AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations
method Input(config: Config, input: ScanInputTransformInput)
returns (output: Result<ScanInputTransformOutput, Error>)
requires ValidConfig?(config)
ensures ValidConfig?(config)
modifies ModifiesConfig(config)
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#modify-before-scan
//= type=implication
//# If the `TableName` in the request does not refer to an [encrypted-table](#encrypted-table),
//# the Scan request MUST be unchanged.
ensures input.sdkInput.TableName !in config.tableEncryptionConfigs ==>
&& output.Success?
&& output.value.transformedInput == input.sdkInput
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#modify-before-scan
//= type=implication
//# The Scan request MUST NOT refer to any legacy parameters,
//# specifically AttributesToGet, ScanFilter and ConditionalOperator MUST NOT be set.
ensures output.Success? && input.sdkInput.TableName in config.tableEncryptionConfigs ==>
&& NoList(input.sdkInput.AttributesToGet)
&& NoMap(input.sdkInput.ScanFilter)
&& input.sdkInput.ConditionalOperator.None?
{
if input.sdkInput.TableName !in config.tableEncryptionConfigs {
return Success(ScanInputTransformOutput(transformedInput := input.sdkInput));
} else {
:- Need(NoList(input.sdkInput.AttributesToGet), E("Legacy parameter 'AttributesToGet' not supported in UpdateItem with Encryption"));
:- Need(NoMap(input.sdkInput.ScanFilter), E("Legacy parameter 'ScanFilter' not supported in UpdateItem with Encryption"));
:- Need(input.sdkInput.ConditionalOperator.None?, E("Legacy parameter 'ConditionalOperator' not supported in UpdateItem with Encryption"));
var tableConfig := config.tableEncryptionConfigs[input.sdkInput.TableName];
var finalResult :- ScanInputForBeacons(tableConfig, input.sdkInput);
return Success(ScanInputTransformOutput(transformedInput := finalResult));
}
}
function Diff(x : nat, y : nat) : nat
requires x >= y
{
x - y
}
method {:vcs_split_on_every_assert} Output(config: Config, input: ScanOutputTransformInput)
returns (output: Result<ScanOutputTransformOutput, Error>)
requires ValidConfig?(config)
ensures ValidConfig?(config)
modifies ModifiesConfig(config)
ensures input.originalInput.TableName !in config.tableEncryptionConfigs || NoList(input.sdkOutput.Items) ==>
&& output.Success?
&& output.value.transformedOutput == input.sdkOutput
ensures output.Success? && NoList(input.sdkOutput.Items) ==> NoList(output.value.transformedOutput.Items)
ensures output.Success? && input.sdkOutput.Items.Some? ==> output.value.transformedOutput.Items.Some?
{
var tableName := input.originalInput.TableName;
if tableName !in config.tableEncryptionConfigs || NoList(input.sdkOutput.Items) {
return Success(ScanOutputTransformOutput(transformedOutput := input.sdkOutput));
}
var tableConfig := config.tableEncryptionConfigs[tableName];
var decryptedItems : DDB.ItemList := [];
var encryptedItems := input.sdkOutput.Items.value;
var keyId :- GetBeaconKeyId(tableConfig, None, input.originalInput.FilterExpression, input.originalInput.ExpressionAttributeValues, input.originalInput.ExpressionAttributeNames);
var keyIdUtf8 := [];
if keyId.KeyId? {
keyIdUtf8 :- UTF8.Encode(keyId.value).MapFailure(e => E(e));
}
for x := 0 to |encryptedItems|
{
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-scan
//# Each of these entries on the original response MUST be replaced
//# with the resulting decrypted
//# [DynamoDB Item](./decrypt-item.md#dynamodb-item-1).
var decryptInput := EncTypes.DecryptItemInput(encryptedItem := encryptedItems[x]);
var decryptRes := tableConfig.itemEncryptor.DecryptItem(decryptInput);
var decrypted :- MapError(decryptRes);
// If the decrypted result was plaintext, i.e. has no parsedHeader
// then this is expected IFF the table config allows plaintext read
assert decrypted.parsedHeader.None? ==>
&& EncOps.IsPlaintextItem(encryptedItems[x])
&& !tableConfig.plaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ?
&& (
|| tableConfig.plaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ?
|| tableConfig.plaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ?
);
if keyId.KeyId? && decrypted.parsedHeader.Some? {
:- Need(|decrypted.parsedHeader.value.encryptedDataKeys| == 1, E("Scan result has more than one Encrypted Data Key"));
if decrypted.parsedHeader.value.encryptedDataKeys[0].keyProviderInfo == keyIdUtf8 {
decryptedItems := decryptedItems + [decrypted.plaintextItem];
}
} else {
decryptedItems := decryptedItems + [decrypted.plaintextItem];
}
}
//= specification/dynamodb-encryption-client/ddb-sdk-integration.md#decrypt-after-scan
//# The resulting decrypted response MUST be [filtered](ddb-support.md#scanoutputforbeacons) from the result.
var decryptedOutput := input.sdkOutput.(Items := Some(decryptedItems));
var finalResult :- ScanOutputForBeacons(tableConfig, input.originalInput, decryptedOutput);
return Success(ScanOutputTransformOutput(transformedOutput := finalResult));
}
}