Skip to content

Commit ea94693

Browse files
authored
chore(dafny): further performance enhancements (#1834)
* chore(dafny): further performance enhancements
1 parent 3194054 commit ea94693

File tree

13 files changed

+667
-279
lines changed

13 files changed

+667
-279
lines changed

.github/workflows/ci_test_vector_net.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
- name: Test TestVectors on .NET 6.0
9090
working-directory: ./${{matrix.library}}/runtimes/net
9191
run: |
92-
cp ../java/decrypt_java_*.json ../java/decrypt_dotnet_*.json ../java/decrypt_rust_*.json .
92+
cp ../java/decrypt_java_*.json ../java/decrypt_dotnet_*.json ../java/decrypt_rust_*.json ../java/large_records.json .
9393
dotnet run
9494
cp ../java/*.json .
9595
dotnet run --framework net6.0

DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy

+442-168
Large diffs are not rendered by default.

DynamoDbEncryption/dafny/DynamoDbEncryption/src/Index.dfy

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ include "DDBSupport.dfy"
99
include "DynamoDbEncryptionBranchKeyIdSupplier.dfy"
1010
include "DynamoToStruct.dfy"
1111
include "FilterExpr.dfy"
12+
include "MemoryMath.dfy"
1213
include "NormalizeNumber.dfy"
1314
include "SearchInfo.dfy"
1415
include "TermLoc.dfy"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// When dealing with actual data in actual memory, we can be confident that
5+
// none of the numbers will exceed an exabyte, so we can use uint64, rather than nat.
6+
// To convince Dafny that this is true, we have the following functions
7+
// with assumptions as needed.
8+
9+
10+
include "../Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy"
11+
12+
module {:options "--function-syntax:4"} MemoryMath {
13+
14+
import opened BoundedInts
15+
16+
17+
// This is safe because it is being held in memory
18+
lemma {:axiom} ValueIsSafeBecauseItIsInMemory(value : nat)
19+
ensures value < UINT64_MAX as nat
20+
21+
function {:opaque} Add(x : uint64, y : uint64) : (ret : uint64)
22+
ensures ret < UINT64_MAX as uint64
23+
ensures ret as nat == x as nat + y as nat
24+
{
25+
ValueIsSafeBecauseItIsInMemory(x as nat + y as nat);
26+
x + y
27+
}
28+
29+
function {:opaque} Add3(x : uint64, y : uint64, z : uint64) : (ret : uint64)
30+
ensures ret < UINT64_MAX as uint64
31+
ensures ret as nat == x as nat + y as nat + z as nat
32+
{
33+
ValueIsSafeBecauseItIsInMemory(x as nat + y as nat + z as nat);
34+
x + y + z
35+
}
36+
37+
38+
}

DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy

+24-3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,25 @@ module SearchableEncryptionInfo {
223223
return Success(keyLoc.keys);
224224
}
225225

226+
function method PosLongAdd(x : MP.PositiveLong, y : MP.PositiveLong) : MP.PositiveLong
227+
{
228+
assert MP.IsValid_PositiveLong(x);
229+
assert MP.IsValid_PositiveLong(y);
230+
if x as nat + y as nat < INT64_MAX_LIMIT then
231+
x + y
232+
else
233+
INT64_MAX_LIMIT as MP.PositiveLong
234+
}
235+
function method PosLongSub(x : MP.PositiveLong, y : MP.PositiveLong) : MP.PositiveLong
236+
{
237+
assert MP.IsValid_PositiveLong(x);
238+
assert MP.IsValid_PositiveLong(y);
239+
if x <= y then
240+
0
241+
else
242+
x - y
243+
}
244+
226245
// Checks if (time_now - cache creation time of the extracted cache entry) is less than the allowed
227246
// TTL of the current Beacon Key Source calling the getEntry method from the cache.
228247
// Mitigates risk if another Beacon Key Source wrote the entry with a longer TTL.
@@ -232,7 +251,10 @@ module SearchableEncryptionInfo {
232251
ttlSeconds: MP.PositiveLong
233252
): (output: bool)
234253
{
235-
now - creationTime <= ttlSeconds as MP.PositiveLong
254+
if now <= creationTime then
255+
true
256+
else
257+
PosLongSub(now, creationTime) <= ttlSeconds as MP.PositiveLong
236258
}
237259

238260
method getKeysCache(
@@ -406,7 +428,6 @@ module SearchableEncryptionInfo {
406428
var keyMap :- getAllKeys(stdNames, key.value);
407429
var beaconKeyMaterials := rawBeaconKeyMaterials.beaconKeyMaterials.(beaconKey := None, hmacKeys := Some(keyMap));
408430

409-
expect now < UInt.BoundedInts.INT64_MAX - cacheTTL;
410431
//= specification/searchable-encryption/search-config.md#get-beacon-key-materials
411432
//# These materials MUST be put into the associated [Key Store Cache](#key-store-cache)
412433
//# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time)
@@ -415,7 +436,7 @@ module SearchableEncryptionInfo {
415436
identifier := cacheDigest,
416437
materials := MP.Materials.BeaconKey(beaconKeyMaterials),
417438
creationTime := now,
418-
expiryTime := now + cacheTTL,
439+
expiryTime := PosLongAdd(now, cacheTTL),
419440
messagesUsed := None,
420441
bytesUsed := None
421442
);

DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module DynamoDbEncryptionUtil {
1414
const BeaconPrefix := "aws_dbe_b_"
1515
const VersionPrefix := "aws_dbe_v_"
1616

17-
const MAX_STRUCTURE_DEPTH := 32
17+
const MAX_STRUCTURE_DEPTH : uint64 := 32
1818
const MAX_STRUCTURE_DEPTH_STR := "32"
1919

2020
type HmacKeyMap = map<string, Bytes>

DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy

+31-42
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
417417
ensures version == 1 <==> ret == CSE.SIGN_ONLY
418418
ensures version == 2 <==> ret == CSE.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT
419419
{
420+
assert StructuredEncryptionHeader.ValidVersion(version);
421+
assert version == 1 || version == 2;
420422
if version == 2 then
421423
CSE.SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT
422424
else
@@ -546,7 +548,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
546548
// get CryptoSchema for this item
547549
function method ConfigToCryptoSchema(
548550
config : InternalConfig,
549-
item : ComAmazonawsDynamodbTypes.AttributeMap)
551+
item : DynamoToStruct.TerminalDataMap)
550552
: (ret : Result<CSE.CryptoSchemaMap, DDBE.Error>)
551553

552554
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
@@ -590,7 +592,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
590592
// get AuthenticateSchema for this item
591593
function method ConfigToAuthenticateSchema(
592594
config : InternalConfig,
593-
item : ComAmazonawsDynamodbTypes.AttributeMap)
595+
item : DynamoToStruct.TerminalDataMap)
594596
: (ret : CSE.AuthenticateSchemaMap)
595597
requires ValidInternalConfig?(config)
596598

@@ -636,6 +638,8 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
636638
ensures ret.Success? ==> forall k <- ret.value.Keys :: InSignatureScope(config, k)
637639
ensures ret.Success? ==> forall k <- ret.value.Keys :: !ret.value[k].DO_NOTHING?
638640
{
641+
assert forall k <- schema :: SE.IsAuthAttr(schema[k]);
642+
assert forall k <- schema :: !schema[k].DO_NOTHING?;
639643
:- Need(forall k <- schema :: InSignatureScope(config, k),
640644
DynamoDbItemEncryptorException( message := "Received unexpected Crypto Schema: mismatch with signature scope"));
641645
:- Need(forall k <- schema :: ComAmazonawsDynamodbTypes.IsValid_AttributeName(k),
@@ -747,22 +751,22 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
747751
&& (|config.structuredEncryption.History.EncryptStructure| == |old(config.structuredEncryption.History.EncryptStructure)| + 1)
748752
&& (Seq.Last(config.structuredEncryption.History.EncryptStructure).output.Success?)
749753

750-
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
751-
//= type=implication
752-
//# - Crypto Schema MUST be a [Crypto Schema](../structured-encryption/structures.md#crypto-schema)
753-
//# analogous to the [configured Attribute Actions](./ddb-table-encryption-config.md#attribute-actions).
754-
&& ConfigToCryptoSchema(config, input.plaintextItem).Success?
755-
&& Seq.Last(config.structuredEncryption.History.EncryptStructure).input.cryptoSchema
756-
== ConfigToCryptoSchema(config, input.plaintextItem).value
757-
758754
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
759755
//= type=implication
760756
//# - Structured Data MUST be the Structured Data converted above.
761-
&& DynamoToStruct.ItemToStructured(input.plaintextItem).Success?
762-
&& var plaintextStructure := DynamoToStruct.ItemToStructured(input.plaintextItem).value;
757+
&& DynamoToStruct.ItemToStructured2(input.plaintextItem, config.attributeActionsOnEncrypt).Success?
758+
&& var plaintextStructure := DynamoToStruct.ItemToStructured2(input.plaintextItem, config.attributeActionsOnEncrypt).value;
763759
&& Seq.Last(config.structuredEncryption.History.EncryptStructure).input.plaintextStructure
764760
== plaintextStructure
765761

762+
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
763+
//= type=implication
764+
//# - Crypto Schema MUST be a [Crypto Schema](../structured-encryption/structures.md#crypto-schema)
765+
//# analogous to the [configured Attribute Actions](./ddb-table-encryption-config.md#attribute-actions).
766+
&& ConfigToCryptoSchema(config, plaintextStructure).Success?
767+
&& Seq.Last(config.structuredEncryption.History.EncryptStructure).input.cryptoSchema
768+
== ConfigToCryptoSchema(config, plaintextStructure).value
769+
766770
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
767771
//= type=implication
768772
//# - Encryption Context MUST be this input Item's [DynamoDB Item Base Context](#dynamodb-item-base-context).
@@ -800,8 +804,6 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
800804
==>
801805
&& output.value.encryptedItem == input.plaintextItem
802806
&& output.value.parsedHeader == None
803-
804-
ensures output.Success? ==> |input.plaintextItem| <= MAX_ATTRIBUTE_COUNT
805807
{
806808
:- Need(
807809
&& config.partitionKeyName in input.plaintextItem
@@ -811,12 +813,6 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
811813
:- Need(ContextAttrsExist(config.attributeActionsOnEncrypt, input.plaintextItem),
812814
E(ContextMissingMsg(config.attributeActionsOnEncrypt, input.plaintextItem)));
813815

814-
if |input.plaintextItem| > MAX_ATTRIBUTE_COUNT {
815-
var actCount := String.Base10Int2String(|input.plaintextItem|);
816-
var maxCount := String.Base10Int2String(MAX_ATTRIBUTE_COUNT);
817-
return Failure(E("Item to encrypt had " + actCount + " attributes, but maximum allowed is " + maxCount));
818-
}
819-
820816
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
821817
//# If a [Legacy Policy](./ddb-table-encryption-config.md#legacy-policy) of
822818
//# `FORCE_LEGACY_ENCRYPT_ALLOW_LEGACY_DECRYPT` is specified,
@@ -839,10 +835,10 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
839835
return Success(passthroughOutput);
840836
}
841837

842-
var plaintextStructure :- DynamoToStruct.ItemToStructured(input.plaintextItem)
838+
var plaintextStructure :- DynamoToStruct.ItemToStructured2(input.plaintextItem, config.attributeActionsOnEncrypt)
843839
.MapFailure(e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(e));
844840
var context :- MakeEncryptionContextForEncrypt(config, plaintextStructure);
845-
var cryptoSchema :- ConfigToCryptoSchema(config, input.plaintextItem)
841+
var cryptoSchema :- ConfigToCryptoSchema(config, plaintextStructure)
846842
.MapFailure(e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(e));
847843

848844
//= specification/dynamodb-encryption-client/encrypt-item.md#behavior
@@ -893,7 +889,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
893889
e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(DDBE.AwsCryptographyDbEncryptionSdkStructuredEncryption(e)));
894890
var encryptedData := encryptVal.encryptedStructure;
895891
:- Need(forall k <- encryptedData :: DDB.IsValid_AttributeName(k), E(""));
896-
var ddbKey :- DynamoToStruct.StructuredToItem(encryptedData)
892+
var ddbKey :- DynamoToStruct.StructuredToItemEncrypt(encryptedData, input.plaintextItem, config.attributeActionsOnEncrypt)
897893
.MapFailure(e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(e));
898894

899895
var parsedActions :- ConvertCryptoSchemaToAttributeActions(config, encryptVal.cryptoSchema);
@@ -957,21 +953,21 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
957953
&& (|config.structuredEncryption.History.DecryptStructure| == |old(config.structuredEncryption.History.DecryptStructure)| + 1)
958954
&& (Seq.Last(config.structuredEncryption.History.DecryptStructure).output.Success?)
959955

960-
//= specification/dynamodb-encryption-client/decrypt-item.md#behavior
961-
//= type=implication
962-
//# - Authenticate Schema MUST be a [Authenticate Schema](../structured-encryption/structures.md#crypto-schema)
963-
//# built with the following requirements:
964-
&& Seq.Last(config.structuredEncryption.History.DecryptStructure).input.authenticateSchema
965-
== ConfigToAuthenticateSchema(config, input.encryptedItem)
966-
967956
//= specification/dynamodb-encryption-client/decrypt-item.md#behavior
968957
//= type=implication
969958
//# - Encrypted Structured Data MUST be the Structured Data converted above.
970-
&& DynamoToStruct.ItemToStructured(input.encryptedItem).Success?
971-
&& var plaintextStructure := DynamoToStruct.ItemToStructured(input.encryptedItem).value;
959+
&& DynamoToStruct.ItemToStructured2(input.encryptedItem, config.attributeActionsOnEncrypt).Success?
960+
&& var plaintextStructure := DynamoToStruct.ItemToStructured2(input.encryptedItem, config.attributeActionsOnEncrypt).value;
972961
&& Seq.Last(config.structuredEncryption.History.DecryptStructure).input.encryptedStructure
973962
== plaintextStructure
974963

964+
//= specification/dynamodb-encryption-client/decrypt-item.md#behavior
965+
//= type=implication
966+
//# - Authenticate Schema MUST be a [Authenticate Schema](../structured-encryption/structures.md#crypto-schema)
967+
//# built with the following requirements:
968+
&& Seq.Last(config.structuredEncryption.History.DecryptStructure).input.authenticateSchema
969+
== ConfigToAuthenticateSchema(config, plaintextStructure)
970+
975971
//= specification/dynamodb-encryption-client/decrypt-item.md#dynamodb-item-base-context
976972
//= type=implication
977973
//# The item to be encrypted MUST have an attribute named `aws_dbe_head`.
@@ -1037,13 +1033,6 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
10371033
&& output.value.plaintextItem == input.encryptedItem
10381034
&& output.value.parsedHeader == None
10391035
{
1040-
var realCount := |set k <- input.encryptedItem | !(ReservedPrefix <= k)|;
1041-
if realCount > MAX_ATTRIBUTE_COUNT {
1042-
var actCount := String.Base10Int2String(realCount);
1043-
var maxCount := String.Base10Int2String(MAX_ATTRIBUTE_COUNT);
1044-
return Failure(E("Item to decrypt had " + actCount + " attributes, but maximum allowed is " + maxCount));
1045-
}
1046-
10471036
:- Need(
10481037
&& config.partitionKeyName in input.encryptedItem
10491038
&& (config.sortKeyName.None? || config.sortKeyName.value in input.encryptedItem)
@@ -1081,15 +1070,15 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
10811070
DynamoDbItemEncryptorException(
10821071
message := "Encrypted item missing expected header and footer attributes"));
10831072

1084-
var encryptedStructure :- DynamoToStruct.ItemToStructured(input.encryptedItem)
1073+
var encryptedStructure :- DynamoToStruct.ItemToStructured2(input.encryptedItem, config.attributeActionsOnEncrypt)
10851074
.MapFailure(e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(e));
10861075
:- Need(SE.HeaderField in input.encryptedItem, E("Header field, \"aws_dbe_head\", not in item."));
10871076
var header := input.encryptedItem[SE.HeaderField];
10881077
:- Need(header.B?, E("Header field, \"aws_dbe_head\", not binary"));
10891078
assert header.B?;
10901079
:- Need(0 < |header.B|, E("Unexpected empty header field."));
10911080
var context :- MakeEncryptionContextForDecrypt(config, header.B, encryptedStructure);
1092-
var authenticateSchema := ConfigToAuthenticateSchema(config, input.encryptedItem);
1081+
var authenticateSchema := ConfigToAuthenticateSchema(config, encryptedStructure);
10931082

10941083
//= specification/dynamodb-encryption-client/decrypt-item.md#behavior
10951084
//# This operation MUST create a
@@ -1123,7 +1112,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs
11231112
e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(DDBE.AwsCryptographyDbEncryptionSdkStructuredEncryption(e)));
11241113
var decryptedData := decryptVal.plaintextStructure;
11251114
:- Need(forall k <- decryptedData :: DDB.IsValid_AttributeName(k), E(""));
1126-
var ddbItem :- DynamoToStruct.StructuredToItem(decryptedData)
1115+
var ddbItem :- DynamoToStruct.StructuredToItemDecrypt(decryptedData, input.encryptedItem, decryptVal.cryptoSchema)
11271116
.MapFailure(e => Error.AwsCryptographyDbEncryptionSdkDynamoDb(e));
11281117

11291118
var schemaToConvert := decryptVal.cryptoSchema;

DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/Util.dfy

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ module DynamoDbItemEncryptorUtil {
1414
import SortedSets
1515
import SE = StructuredEncryptionUtil
1616
import DynamoToStruct
17+
import MemoryMath
1718

1819
const ReservedPrefix := "aws_dbe_"
1920
const BeaconPrefix := ReservedPrefix + "b_"
2021
const VersionPrefix := ReservedPrefix + "v_"
21-
const MAX_ATTRIBUTE_COUNT := 100
2222

2323
function method E(msg : string) : Error
2424
{
@@ -181,7 +181,8 @@ module DynamoDbItemEncryptorUtil {
181181
Success(value)
182182
else if legend == SE.LEGEND_BINARY then
183183
var terminal :- SE.DecodeTerminal(ecValue);
184-
var ddbAttrValue :- DynamoToStruct.BytesToAttr(terminal.value, terminal.typeId, false);
184+
MemoryMath.ValueIsSafeBecauseItIsInMemory(|terminal.value|);
185+
var ddbAttrValue :- DynamoToStruct.BytesToAttr(terminal.value, terminal.typeId, Some(|terminal.value| as uint64));
185186
Success(ddbAttrValue.val)
186187
else
187188
Failure("Encryption Context Legend has unexpected character : '" + [legend] + "'.")
@@ -236,7 +237,8 @@ module DynamoDbItemEncryptorUtil {
236237

237238
// Obtain attribute value from EC kvPair value
238239
var terminal :- SE.DecodeTerminal(encodedAttrValue);
239-
var ddbAttrValue :- DynamoToStruct.BytesToAttr(terminal.value, terminal.typeId, false);
240+
MemoryMath.ValueIsSafeBecauseItIsInMemory(|terminal.value|);
241+
var ddbAttrValue :- DynamoToStruct.BytesToAttr(terminal.value, terminal.typeId, Some(|terminal.value| as uint64));
240242

241243
// Add to our AttributeMap
242244
Success(attrMap[ddbAttrName := ddbAttrValue.val])

0 commit comments

Comments
 (0)