From 58f411ea649774e8a46a5194104833774a05a50e Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 20 Nov 2024 12:51:51 -0800 Subject: [PATCH 01/29] first commit --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 27 +++++++++++++++++-- .../DynamoDbEncryption/src/SearchInfo.dfy | 17 +++++++++--- .../dafny/DynamoDbEncryption/src/Util.dfy | 2 +- .../src/Index.dfy | 2 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 09f9393e2..ed3ab8dc0 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -33,6 +33,7 @@ module SearchConfigToInfo { import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import MPT = AwsCryptographyMaterialProvidersTypes import Primitives = AtomicPrimitives + import UUID // convert configured SearchConfig to internal SearchInfo method Convert(outer : DynamoDbTableEncryptionConfig) @@ -151,13 +152,35 @@ module SearchConfigToInfo { cache :- maybeCache.MapFailure(e => AwsCryptographyMaterialProviders(e)); } + var partitionIdBytes : seq; + + if outer.keyring.Some? { + if outer.keyring.value.partitionId.Some? { + partitionIdBytes :- UTF8.Encode(outer.keyring.value.partitionId.value) + .MapFailure( + e => Error.DynamoDbEncryptionException( + message := "Could not UTF-8 Encode Partition ID: " + e + ) + ); + } + } + else { + var uuid? := UUID.GenerateUUID(); + + var uuid :- uuid? + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + + partitionIdBytes :- UUID.ToByteArray(uuid) + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + } + if config.multi? { :- Need(0 < config.multi.cacheTTL, E("Beacon Cache TTL must be at least 1.")); var deleteKey :- ShouldDeleteKeyField(outer, config.multi.keyFieldName); - output := Success(I.KeySource(client, keyStore, I.MultiLoc(config.multi.keyFieldName, deleteKey), cache, config.multi.cacheTTL as uint32)); + output := Success(I.KeySource(client, keyStore, I.MultiLoc(config.multi.keyFieldName, deleteKey), cache, config.multi.cacheTTL as uint32, partitionIdBytes)); } else { :- Need(0 < config.single.cacheTTL, E("Beacon Cache TTL must be at least 1.")); - output := Success(I.KeySource(client, keyStore, I.SingleLoc(config.single.keyId), cache, config.single.cacheTTL as uint32)); + output := Success(I.KeySource(client, keyStore, I.SingleLoc(config.single.keyId), cache, config.single.cacheTTL as uint32, partitionIdBytes)); } } diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 92a730fad..16a7359da 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -26,6 +26,7 @@ module SearchableEncryptionInfo { import MP = AwsCryptographyMaterialProvidersTypes import KeyStoreTypes = AwsCryptographyKeyStoreTypes import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes + import CacheConstants //= specification/searchable-encryption/search-config.md#version-number //= type=implication @@ -137,7 +138,8 @@ module SearchableEncryptionInfo { store : ValidStore, keyLoc : KeyLocation, cache : MP.ICryptographicMaterialsCache, - cacheTTL : uint32 + cacheTTL : uint32, + partitionIdBytes : seq ) { function Modifies() : set { client.Modifies + store.Modifies @@ -153,7 +155,7 @@ module SearchableEncryptionInfo { { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); - var theMap :- getKeysCache(stdNames, keyLoc.keyId); + var theMap :- getKeysCache(stdNames, keyLoc.keyId, partitionIdBytes); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -163,7 +165,7 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must not be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var theMap :- getKeysCache(stdNames, id); return Success(Keys(theMap)); + case KeyId(id) => var theMap :- getKeysCache(stdNames, id, partitionIdBytes); return Success(Keys(theMap)); } } } @@ -182,7 +184,8 @@ module SearchableEncryptionInfo { method getKeysCache( stdNames : seq, - keyId : string + keyId : string, + partitionIdBytes : seq ) returns (output : Result) requires Seq.HasNoDuplicates(stdNames) @@ -241,6 +244,11 @@ module SearchableEncryptionInfo { ) { + + // Resource ID: Searchable Encryption [0x02] + // var resourceId : seq := RESOURCE_ID_HIERARCHICAL_KEYRING; + + var keyIdBytesR := UTF8.Encode(keyId); var keyIdBytes :- keyIdBytesR.MapFailure(e => E(e)); var getCacheInput := MP.GetCacheEntryInput(identifier := keyIdBytes, bytesUsed := None); @@ -253,6 +261,7 @@ module SearchableEncryptionInfo { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=getCacheOutput.error)); } + // TODO: Add cacheEntryWithinLimits if getCacheOutput.Failure? { //= specification/searchable-encryption/search-config.md#beacon-keys //# Beacon keys MUST be obtained from the configured [Beacon Key Source](#beacon-key-source). diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy index f839c551d..b0ec519f3 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy @@ -26,7 +26,7 @@ module DynamoDbEncryptionUtil { // // Counterintuitively, DontUseKeys and DontUseKeyId are very different things. // DontUseKeyId is the usual thing for single tenant, meaning to use the pre-configured - // KeyId, rather than fnd a new one from somewhere. + // KeyId, rather than find a new one from somewhere. // DontUseKeys means to not hash the values into beacons, // but to leave them plaintext, which is necessary for the post-query filtering. datatype MaybeKeyMap = diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy index f44f59b6c..4bdb3256a 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy @@ -150,7 +150,7 @@ module var tableName: string := tableNamesSeq[i]; var inputConfig := config.tableEncryptionConfigs[tableName]; - :- Need(inputConfig.logicalTableName !in allLogicalTableNames, E("Duplicate logical table maped to multipule physical tables: " + inputConfig.logicalTableName)); + :- Need(inputConfig.logicalTableName !in allLogicalTableNames, E("Duplicate logical table mapped to multiple physical tables: " + inputConfig.logicalTableName)); assert SearchConfigToInfo.ValidSearchConfig(inputConfig.search); SearchInModifies(config, tableName); From 57f6ea67cf6f2fc875899f894ee9fb85e8bcc18f Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 20 Nov 2024 15:17:05 -0800 Subject: [PATCH 02/29] update smithy models --- ...yptographyDbEncryptionSdkDynamoDbTypes.dfy | 7 ++-- .../Model/DynamoDbEncryption.smithy | 11 +++++-- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 1 + .../DynamoDbEncryption/src/SearchInfo.dfy | 12 +++++-- ...DbEncryptionSdkDynamoDbTransformsTypes.dfy | 32 +++++++++---------- 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy index 48fe485e0..118a5af53 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy @@ -354,7 +354,8 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald datatype MultiKeyStore = | MultiKeyStore ( nameonly keyFieldName: string , nameonly cacheTTL: int32 , - nameonly cache: Option := Option.None + nameonly cache: Option := Option.None , + nameonly partitionId: Option := Option.None ) datatype PartOnly = | PartOnly ( @@ -388,7 +389,9 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald } datatype SingleKeyStore = | SingleKeyStore ( nameonly keyId: string , - nameonly cacheTTL: int32 + nameonly cacheTTL: Option := Option.None , + nameonly cache: Option := Option.None , + nameonly partitionId: Option := Option.None ) datatype StandardBeacon = | StandardBeacon ( nameonly name: string , diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy index 64b4c82ac..5b4c2ac6d 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy @@ -713,9 +713,12 @@ structure SingleKeyStore { @required @javadoc("The Beacon Key ID.") keyId : String, - @required - @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS.") + @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS. Provide only one of cacheTTL or cache.") cacheTTL: Integer, + @documentation("Provide the Shared Cache for Searchable Encryption. Provide only one of cacheTTL or cache.") + cache : CacheType, + @documentation("Partition ID to share DynamoDB Interceptors. TODO: Update description") + partitionId: String } //= specification/searchable-encryption/search-config.md#multi-key-store-initialization @@ -734,7 +737,9 @@ structure MultiKeyStore { @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS.") cacheTTL: Integer, @javadoc("Which type of local cache to use.") - cache : CacheType + cache : CacheType, + @documentation("Partition ID to share DynamoDB Interceptors. TODO: Update description") + partitionId: String } //= specification/searchable-encryption/search-config.md#beacon-key-source diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index ed3ab8dc0..629b3aeb0 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -138,6 +138,7 @@ module SearchConfigToInfo { else MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); + // TODO : Add check that customers only provide either cacheTTL or cache in case of SingleKeyStore var cache; if cacheType.Shared? { cache := cacheType.Shared; diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 16a7359da..ff514598f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -26,7 +26,7 @@ module SearchableEncryptionInfo { import MP = AwsCryptographyMaterialProvidersTypes import KeyStoreTypes = AwsCryptographyKeyStoreTypes import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes - import CacheConstants + import opened CacheConstants //= specification/searchable-encryption/search-config.md#version-number //= type=implication @@ -246,11 +246,19 @@ module SearchableEncryptionInfo { { // Resource ID: Searchable Encryption [0x02] - // var resourceId : seq := RESOURCE_ID_HIERARCHICAL_KEYRING; + var resourceId : seq := RESOURCE_ID_HIERARCHICAL_KEYRING; + // Scope ID: Searchable Encryption [0x03] + var scopeId : seq := SCOPE_ID_SEARCHABLE_ENCRYPTION; + // Create the Suffix var keyIdBytesR := UTF8.Encode(keyId); var keyIdBytes :- keyIdBytesR.MapFailure(e => E(e)); + var suffix : seq := keyIdBytes; + + // Append Resource Id, Scope Id, Partition Id, and Suffix to create the cache identifier + var identifier := resourceId + NULL_BYTE + scopeId + NULL_BYTE + partitionIdBytes + NULL_BYTE + suffix; + var getCacheInput := MP.GetCacheEntryInput(identifier := keyIdBytes, bytesUsed := None); verifyValidStateCache(cache); assume {:axiom} cache.Modifies == {}; diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy index b3a92716d..687454b1b 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy @@ -764,10 +764,10 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsService tmp5.search.Some? ==> var tmps6 := set t6 | t6 in tmp5.search.value.versions; forall tmp6 :: tmp6 in tmps6 ==> - tmp6.keySource.multi? ==> - tmp6.keySource.multi.cache.Some? ==> - tmp6.keySource.multi.cache.value.Shared? ==> - tmp6.keySource.multi.cache.value.Shared.ValidState() + tmp6.keySource.single? ==> + tmp6.keySource.single.cache.Some? ==> + tmp6.keySource.single.cache.value.Shared? ==> + tmp6.keySource.single.cache.value.Shared.ValidState() modifies set tmps7 <- set t7 <- config.tableEncryptionConfigs.Values | true && t7.keyring.Some? :: t7.keyring.value, @@ -788,10 +788,10 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsService modifies set tmps12 <- set t12 <- config.tableEncryptionConfigs.Values | true && t12.search.Some? , t13 <- t12.search.value.versions | true - && t13.keySource.multi? - && t13.keySource.multi.cache.Some? - && t13.keySource.multi.cache.value.Shared? - :: t13.keySource.multi.cache.value.Shared, + && t13.keySource.single? + && t13.keySource.single.cache.Some? + && t13.keySource.single.cache.value.Shared? + :: t13.keySource.single.cache.value.Shared, obj <- tmps12.Modifies | obj in tmps12.Modifies :: obj ensures res.Success? ==> && fresh(res.value) @@ -816,10 +816,10 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsService ) - ( set tmps19 <- set t19 <- config.tableEncryptionConfigs.Values | true && t19.search.Some? , t20 <- t19.search.value.versions | true - && t20.keySource.multi? - && t20.keySource.multi.cache.Some? - && t20.keySource.multi.cache.value.Shared? - :: t20.keySource.multi.cache.value.Shared, + && t20.keySource.single? + && t20.keySource.single.cache.Some? + && t20.keySource.single.cache.value.Shared? + :: t20.keySource.single.cache.value.Shared, obj <- tmps19.Modifies | obj in tmps19.Modifies :: obj ) ) && fresh(res.value.History) @@ -847,10 +847,10 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsService tmp26.search.Some? ==> var tmps27 := set t27 | t27 in tmp26.search.value.versions; forall tmp27 :: tmp27 in tmps27 ==> - tmp27.keySource.multi? ==> - tmp27.keySource.multi.cache.Some? ==> - tmp27.keySource.multi.cache.value.Shared? ==> - tmp27.keySource.multi.cache.value.Shared.ValidState() + tmp27.keySource.single? ==> + tmp27.keySource.single.cache.Some? ==> + tmp27.keySource.single.cache.value.Shared? ==> + tmp27.keySource.single.cache.value.Shared.ValidState() // Helper functions for the benefit of native code to create a Success(client) without referring to Dafny internals function method CreateSuccessOfClient(client: IDynamoDbEncryptionTransformsClient): Result { From b7a6159db8369eb3a5db4fd7a8fe8f733fb5efc5 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 28 Nov 2024 12:20:37 -0800 Subject: [PATCH 03/29] add partition ID and cache --- ...yptographyDbEncryptionSdkDynamoDbTypes.dfy | 2 +- .../Model/DynamoDbEncryption.smithy | 9 +-- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 30 +++++--- .../DynamoDbEncryption/src/SearchInfo.dfy | 34 +++++++-- .../dbencryptionsdk/dynamodb/ToDafny.java | 38 +++++++++- .../dbencryptionsdk/dynamodb/ToNative.java | 21 ++++++ .../dynamodb/model/MultiKeyStore.java | 35 +++++++++ .../dynamodb/model/SingleKeyStore.java | 71 +++++++++++++++++++ .../DynamoDbEncryption/MultiKeyStore.cs | 10 +++ .../DynamoDbEncryption/SingleKeyStore.cs | 20 ++++++ .../DynamoDbEncryption/TypeConversion.cs | 39 ++++++++-- .../TypeConversion.cs | 39 ++++++++-- .../Generated/DDBEncryption/TypeConversion.cs | 39 ++++++++-- .../searchable-encryption/search-config.md | 8 +++ 14 files changed, 358 insertions(+), 37 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy index 118a5af53..6106e35dc 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy @@ -389,7 +389,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald } datatype SingleKeyStore = | SingleKeyStore ( nameonly keyId: string , - nameonly cacheTTL: Option := Option.None , + nameonly cacheTTL: int32 , nameonly cache: Option := Option.None , nameonly partitionId: Option := Option.None ) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy index 5b4c2ac6d..0706061ea 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy @@ -713,11 +713,12 @@ structure SingleKeyStore { @required @javadoc("The Beacon Key ID.") keyId : String, - @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS. Provide only one of cacheTTL or cache.") + @required + @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS.") cacheTTL: Integer, - @documentation("Provide the Shared Cache for Searchable Encryption. Provide only one of cacheTTL or cache.") + @documentation("Provide the Shared Cache for Searchable Encryption.") cache : CacheType, - @documentation("Partition ID to share DynamoDB Interceptors. TODO: Update description") + @documentation("Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache.") partitionId: String } @@ -738,7 +739,7 @@ structure MultiKeyStore { cacheTTL: Integer, @javadoc("Which type of local cache to use.") cache : CacheType, - @documentation("Partition ID to share DynamoDB Interceptors. TODO: Update description") + @documentation("Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache.") partitionId: String } diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 629b3aeb0..ebf7b388b 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -136,9 +136,11 @@ module SearchConfigToInfo { else MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1000)) else - MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); + if config.single.cache.Some? then + config.single.cache.value + else + MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); - // TODO : Add check that customers only provide either cacheTTL or cache in case of SingleKeyStore var cache; if cacheType.Shared? { cache := cacheType.Shared; @@ -155,15 +157,21 @@ module SearchConfigToInfo { var partitionIdBytes : seq; - if outer.keyring.Some? { - if outer.keyring.value.partitionId.Some? { - partitionIdBytes :- UTF8.Encode(outer.keyring.value.partitionId.value) - .MapFailure( - e => Error.DynamoDbEncryptionException( - message := "Could not UTF-8 Encode Partition ID: " + e - ) - ); - } + if config.multi? && config.multi.partitionId.Some? { + partitionIdBytes :- UTF8.Encode(config.multi.partitionId.value) + .MapFailure( + e => Error.DynamoDbEncryptionException( + message := "Could not UTF-8 Encode Partition ID from MultiKeyStore: " + e + ) + ); + } + if config.single? && config.single.partitionId.Some? { + partitionIdBytes :- UTF8.Encode(config.single.partitionId.value) + .MapFailure( + e => Error.DynamoDbEncryptionException( + message := "Could not UTF-8 Encode Partition ID from SingleKeyStore: " + e + ) + ); } else { var uuid? := UUID.GenerateUUID(); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index ff514598f..18f1f89ec 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -155,7 +155,7 @@ module SearchableEncryptionInfo { { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); - var theMap :- getKeysCache(stdNames, keyLoc.keyId, partitionIdBytes); + var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -165,7 +165,7 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must not be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var theMap :- getKeysCache(stdNames, id, partitionIdBytes); return Success(Keys(theMap)); + case KeyId(id) => var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes); return Success(Keys(theMap)); } } } @@ -182,9 +182,22 @@ module SearchableEncryptionInfo { return Success(keyLoc.keys); } + // Checks if (time_now - cache creation time of the extracted cache entry) is less than the allowed + // TTL of the current Beacon Key Source calling the getEntry method from the cache. + // Mitigates risk if another Beacon Key Source wrote the entry with a longer TTL. + predicate method cacheEntryWithinLimits( + creationTime: MP.PositiveLong, + now: MP.PositiveLong, + ttlSeconds: MP.PositiveLong + ): (output: bool) + { + now - creationTime <= ttlSeconds as MP.PositiveLong + } + method getKeysCache( stdNames : seq, keyId : string, + cacheTTL : MP.PositiveLong, partitionIdBytes : seq ) returns (output : Result) @@ -269,8 +282,21 @@ module SearchableEncryptionInfo { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=getCacheOutput.error)); } - // TODO: Add cacheEntryWithinLimits - if getCacheOutput.Failure? { + var now := Time.GetCurrent(); + + // //= specification/searchable-encryption/search-config.md# + //# If using a `Shared` cache across multiple Beacon Key Sources, + //# different Key Sources having the same `beaconKey` can have different TTLs. + //# In such a case, the expiry time in the cache is set according to the Beacon Key Source that populated the cache. + //# There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, + //# `time.now() - cacheEntryCreationTime <= ttlSeconds` is true and + //# valid for TTL of the Beacon Key Source getting the cache entry. + //# If this is NOT true, then we MUST treat the cache entry as expired. + if getCacheOutput.Failure? || !cacheEntryWithinLimits( + creationTime := getCacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) { //= specification/searchable-encryption/search-config.md#beacon-keys //# Beacon keys MUST be obtained from the configured [Beacon Key Source](#beacon-key-source). var maybeRawBeaconKeyMaterials := store.GetBeaconKey( diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToDafny.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToDafny.java index a20f1f9d3..861a7eba0 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToDafny.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToDafny.java @@ -668,7 +668,19 @@ public static MultiKeyStore MultiKeyStore( ) ) : Option.create_None(CacheType._typeDescriptor()); - return new MultiKeyStore(keyFieldName, cacheTTL, cache); + Option> partitionId; + partitionId = + Objects.nonNull(nativeValue.partitionId()) + ? Option.create_Some( + DafnySequence._typeDescriptor(TypeDescriptor.CHAR), + software.amazon.smithy.dafny.conversion.ToDafny.Simple.CharacterSequence( + nativeValue.partitionId() + ) + ) + : Option.create_None( + DafnySequence._typeDescriptor(TypeDescriptor.CHAR) + ); + return new MultiKeyStore(keyFieldName, cacheTTL, cache, partitionId); } public static PartOnly PartOnly( @@ -747,7 +759,29 @@ public static SingleKeyStore SingleKeyStore( ); Integer cacheTTL; cacheTTL = (nativeValue.cacheTTL()); - return new SingleKeyStore(keyId, cacheTTL); + Option cache; + cache = + Objects.nonNull(nativeValue.cache()) + ? Option.create_Some( + CacheType._typeDescriptor(), + software.amazon.cryptography.materialproviders.ToDafny.CacheType( + nativeValue.cache() + ) + ) + : Option.create_None(CacheType._typeDescriptor()); + Option> partitionId; + partitionId = + Objects.nonNull(nativeValue.partitionId()) + ? Option.create_Some( + DafnySequence._typeDescriptor(TypeDescriptor.CHAR), + software.amazon.smithy.dafny.conversion.ToDafny.Simple.CharacterSequence( + nativeValue.partitionId() + ) + ) + : Option.create_None( + DafnySequence._typeDescriptor(TypeDescriptor.CHAR) + ); + return new SingleKeyStore(keyId, cacheTTL, cache, partitionId); } public static StandardBeacon StandardBeacon( diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToNative.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToNative.java index bd4b65bc4..541ef0da2 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToNative.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/ToNative.java @@ -574,6 +574,13 @@ public static MultiKeyStore MultiKeyStore( ) ); } + if (dafnyValue.dtor_partitionId().is_Some()) { + nativeBuilder.partitionId( + software.amazon.smithy.dafny.conversion.ToNative.Simple.String( + dafnyValue.dtor_partitionId().dtor_value() + ) + ); + } return nativeBuilder.build(); } @@ -653,6 +660,20 @@ public static SingleKeyStore SingleKeyStore( ) ); nativeBuilder.cacheTTL((dafnyValue.dtor_cacheTTL())); + if (dafnyValue.dtor_cache().is_Some()) { + nativeBuilder.cache( + software.amazon.cryptography.materialproviders.ToNative.CacheType( + dafnyValue.dtor_cache().dtor_value() + ) + ); + } + if (dafnyValue.dtor_partitionId().is_Some()) { + nativeBuilder.partitionId( + software.amazon.smithy.dafny.conversion.ToNative.Simple.String( + dafnyValue.dtor_partitionId().dtor_value() + ) + ); + } return nativeBuilder.build(); } diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java index 0f1420eaa..0c8a47ea8 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java @@ -26,10 +26,16 @@ public class MultiKeyStore { */ private final CacheType cache; + /** + * Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + private final String partitionId; + protected MultiKeyStore(BuilderImpl builder) { this.keyFieldName = builder.keyFieldName(); this.cacheTTL = builder.cacheTTL(); this.cache = builder.cache(); + this.partitionId = builder.partitionId(); } /** @@ -53,6 +59,13 @@ public CacheType cache() { return this.cache; } + /** + * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + public String partitionId() { + return this.partitionId; + } + public Builder toBuilder() { return new BuilderImpl(this); } @@ -92,6 +105,16 @@ public interface Builder { */ CacheType cache(); + /** + * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + Builder partitionId(String partitionId); + + /** + * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + String partitionId(); + MultiKeyStore build(); } @@ -103,12 +126,15 @@ static class BuilderImpl implements Builder { protected CacheType cache; + protected String partitionId; + protected BuilderImpl() {} protected BuilderImpl(MultiKeyStore model) { this.keyFieldName = model.keyFieldName(); this.cacheTTL = model.cacheTTL(); this.cache = model.cache(); + this.partitionId = model.partitionId(); } public Builder keyFieldName(String keyFieldName) { @@ -138,6 +164,15 @@ public CacheType cache() { return this.cache; } + public Builder partitionId(String partitionId) { + this.partitionId = partitionId; + return this; + } + + public String partitionId() { + return this.partitionId; + } + public MultiKeyStore build() { if (Objects.isNull(this.keyFieldName())) { throw new IllegalArgumentException( diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java index 9ece5763f..e38c22ae3 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java @@ -4,6 +4,7 @@ package software.amazon.cryptography.dbencryptionsdk.dynamodb.model; import java.util.Objects; +import software.amazon.cryptography.materialproviders.model.CacheType; /** * The configuration for using a single Beacon Key. @@ -20,9 +21,21 @@ public class SingleKeyStore { */ private final Integer cacheTTL; + /** + * Provide the Shared Cache for Searchable Encryption. + */ + private final CacheType cache; + + /** + * Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + private final String partitionId; + protected SingleKeyStore(BuilderImpl builder) { this.keyId = builder.keyId(); this.cacheTTL = builder.cacheTTL(); + this.cache = builder.cache(); + this.partitionId = builder.partitionId(); } /** @@ -39,6 +52,20 @@ public Integer cacheTTL() { return this.cacheTTL; } + /** + * @return Provide the Shared Cache for Searchable Encryption. + */ + public CacheType cache() { + return this.cache; + } + + /** + * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + public String partitionId() { + return this.partitionId; + } + public Builder toBuilder() { return new BuilderImpl(this); } @@ -68,6 +95,26 @@ public interface Builder { */ Integer cacheTTL(); + /** + * @param cache Provide the Shared Cache for Searchable Encryption. + */ + Builder cache(CacheType cache); + + /** + * @return Provide the Shared Cache for Searchable Encryption. + */ + CacheType cache(); + + /** + * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + Builder partitionId(String partitionId); + + /** + * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + */ + String partitionId(); + SingleKeyStore build(); } @@ -77,11 +124,17 @@ static class BuilderImpl implements Builder { protected Integer cacheTTL; + protected CacheType cache; + + protected String partitionId; + protected BuilderImpl() {} protected BuilderImpl(SingleKeyStore model) { this.keyId = model.keyId(); this.cacheTTL = model.cacheTTL(); + this.cache = model.cache(); + this.partitionId = model.partitionId(); } public Builder keyId(String keyId) { @@ -102,6 +155,24 @@ public Integer cacheTTL() { return this.cacheTTL; } + public Builder cache(CacheType cache) { + this.cache = cache; + return this; + } + + public CacheType cache() { + return this.cache; + } + + public Builder partitionId(String partitionId) { + this.partitionId = partitionId; + return this; + } + + public String partitionId() { + return this.partitionId; + } + public SingleKeyStore build() { if (Objects.isNull(this.keyId())) { throw new IllegalArgumentException( diff --git a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/MultiKeyStore.cs b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/MultiKeyStore.cs index 82fc3f519..ad6541b51 100644 --- a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/MultiKeyStore.cs +++ b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/MultiKeyStore.cs @@ -10,6 +10,7 @@ public class MultiKeyStore private string _keyFieldName; private int? _cacheTTL; private AWS.Cryptography.MaterialProviders.CacheType _cache; + private string _partitionId; public string KeyFieldName { get { return this._keyFieldName; } @@ -37,6 +38,15 @@ public bool IsSetCache() { return this._cache != null; } + public string PartitionId + { + get { return this._partitionId; } + set { this._partitionId = value; } + } + public bool IsSetPartitionId() + { + return this._partitionId != null; + } public void Validate() { if (!IsSetKeyFieldName()) throw new System.ArgumentException("Missing value for required property 'KeyFieldName'"); diff --git a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/SingleKeyStore.cs b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/SingleKeyStore.cs index c0e76e378..891baa636 100644 --- a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/SingleKeyStore.cs +++ b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/SingleKeyStore.cs @@ -9,6 +9,8 @@ public class SingleKeyStore { private string _keyId; private int? _cacheTTL; + private AWS.Cryptography.MaterialProviders.CacheType _cache; + private string _partitionId; public string KeyId { get { return this._keyId; } @@ -27,6 +29,24 @@ public bool IsSetCacheTTL() { return this._cacheTTL.HasValue; } + public AWS.Cryptography.MaterialProviders.CacheType Cache + { + get { return this._cache; } + set { this._cache = value; } + } + public bool IsSetCache() + { + return this._cache != null; + } + public string PartitionId + { + get { return this._partitionId; } + set { this._partitionId = value; } + } + public bool IsSetPartitionId() + { + return this._partitionId != null; + } public void Validate() { if (!IsSetKeyId()) throw new System.ArgumentException("Missing value for required property 'KeyId'"); diff --git a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/TypeConversion.cs b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/TypeConversion.cs index 0044c6b7c..fce945c13 100644 --- a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/TypeConversion.cs +++ b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryption/TypeConversion.cs @@ -522,25 +522,30 @@ public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafn public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore(); converted.KeyId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(concrete._keyId); - converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); return converted; + converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore value) { value.Validate(); - - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL)); + AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore(); converted.KeyFieldName = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(concrete._keyFieldName); converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(concrete._cacheTTL); - if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); return converted; + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore value) { value.Validate(); AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache)); + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.PartOnly FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S8_PartOnly(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IPartOnly value) { @@ -762,6 +767,22 @@ public static int ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dyna { return ToDafny_N6_smithy__N3_api__S7_Integer(value); } + public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(Wrappers_Compile._IOption value) + { + return value.is_None ? (AWS.Cryptography.MaterialProviders.CacheType)null : FromDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType(value.Extract()); + } + public static Wrappers_Compile._IOption ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(AWS.Cryptography.MaterialProviders.CacheType value) + { + return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); + } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); @@ -786,6 +807,14 @@ public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12 { return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S6_Shared__M5_other(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); diff --git a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryptionTransforms/TypeConversion.cs b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryptionTransforms/TypeConversion.cs index 69852084b..320227daa 100644 --- a/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryptionTransforms/TypeConversion.cs +++ b/DynamoDbEncryption/runtimes/net/Generated/DynamoDbEncryptionTransforms/TypeConversion.cs @@ -5486,25 +5486,30 @@ public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafn public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore(); converted.KeyId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(concrete._keyId); - converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); return converted; + converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore value) { value.Validate(); - - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL)); + AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore(); converted.KeyFieldName = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(concrete._keyFieldName); converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(concrete._cacheTTL); - if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); return converted; + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore value) { value.Validate(); AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache)); + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.StandardBeacon FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_StandardBeacon(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IStandardBeacon value) { @@ -5586,6 +5591,22 @@ public static int ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dyna { return ToDafny_N6_smithy__N3_api__S7_Integer(value); } + public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(Wrappers_Compile._IOption value) + { + return value.is_None ? (AWS.Cryptography.MaterialProviders.CacheType)null : FromDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType(value.Extract()); + } + public static Wrappers_Compile._IOption ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(AWS.Cryptography.MaterialProviders.CacheType value) + { + return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); + } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); @@ -5610,6 +5631,14 @@ public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12 { return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_StandardBeacon__M4_name(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); diff --git a/TestVectors/runtimes/net/Generated/DDBEncryption/TypeConversion.cs b/TestVectors/runtimes/net/Generated/DDBEncryption/TypeConversion.cs index 009ce943e..ffb8ab363 100644 --- a/TestVectors/runtimes/net/Generated/DDBEncryption/TypeConversion.cs +++ b/TestVectors/runtimes/net/Generated/DDBEncryption/TypeConversion.cs @@ -489,25 +489,30 @@ public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafn public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore(); converted.KeyId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(concrete._keyId); - converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); return converted; + converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(concrete._cacheTTL); + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._ISingleKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.SingleKeyStore value) { value.Validate(); - - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL)); + AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.SingleKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_keyId(value.KeyId), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore value) { software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore concrete = (software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore)value; AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore converted = new AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore(); converted.KeyFieldName = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(concrete._keyFieldName); converted.CacheTTL = (int)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(concrete._cacheTTL); - if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); return converted; + if (concrete._cache.is_Some) converted.Cache = (AWS.Cryptography.MaterialProviders.CacheType)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(concrete._cache); + if (concrete._partitionId.is_Some) converted.PartitionId = (string)FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(concrete._partitionId); return converted; } public static software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IMultiKeyStore ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore(AWS.Cryptography.DbEncryptionSDK.DynamoDb.MultiKeyStore value) { value.Validate(); AWS.Cryptography.MaterialProviders.CacheType var_cache = value.IsSetCache() ? value.Cache : (AWS.Cryptography.MaterialProviders.CacheType)null; - return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache)); + string var_partitionId = value.IsSetPartitionId() ? value.PartitionId : (string)null; + return new software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types.MultiKeyStore(ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(value.KeyFieldName), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M8_cacheTTL(value.CacheTTL), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M5_cache(var_cache), ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(var_partitionId)); } public static AWS.Cryptography.DbEncryptionSDK.DynamoDb.PartOnly FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S8_PartOnly(software.amazon.cryptography.dbencryptionsdk.dynamodb.internaldafny.types._IPartOnly value) { @@ -722,6 +727,22 @@ public static int ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dyna { return ToDafny_N6_smithy__N3_api__S7_Integer(value); } + public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(Wrappers_Compile._IOption value) + { + return value.is_None ? (AWS.Cryptography.MaterialProviders.CacheType)null : FromDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType(value.Extract()); + } + public static Wrappers_Compile._IOption ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M5_cache(AWS.Cryptography.MaterialProviders.CacheType value) + { + return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); + } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S14_SingleKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M12_keyFieldName(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); @@ -746,6 +767,14 @@ public static AWS.Cryptography.MaterialProviders.CacheType FromDafny_N3_aws__N12 { return value == null ? Wrappers_Compile.Option.create_None() : Wrappers_Compile.Option.create_Some(ToDafny_N3_aws__N12_cryptography__N17_materialProviders__S9_CacheType((AWS.Cryptography.MaterialProviders.CacheType)value)); } + public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(Wrappers_Compile._IOption> value) + { + return value.is_None ? (string)null : FromDafny_N6_smithy__N3_api__S6_String(value.Extract()); + } + public static Wrappers_Compile._IOption> ToDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S13_MultiKeyStore__M11_partitionId(string value) + { + return value == null ? Wrappers_Compile.Option>.create_None() : Wrappers_Compile.Option>.create_Some(ToDafny_N6_smithy__N3_api__S6_String((string)value)); + } public static string FromDafny_N3_aws__N12_cryptography__N15_dbEncryptionSdk__N8_dynamoDb__S6_Shared__M5_other(Dafny.ISequence value) { return FromDafny_N6_smithy__N3_api__S6_String(value); diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index 9c70d99cb..3c7c4a474 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -164,6 +164,8 @@ This can also be described as single tenant. On initialization of a Single Key Store, the caller MUST provide: +TODO: Update + - [Beacon Key Id](#beacon-key-id) - [cacheTTL](#cachettl) @@ -175,6 +177,8 @@ This can also be described as multi tenant. On initialization of a Multi Key Store, the caller MUST provide: +TODO: Update + - [Beacon Key Field Name](#beacon-key-field-name) - [cacheTTL](#cachettl) - [max cache size](#max-cache-size) @@ -210,11 +214,15 @@ for how long a beacon key should exist locally before reauthorization. #### max cache size +TODO: Remove and add partition ID + The [max cache size](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#entry-capacity) that the [Key Store Cache](#key-store-cache) will be configured to. ### Key Store Cache +TODO: Update + For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) MUST be created. For a [Single Key Store](#single-key-store-initialization) the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) From 071cf6d8c8b38393e9dde44c15cc7d8f8ae7144a Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 29 Nov 2024 21:11:30 -0800 Subject: [PATCH 04/29] fix tests --- .../dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy | 8 ++++++-- TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index bbf448e00..1405c04d3 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -242,7 +242,9 @@ module BeaconTestFixtures { cache := MPT.Default(Default := MPT.DefaultCache(entryCapacity := 3)) ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); - return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0); + // Create a test partitionIdBytes + var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0, partitionIdBytes); } method GetMultiSource(keyName : string, version : BeaconVersion) returns (output : SI.KeySource) @@ -257,7 +259,9 @@ module BeaconTestFixtures { cache := MPT.Default(Default := MPT.DefaultCache(entryCapacity := 3)) ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); - return SI.KeySource(client, version.keyStore, SI.MultiLoc(keyName, false), cache, 0); + // Create a test partitionIdBytes + var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + return SI.KeySource(client, version.keyStore, SI.MultiLoc(keyName, false), cache, 0, partitionIdBytes); } const SimpleItem : DDB.AttributeMap := map[ diff --git a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy index 650e9fd21..6e0a5a211 100644 --- a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy +++ b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy @@ -524,7 +524,10 @@ module {:options "-functionSyntax:4"} JsonConfig { var cache :- expect mpl.CreateCryptographicMaterialsCache(input); var client :- expect Primitives.AtomicPrimitives(); - var src := SI.KeySource(client, store, SI.SingleLoc("foo"), cache, 100 as uint32); + + // Create a test partitionIdBytes + var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + var src := SI.KeySource(client, store, SI.SingleLoc("foo"), cache, 100 as uint32, partitionIdBytes); var bv :- expect SI.MakeBeaconVersion(1, src, map[], map[], map[]); return Success(bv); From 421a648d78b3d1dba5bcc8482f1f99a785cd29e1 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 29 Nov 2024 21:32:11 -0800 Subject: [PATCH 05/29] format --- .../dafny/DynamoDbEncryption/src/ConfigToInfo.dfy | 8 ++++---- .../dafny/DynamoDbEncryption/src/SearchInfo.dfy | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index ebf7b388b..9f873b831 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -136,10 +136,10 @@ module SearchConfigToInfo { else MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1000)) else - if config.single.cache.Some? then - config.single.cache.value - else - MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); + if config.single.cache.Some? then + config.single.cache.value + else + MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); var cache; if cacheType.Shared? { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 18f1f89ec..9863aa522 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -293,10 +293,10 @@ module SearchableEncryptionInfo { //# valid for TTL of the Beacon Key Source getting the cache entry. //# If this is NOT true, then we MUST treat the cache entry as expired. if getCacheOutput.Failure? || !cacheEntryWithinLimits( - creationTime := getCacheOutput.value.creationTime, - now := now, - ttlSeconds := cacheTTL - ) { + creationTime := getCacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) { //= specification/searchable-encryption/search-config.md#beacon-keys //# Beacon keys MUST be obtained from the configured [Beacon Key Source](#beacon-key-source). var maybeRawBeaconKeyMaterials := store.GetBeaconKey( From cda1fdf931a251b58386c7b55190d1b66ead9e5d Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 4 Dec 2024 15:58:22 -0800 Subject: [PATCH 06/29] fix --- DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 9863aa522..1bf19521a 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -272,7 +272,7 @@ module SearchableEncryptionInfo { // Append Resource Id, Scope Id, Partition Id, and Suffix to create the cache identifier var identifier := resourceId + NULL_BYTE + scopeId + NULL_BYTE + partitionIdBytes + NULL_BYTE + suffix; - var getCacheInput := MP.GetCacheEntryInput(identifier := keyIdBytes, bytesUsed := None); + var getCacheInput := MP.GetCacheEntryInput(identifier := identifier, bytesUsed := None); verifyValidStateCache(cache); assume {:axiom} cache.Modifies == {}; var getCacheOutput := cache.GetCacheEntry(getCacheInput); From 9f84eb97c803f07f5036b4ce52a3ee476f10a9fb Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 13 Dec 2024 13:28:57 -0800 Subject: [PATCH 07/29] updates --- ...yptographyDbEncryptionSdkDynamoDbTypes.dfy | 2 +- .../Model/DynamoDbEncryption.smithy | 4 +- .../dafny/DynamoDbEncryption/src/Beacon.dfy | 28 +++++------ .../DynamoDbEncryption/src/CompoundBeacon.dfy | 18 +++---- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 47 ++++++++--------- .../DynamoDbEncryption/src/DynamoToStruct.dfy | 8 +-- .../DynamoDbEncryption/src/FilterExpr.dfy | 6 +-- .../DynamoDbEncryption/src/SearchInfo.dfy | 50 ++++++++++++------- .../dafny/DynamoDbEncryption/src/Virtual.dfy | 22 ++++---- .../test/BeaconTestFixtures.dfy | 6 +-- ...DbEncryptionSdkDynamoDbTransformsTypes.dfy | 2 +- .../src/DdbMiddlewareConfig.dfy | 6 +-- .../src/DynamoDbMiddlewareSupport.dfy | 10 ++-- .../src/UpdateItemTransform.dfy | 4 +- ...ncryptionSdkDynamoDbItemEncryptorTypes.dfy | 2 +- ...tionSdkDynamoDbItemEncryptorOperations.dfy | 24 ++++----- ...EncryptionSdkStructuredEncryptionTypes.dfy | 2 +- ...ptionSdkStructuredEncryptionOperations.dfy | 12 ++--- .../StructuredEncryption/src/Canonize.dfy | 2 +- .../dafny/StructuredEncryption/src/Crypt.dfy | 16 +++--- .../dafny/StructuredEncryption/src/Header.dfy | 30 +++++------ .../dynamodb/model/MultiKeyStore.java | 8 +-- .../dynamodb/model/SingleKeyStore.java | 8 +-- ...tographyDynamoDbEncryptionTypesWrapped.dfy | 4 +- .../dafny/DDBEncryption/src/JsonConfig.dfy | 2 +- 25 files changed, 166 insertions(+), 157 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy index 2712ba605..2eb0fb26f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald import AwsCryptographyKeyStoreTypes import AwsCryptographyPrimitivesTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy index d0275fd81..77f11c466 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy @@ -719,7 +719,7 @@ structure SingleKeyStore { cacheTTL: Integer, @documentation("Provide the Shared Cache for Searchable Encryption.") cache : CacheType, - @documentation("Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache.") + @documentation("Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache.") partitionId: String } @@ -740,7 +740,7 @@ structure MultiKeyStore { cacheTTL: Integer, @javadoc("Which type of local cache to use.") cache : CacheType, - @documentation("Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache.") + @documentation("Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache.") partitionId: String } diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy index 6d423cc91..d6bd1ff17 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy @@ -236,14 +236,14 @@ module BaseBeacon { //# [AttributeValue](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html) //# MUST be type "SS" StringSet. && (ret.value.Some? ==> ret.value.value.SS?) - //= specification/searchable-encryption/beacons.md#value-for-a-set-standard-beacon - //= type=implication - //# * The resulting set MUST NOT contain duplicates. + //= specification/searchable-encryption/beacons.md#value-for-a-set-standard-beacon + //= type=implication + //# * The resulting set MUST NOT contain duplicates. && (ret.value.Some? ==> HasNoDuplicates(ret.value.value.SS)) - //= specification/searchable-encryption/beacons.md#asset-initialization - //= type=implication - //# * Writing an item MUST fail if the item contains this beacon's attribute, - //# and that attribute is not of type Set. + //= specification/searchable-encryption/beacons.md#asset-initialization + //= type=implication + //# * Writing an item MUST fail if the item contains this beacon's attribute, + //# and that attribute is not of type Set. && var value := TermLoc.TermToAttr(loc, item, None); && (value.Some? && !(value.value.SS? || value.value.NS? || value.value.BS?) ==> ret.Failure?) { @@ -272,14 +272,14 @@ module BaseBeacon { //= type=implication //# * This operation MUST return no value if the associated field does not exist in the record && (bytes.None? ==> ret.value.None?) - //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon - //= type=implication - //# * This operation MUST convert the attribute value of the associated field to - //# a sequence of bytes, as per [attribute serialization](../dynamodb-encryption-client/ddb-attribute-serialization.md). + //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon + //= type=implication + //# * This operation MUST convert the attribute value of the associated field to + //# a sequence of bytes, as per [attribute serialization](../dynamodb-encryption-client/ddb-attribute-serialization.md). && (bytes.Some? ==> ret.value.Some? && hash(bytes.value, key).Success? && ret.value.value == DDB.AttributeValue.S(hash(bytes.value, key).value)) - //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon - //= type=implication - //# * This operation MUST return the [basicHash](#basichash) of the resulting bytes and the configured [beacon length](#beacon-length). + //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon + //= type=implication + //# * This operation MUST return the [basicHash](#basichash) of the resulting bytes and the configured [beacon length](#beacon-length). && (bytes.Some? ==> ret.value.Some? && base.hash(bytes.value, key, length).Success? && ret.value.value == DDB.AttributeValue.S(base.hash(bytes.value, key, length).value)) { var bytes :- VirtToBytes(loc, item, vf); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy index e8fc00e35..275f008fc 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy @@ -397,9 +397,9 @@ module CompoundBeacon { //= type=implication //# * If a string is returned, it MUST NOT be empty. && |res.value.value| > 0 - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST iterate through all constructors, in order, using the first that succeeds. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST iterate through all constructors, in order, using the first that succeeds. && TryConstructors(construct, item, vf, keys).Success? { TryConstructors(construct, item, vf, keys) @@ -578,9 +578,9 @@ module CompoundBeacon { ensures part.Signed? && ret.Success? ==> && ret.value == part.prefix + data && 0 < |ret.value| - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST fail if any plaintext value used in the construction contains the split character. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST fail if any plaintext value used in the construction contains the split character. && split !in data //= specification/searchable-encryption/beacons.md#part-value-calculation @@ -593,9 +593,9 @@ module CompoundBeacon { && keys.Keys? && part.beacon.hashStr(data, keys.value).Success? && ret.value == part.prefix + part.beacon.hashStr(data, keys.value).value - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST fail if any plaintext value used in the construction contains the split character. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST fail if any plaintext value used in the construction contains the split character. && split !in data { :- Need(split !in data, E("Value '" + data + "' for beacon part " + part.getName() + " contains the split character '" + [split] + "'.")); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 9f873b831..79d2442e2 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -33,7 +33,6 @@ module SearchConfigToInfo { import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import MPT = AwsCryptographyMaterialProvidersTypes import Primitives = AtomicPrimitives - import UUID // convert configured SearchConfig to internal SearchInfo method Convert(outer : DynamoDbTableEncryptionConfig) @@ -165,7 +164,7 @@ module SearchConfigToInfo { ) ); } - if config.single? && config.single.partitionId.Some? { + else if config.single? && config.single.partitionId.Some? { partitionIdBytes :- UTF8.Encode(config.single.partitionId.value) .MapFailure( e => Error.DynamoDbEncryptionException( @@ -174,13 +173,7 @@ module SearchConfigToInfo { ); } else { - var uuid? := UUID.GenerateUUID(); - - var uuid :- uuid? - .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); - - partitionIdBytes :- UUID.ToByteArray(uuid) - .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + partitionIdBytes :- I.GeneratePartitionId(); } if config.multi? { @@ -511,10 +504,10 @@ module SearchConfigToInfo { //= type=implication //# Initialization MUST fail if two standard beacons are configured with the same location. && FindBeaconWithThisLocation(converted, loc).None? - //= specification/searchable-encryption/virtual.md#virtual-field-initialization - //= type=implication - //# Initialization MUST fail if a virtual field is defined with only one location, - //# and also a [standard beacon](beacons.md#standard-beacon) is defined with that same location. + //= specification/searchable-encryption/virtual.md#virtual-field-initialization + //= type=implication + //# Initialization MUST fail if a virtual field is defined with only one location, + //# and also a [standard beacon](beacons.md#standard-beacon) is defined with that same location. && FindVirtualFieldWithThisLocation(virtualFields, {loc}).None? { if |beacons| == 0 { @@ -643,10 +636,10 @@ module SearchConfigToInfo { //# or is not `signed`. ensures (&& 0 < |parts| - //= specification/searchable-encryption/beacons.md#signed-part-initialization - //= type=implication - //# If no [terminal location](virtual.md#terminal-location) is provided, - //# the `name` MUST be used as the [terminal location](virtual.md#terminal-location). + //= specification/searchable-encryption/beacons.md#signed-part-initialization + //= type=implication + //# If no [terminal location](virtual.md#terminal-location) is provided, + //# the `name` MUST be used as the [terminal location](virtual.md#terminal-location). && GetLoc(parts[0].name, parts[0].loc).Success? && var loc := GetLoc(parts[0].name, parts[0].loc).value; && !IsSignOnly(outer, CB.Signed(parts[0].prefix, parts[0].name, loc).loc)) @@ -728,10 +721,10 @@ module SearchConfigToInfo { ensures ret.Success? ==> && |ret.value| == 1 && |ret.value[0].parts| == |parts| + |converted| - //= specification/searchable-encryption/beacons.md#default-construction - //= type=implication - //# * This default constructor MUST be all of the signed parts, - //# followed by all the encrypted parts, all parts being required. + //= specification/searchable-encryption/beacons.md#default-construction + //= type=implication + //# * This default constructor MUST be all of the signed parts, + //# followed by all the encrypted parts, all parts being required. && CB.OrderedParts(allParts, numNon) && (forall i | 0 <= i < |ret.value[0].parts| :: && ret.value[0].parts[i].part == allParts[i] @@ -1116,14 +1109,14 @@ module SearchConfigToInfo { ensures ret.Success? && 0 < |names| && data[names[0]].Standard? && data[names[0]].std.share.Some? ==> && var share := data[names[0]].std.share.value; && IsValidShare(data, names[0], data[names[0]].std.length, share).Success? - //= specification/searchable-encryption/beacons.md#shared-initialization - //= type=implication - //# This name MUST be the name of a previously defined Standard Beacon. + //= specification/searchable-encryption/beacons.md#shared-initialization + //= type=implication + //# This name MUST be the name of a previously defined Standard Beacon. && share in data && data[share].Standard? - //= specification/searchable-encryption/beacons.md#shared-initialization - //= type=implication - //# This beacon's [length](#beacon-length) MUST be equal to the `other` beacon's [length](#beacon-length). + //= specification/searchable-encryption/beacons.md#shared-initialization + //= type=implication + //# This beacon's [length](#beacon-length) MUST be equal to the `other` beacon's [length](#beacon-length). && data[share].std.length == data[names[0]].std.length { if |names| == 0 then diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy index 616dfe404..90dd701a9 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy @@ -584,10 +584,10 @@ module DynamoToStruct { ensures ret.Success? ==> && U32ToBigEndian(|b|).Success? && |ret.value| == LENGTH_LEN + |b| - //= specification/dynamodb-encryption-client/ddb-attribute-serialization.md#set-entry-length - //= type=implication - //# Set Entry Length MUST be a big-endian unsigned integer - //# equal to the length of [Set Entry Value](#set-entry-value). + //= specification/dynamodb-encryption-client/ddb-attribute-serialization.md#set-entry-length + //= type=implication + //# Set Entry Length MUST be a big-endian unsigned integer + //# equal to the length of [Set Entry Value](#set-entry-value). && ret.value[0..LENGTH_LEN] == U32ToBigEndian(|b|).value && ret.value[LENGTH_LEN..] == b { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy index 2f2a2cff9..33b937ea2 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy @@ -1289,9 +1289,9 @@ module DynamoDBFilterExpr { a[0] < b[0] else bIsHighSurrogate - // we know aIsHighSurrogate != bIsHighSurrogate and a[0] != b[0] - // so if bIsHighSurrogate then a is less - // and if aIsHighSurrogate then a is greater + // we know aIsHighSurrogate != bIsHighSurrogate and a[0] != b[0] + // so if bIsHighSurrogate then a is less + // and if aIsHighSurrogate then a is greater } predicate method UnicodeLessOrEqual(a : string, b : string) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 1bf19521a..9cc900063 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -27,6 +27,7 @@ module SearchableEncryptionInfo { import KeyStoreTypes = AwsCryptographyKeyStoreTypes import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import opened CacheConstants + import UUID //= specification/searchable-encryption/search-config.md#version-number //= type=implication @@ -72,11 +73,11 @@ module SearchableEncryptionInfo { //# MUST be generated in accordance with [HMAC Key Generation](#hmac-key-generation). var newKey :- GetBeaconKey(client, key, keysLeft[0]); reveal Seq.HasNoDuplicates(); - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# [Beacon Key Materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#beacon-key-materials) MUST be generated - //# with the [beacon key id](#beacon-key-id) equal to the `beacon key id` - //# and the [HMAC Keys](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#hmac-keys) equal to a map - //# of every [standard beacons](beacons.md#standard-beacon-initialization) name to its generated HMAC key. + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //# [Beacon Key Materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#beacon-key-materials) MUST be generated + //# with the [beacon key id](#beacon-key-id) equal to the `beacon key id` + //# and the [HMAC Keys](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#hmac-keys) equal to a map + //# of every [standard beacons](beacons.md#standard-beacon-initialization) name to its generated HMAC key. output := GetHmacKeys(client, allKeys, keysLeft[1..], key, acc[keysLeft[0] := newKey]); } } @@ -106,14 +107,14 @@ module SearchableEncryptionInfo { && hkdfInput.digestAlgorithm == Prim.SHA_512 && hkdfInput.salt == None && hkdfInput.ikm == key - //= specification/searchable-encryption/search-config.md#hmac-key-generation - //= type=implication - //# The `info` MUST be the concatenation of "AWS_DBE_SCAN_BEACON" encoded as UTF8 - //# and the beacon name. + //= specification/searchable-encryption/search-config.md#hmac-key-generation + //= type=implication + //# The `info` MUST be the concatenation of "AWS_DBE_SCAN_BEACON" encoded as UTF8 + //# and the beacon name. && hkdfInput.info == info - //= specification/searchable-encryption/search-config.md#hmac-key-generation - //= type=implication - //# The `expectedLength` MUST be 64 bytes. + //= specification/searchable-encryption/search-config.md#hmac-key-generation + //= type=implication + //# The `expectedLength` MUST be 64 bytes. && hkdfInput.expectedLength == 64 { var info :- UTF8.Encode("AWS_DBE_SCAN_BEACON" + name).MapFailure(e => E(e)); @@ -128,6 +129,20 @@ module SearchableEncryptionInfo { return Success(newKey); } + // Generates a new partitionId, which is a random UUID + method GeneratePartitionId() returns (output : Result, Error>) + { + var uuid? := UUID.GenerateUUID(); + + var uuid :- uuid? + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + + var partitionIdBytes: seq :- UUID.ToByteArray(uuid) + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + + output := Success(partitionIdBytes); + } + datatype KeyLocation = | LiteralLoc (keys: HmacKeyMap) | SingleLoc (keyId: string) @@ -219,6 +234,7 @@ module SearchableEncryptionInfo { && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? + // TODO - why is this verifying? && cacheInput.identifier == UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials @@ -234,9 +250,9 @@ module SearchableEncryptionInfo { && var oldGetHistory := old(store.History.GetBeaconKey); && var newGetHistory := store.History.GetBeaconKey; && |newGetHistory| == |oldGetHistory|+1 - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //= type=implication - //# If `GetBeaconKey` fails get beacon key MUST fail. + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //= type=implication + //# If `GetBeaconKey` fails get beacon key MUST fail. && Seq.Last(newGetHistory).output.Success? && var storeInput := Seq.Last(newGetHistory).input; && var storeOutput := Seq.Last(newGetHistory).output; @@ -258,7 +274,7 @@ module SearchableEncryptionInfo { ) { - // Resource ID: Searchable Encryption [0x02] + // Resource ID: Hierarchical Keyring [0x02] var resourceId : seq := RESOURCE_ID_HIERARCHICAL_KEYRING; // Scope ID: Searchable Encryption [0x03] @@ -318,7 +334,7 @@ module SearchableEncryptionInfo { //# equal to now + configured [cacheTTL](#cachettl). var now := Time.GetCurrent(); var putCacheEntryInput:= MP.PutCacheEntryInput( - identifier := keyIdBytes, + identifier := identifier, materials := MP.Materials.BeaconKey(beaconKeyMaterials), creationTime := now, expiryTime := now+cacheTTL as MP.PositiveLong, diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy index c3cdbf5f6..163726a44 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy @@ -171,10 +171,10 @@ module DdbVirtualFields { //# If the position provided is positive, it MUST be the zero-based index from the start of the list. && (0 <= pos < limit ==> ret == pos) && (limit <= pos ==> ret == limit-1) - //= specification/searchable-encryption/virtual.md#position-definition - //= type=implication - //# If the position provided is negative, it's absolute value MUST be the - //# one-based index from the end of the list, that is, -1 refers to the last item in the list. + //= specification/searchable-encryption/virtual.md#position-definition + //= type=implication + //# If the position provided is negative, it's absolute value MUST be the + //# one-based index from the end of the list, that is, -1 refers to the last item in the list. && (0 < -pos < limit ==> ret == limit + pos) && (limit <= -pos ==> ret == 0) { @@ -259,10 +259,10 @@ module DdbVirtualFields { //= type=implication //# If index is greater than the number of items in the list, an empty string MUST be returned. && (index >= |parts| || -index > |parts| ==> ret == "") - //= specification/searchable-encryption/virtual.md#getsegment-transform-initialization - //= type=implication - //# The GetSegment transform MUST split the input string on the given character, - //# and return the item in the resulting list the corresponds to the given position. + //= specification/searchable-encryption/virtual.md#getsegment-transform-initialization + //= type=implication + //# The GetSegment transform MUST split the input string on the given character, + //# and return the item in the resulting list the corresponds to the given position. && (index < |parts| && -index <= |parts| ==> ret == parts[GetPos(index, |parts|)]) { var parts := Split(s, split); @@ -285,9 +285,9 @@ module DdbVirtualFields { //# GetSegments MUST return the range of parts from low (inclusive) to high (exclusive), //# joined on the `split` character. && (lo < hi ==> ret == Join(parts[lo..hi], [split])) - //= specification/searchable-encryption/virtual.md#getsegments-transform-initialization - //= type=implication - //# If high is less than or equal to low, an empty string MUST be returned. + //= specification/searchable-encryption/virtual.md#getsegments-transform-initialization + //= type=implication + //# If high is less than or equal to low, an empty string MUST be returned. && (lo >= hi ==> ret == "") { var parts := Split(s, split); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index 1405c04d3..30a04e3e1 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -243,11 +243,11 @@ module BeaconTestFixtures { ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); // Create a test partitionIdBytes - var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0, partitionIdBytes); } - method GetMultiSource(keyName : string, version : BeaconVersion) returns (output : SI.KeySource) + method GetMultiSource(keyName : string, version : BeaconVersion, partitionId: Option>, shared_cache: Option) returns (output : SI.KeySource) requires version.keyStore.ValidState() ensures output.ValidState() ensures version.keyStore == output.store @@ -260,7 +260,7 @@ module BeaconTestFixtures { ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); // Create a test partitionIdBytes - var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); return SI.KeySource(client, version.keyStore, SI.MultiLoc(keyName, false), cache, 0, partitionIdBytes); } diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy index 6a63e318f..5139337ea 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.transform import AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import AwsCryptographyMaterialProvidersTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy index e2c4036f7..90ec75d68 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy @@ -72,9 +72,9 @@ module DdbMiddlewareConfig { { && (forall tableName <- config.tableEncryptionConfigs :: config.tableEncryptionConfigs[tableName].physicalTableName == tableName) - //= specification/dynamodb-encryption-client/ddb-table-encryption-config.md#logical-table-name - //# When mapping [DynamoDB Table Names](#dynamodb-table-name) to [logical table name](#logical-table-name) - //# there MUST a one to one mapping between the two. + //= specification/dynamodb-encryption-client/ddb-table-encryption-config.md#logical-table-name + //# When mapping [DynamoDB Table Names](#dynamodb-table-name) to [logical table name](#logical-table-name) + //# there MUST a one to one mapping between the two. && (forall c1 <- config.tableEncryptionConfigs.Values, c2 <- config.tableEncryptionConfigs.Values diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy index 8d2ce7e19..00e9427a3 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy @@ -128,11 +128,11 @@ module DynamoDbMiddlewareSupport { (ret : Result, Error>) ensures ret.Success? && config.search.Some? && config.search.value.curr().keySource.keyLoc.MultiLoc? ==> && output.parsedHeader.Some? - //= specification/searchable-encryption/search-config.md#get-beacon-key-id-from-parsed-header - //= type=implication - //# If the [Parsed Header](../dynamodb-encryption-client/encrypt-item.md#parsed-header)'s encrypted data keys - //# do not contain only one encrypted data key - //# this function MUST fail. + //= specification/searchable-encryption/search-config.md#get-beacon-key-id-from-parsed-header + //= type=implication + //# If the [Parsed Header](../dynamodb-encryption-client/encrypt-item.md#parsed-header)'s encrypted data keys + //# do not contain only one encrypted data key + //# this function MUST fail. && var keys := output.parsedHeader.value.encryptedDataKeys; && |keys| == 1 diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy index 852c819d6..478190680 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy @@ -188,8 +188,8 @@ module UpdateItemTransform { || input.originalInput.ReturnValues.value.ALL_OLD?) ) { - // This error should not be possible to reach if we assume the DDB API contract is correct. - // We include this runtime check for defensive purposes. + // This error should not be possible to reach if we assume the DDB API contract is correct. + // We include this runtime check for defensive purposes. :- Need(forall k <- attributes.Keys :: !IsSigned(tableConfig, k), E("UpdateItems response contains signed attributes, but does not include the entire item which is required for verification.")); diff --git a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy index 2a2c00d7f..e712158c7 100644 --- a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.itemencry import AwsCryptographyMaterialProvidersTypes import AwsCryptographyPrimitivesTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy index c1b9681fa..411b1496b 100644 --- a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy @@ -54,11 +54,11 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs || (unauthenticatedAttributes.Some? && attr in unauthenticatedAttributes.value) || (unauthenticatedPrefix.Some? && unauthenticatedPrefix.value <= attr) || ReservedPrefix <= attr - // Attributes with the reserved prefix are "allowed unauthenticated" in that - // they are not specified as signed within attributeActionsOnEncrypt. - // These attributes MAY still be authenticated via other methods, - // such as "aws_dbe_head" which is explicitly added to the canonical hash - // used in signing. + // Attributes with the reserved prefix are "allowed unauthenticated" in that + // they are not specified as signed within attributeActionsOnEncrypt. + // These attributes MAY still be authenticated via other methods, + // such as "aws_dbe_head" which is explicitly added to the canonical hash + // used in signing. } //= specification/dynamodb-encryption-client/decrypt-item.md#signature-scope @@ -126,9 +126,9 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs { && InSignatureScope(config, attr) && attr !in config.attributeActionsOnEncrypt - // Attributes in signature scope MUST be configured in attributeActionsOnEncrypt - // so these two lines are saying "in scope && not in scope" - // and that's why it's an error + // Attributes in signature scope MUST be configured in attributeActionsOnEncrypt + // so these two lines are saying "in scope && not in scope" + // and that's why it's an error } // Is the attribute name in signature scope? @@ -176,9 +176,9 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs //= type=implication //# If the Version Number is 2, then the base context MUST be the [version 2](./encrypt-item.md#dynamodb-item-base-context-version-2) context. && (header[0] == 2 ==> ret == MakeEncryptionContextV2(config, item)) - //= specification/dynamodb-encryption-client/decrypt-item.md#dynamodb-item-base-context - //= type=implication - //# If the Version Number is 1, the base context MUST be the [version 1](./encrypt-item.md#dynamodb-item-base-context-version-1) context. + //= specification/dynamodb-encryption-client/decrypt-item.md#dynamodb-item-base-context + //= type=implication + //# If the Version Number is 1, the base context MUST be the [version 1](./encrypt-item.md#dynamodb-item-base-context-version-1) context. && (header[0] == 1 ==> ret == MakeEncryptionContextV1(config, item)) && ((header[0] == 1) || (header[0] == 2)) @@ -441,7 +441,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs // The partition key MUST be CSE.SIGN_ONLY && config.partitionKeyName in config.attributeActionsOnEncrypt && config.attributeActionsOnEncrypt[config.partitionKeyName] == KeyActionFromVersion(config.version) - // The sort key MUST be CSE.SIGN_ONLY + // The sort key MUST be CSE.SIGN_ONLY && (config.sortKeyName.Some? ==> && config.sortKeyName.value in config.attributeActionsOnEncrypt && config.attributeActionsOnEncrypt[config.sortKeyName.value] == KeyActionFromVersion(config.version)) diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy index 7c14e9f87..0ad88e609 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy @@ -11,7 +11,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.structuredencrypti import opened UTF8 import AwsCryptographyMaterialProvidersTypes import AwsCryptographyPrimitivesTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy index e2aef0b72..ff18410eb 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy @@ -715,9 +715,9 @@ module AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations refines Abst assume {:axiom} input.cmm.Modifies !! {config.materialProviders.History}; var newEncryptionContext :- GetV2EncryptionContext(input.plaintextStructure); if |newEncryptionContext| != 0 { - //= specification/structured-encryption/encrypt-path-structure.md#create-new-encryption-context-and-cmm - //# An error MUST be returned if any of the entries added to the encryption context in this step - //# have the same key as any entry already in the encryption context. + //= specification/structured-encryption/encrypt-path-structure.md#create-new-encryption-context-and-cmm + //# An error MUST be returned if any of the entries added to the encryption context in this step + //# have the same key as any entry already in the encryption context. :- Need(encryptionContext.Keys !! newEncryptionContext.Keys, E("Internal Error - Structured Encryption encryption context overlaps with Item Encryptor encryption context.")); encryptionContext := encryptionContext + newEncryptionContext; @@ -1070,9 +1070,9 @@ module AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations refines Abst //# in the input record, plus the Legend. var newEncryptionContext :- GetV2EncryptionContext(UnCanon(canonData)); if |newEncryptionContext| != 0 { - //= specification/structured-encryption/decrypt-path-structure.md#create-new-encryption-context-and-cmm - //# An error MUST be returned if any of the entries added to the encryption context in this step - //# have the same key as any entry already in the encryption context. + //= specification/structured-encryption/decrypt-path-structure.md#create-new-encryption-context-and-cmm + //# An error MUST be returned if any of the entries added to the encryption context in this step + //# have the same key as any entry already in the encryption context. :- Need(encryptionContext.Keys !! newEncryptionContext.Keys, E("Internal Error - Structured Encryption encryption context overlaps with Item Encryptor encryption context.")); encryptionContext := encryptionContext + newEncryptionContext; diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy index 306f63521..8361e68b4 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy @@ -552,7 +552,7 @@ module {:options "/functionSyntax:4" } Canonize { // This says vcs_split_on_every_assert, because it has many many asserts, each of which is cheap opaque function {:vcs_split_on_every_assert} - DoResolveLegend(canonSorted : CanonAuthList, legend: Header.Legend, ghost tableName : GoodString, ghost data : AuthList) : (ret : Result) + DoResolveLegend(canonSorted : CanonAuthList, legend: Header.Legend, ghost tableName : GoodString, ghost data : AuthList) : (ret : Result) requires CanonAuthMatchesAuthList(tableName, data, canonSorted) requires Relations.SortedBy(canonSorted, SortCanon.AuthBelow) ensures ret.Success? ==> diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy index f8c178764..0c009e4e7 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy @@ -26,7 +26,7 @@ module StructuredEncryptionCrypt { import AesKdfCtr import Seq import SortCanon - // import Relations + // import Relations import opened Canonize function method FieldKey(HKDFOutput : Bytes, offset : uint32) @@ -455,9 +455,9 @@ module StructuredEncryptionCrypt { //# The `Cipherkey` MUST be the first 32 bytes of the `FieldKey` && KeySize == 32 && encryptInput.key == fieldKey[0..KeySize] - //= specification/structured-encryption/encrypt-path-structure.md#calculate-cipherkey-and-nonce - //= type=implication - //# The `Nonce` MUST be the remaining 12 bytes of the `FieldKey` + //= specification/structured-encryption/encrypt-path-structure.md#calculate-cipherkey-and-nonce + //= type=implication + //# The `Nonce` MUST be the remaining 12 bytes of the `FieldKey` && NonceSize == 12 && |fieldKey| - KeySize == 12 && encryptInput.iv == fieldKey[KeySize..] @@ -510,10 +510,10 @@ module StructuredEncryptionCrypt { returns (ret : Result) ensures ret.Success? ==> && |data.value| >= (AuthTagSize+2) - //= specification/structured-encryption/decrypt-path-structure.md#terminal-data-decryption - //= type=implication - //# The output Terminal Data MUST have a [Terminal Type Id](./structures.md#terminal-type-id) - //# equal to the deserialized Terminal Type Id. + //= specification/structured-encryption/decrypt-path-structure.md#terminal-data-decryption + //= type=implication + //# The output Terminal Data MUST have a [Terminal Type Id](./structures.md#terminal-type-id) + //# equal to the deserialized Terminal Type Id. && ret.value.typeId == data.value[0..TYPEID_LEN] && ret.value != data diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy index 66169eea6..66c93b86e 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy @@ -114,17 +114,17 @@ module StructuredEncryptionHeader { function method {:opaque} serialize() : (ret : Bytes) ensures && PREFIX_LEN <= |ret| - //= specification/structured-encryption/header.md#partial-header - //= type=implication - //# The Partial Header MUST be - // | Length (bytes) | Meaning | - // |---|---| - // | 1 | [Format Version](#format-version) | - // | 1 | [Format Flavor](#format-flavor) | - // | 32 | [Message ID](#message-id) | - // | Variable | [Encrypt Legend](#encrypt-legend) | - // | Variable | [Encryption Context](#encryption-context) | - // | Variable | [Encrypted Data Keys](#encrypted-data-keys) | + //= specification/structured-encryption/header.md#partial-header + //= type=implication + //# The Partial Header MUST be + // | Length (bytes) | Meaning | + // |---|---| + // | 1 | [Format Version](#format-version) | + // | 1 | [Format Flavor](#format-flavor) | + // | 32 | [Message ID](#message-id) | + // | Variable | [Encrypt Legend](#encrypt-legend) | + // | Variable | [Encryption Context](#encryption-context) | + // | Variable | [Encrypted Data Keys](#encrypted-data-keys) | && ret == ( [version] + [flavor] @@ -321,10 +321,10 @@ module StructuredEncryptionHeader { requires ValidSuite(alg) ensures ret.Success? ==> && |ret.value| == COMMITMENT_LEN - //= specification/structured-encryption/header.md#commitment-calculation - //= type=implication - //# The Header Commitment MUST be calculated as a the first 32 bytes of an HmacSha384, - //# with the serialized partial header as the message, and the Commit Key as the key. + //= specification/structured-encryption/header.md#commitment-calculation + //= type=implication + //# The Header Commitment MUST be calculated as a the first 32 bytes of an HmacSha384, + //# with the serialized partial header as the message, and the Commit Key as the key. && var input := Prim.HMacInput( digestAlgorithm := alg.commitment.HKDF.hmac, key := commitKey, diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java index 0c8a47ea8..77ad60629 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/MultiKeyStore.java @@ -27,7 +27,7 @@ public class MultiKeyStore { private final CacheType cache; /** - * Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ private final String partitionId; @@ -60,7 +60,7 @@ public CacheType cache() { } /** - * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @return Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ public String partitionId() { return this.partitionId; @@ -106,12 +106,12 @@ public interface Builder { CacheType cache(); /** - * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ Builder partitionId(String partitionId); /** - * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @return Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ String partitionId(); diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java index e38c22ae3..4627c3f5f 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java @@ -27,7 +27,7 @@ public class SingleKeyStore { private final CacheType cache; /** - * Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ private final String partitionId; @@ -60,7 +60,7 @@ public CacheType cache() { } /** - * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @return Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ public String partitionId() { return this.partitionId; @@ -106,12 +106,12 @@ public interface Builder { CacheType cache(); /** - * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @param partitionId Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ Builder partitionId(String partitionId); /** - * @return Partition ID to distinguish Beacon Key Sources writing to a cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the cache. + * @return Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache. */ String partitionId(); diff --git a/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy b/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy index cb0d63baa..2aff57584 100644 --- a/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy +++ b/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // Do not modify this file. This file is machine generated, and any changes to it will be overwritten. include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy" - // BEGIN MANUAL EDIT +// BEGIN MANUAL EDIT include "../../../../DynamoDbEncryption/dafny/DynamoDbEncryption/src/Index.dfy" include "../../../../submodules/MaterialProviders/TestVectorsAwsCryptographicMaterialProviders/dafny/KeyVectors/src/Index.dfy" - // END MANUAL EDIT +// END MANUAL EDIT abstract module WrappedAbstractAwsCryptographyDynamoDbEncryptionService { import opened Wrappers import opened StandardLibrary.UInt diff --git a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy index 266349a5d..cddf36526 100644 --- a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy +++ b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy @@ -526,7 +526,7 @@ module {:options "-functionSyntax:4"} JsonConfig { var client :- expect Primitives.AtomicPrimitives(); // Create a test partitionIdBytes - var partitionIdBytes : seq := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); var src := SI.KeySource(client, store, SI.SingleLoc("foo"), cache, 100 as uint32, partitionIdBytes); var bv :- expect SI.MakeBeaconVersion(1, src, map[], map[], map[]); From 77abfc21cc388f55ff743d3402753d452aa3b9c5 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 13 Dec 2024 14:58:44 -0800 Subject: [PATCH 08/29] m --- ...yptographyDbEncryptionSdkDynamoDbTypes.dfy | 2 +- .../dafny/DynamoDbEncryption/src/Beacon.dfy | 28 +++++++-------- .../DynamoDbEncryption/src/CompoundBeacon.dfy | 18 +++++----- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 36 +++++++++---------- .../DynamoDbEncryption/src/DynamoToStruct.dfy | 8 ++--- .../DynamoDbEncryption/src/FilterExpr.dfy | 6 ++-- .../DynamoDbEncryption/src/SearchInfo.dfy | 34 +++++++++--------- .../dafny/DynamoDbEncryption/src/Virtual.dfy | 22 ++++++------ ...DbEncryptionSdkDynamoDbTransformsTypes.dfy | 2 +- .../src/DdbMiddlewareConfig.dfy | 6 ++-- .../src/DynamoDbMiddlewareSupport.dfy | 10 +++--- .../src/UpdateItemTransform.dfy | 4 +-- ...ncryptionSdkDynamoDbItemEncryptorTypes.dfy | 2 +- ...tionSdkDynamoDbItemEncryptorOperations.dfy | 24 ++++++------- ...EncryptionSdkStructuredEncryptionTypes.dfy | 2 +- ...ptionSdkStructuredEncryptionOperations.dfy | 12 +++---- .../StructuredEncryption/src/Canonize.dfy | 2 +- .../dafny/StructuredEncryption/src/Crypt.dfy | 16 ++++----- .../dafny/StructuredEncryption/src/Header.dfy | 30 ++++++++-------- ...tographyDynamoDbEncryptionTypesWrapped.dfy | 4 +-- 20 files changed, 134 insertions(+), 134 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy index 2eb0fb26f..2712ba605 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/AwsCryptographyDbEncryptionSdkDynamoDbTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.internald import AwsCryptographyKeyStoreTypes import AwsCryptographyPrimitivesTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy index d6bd1ff17..6d423cc91 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy @@ -236,14 +236,14 @@ module BaseBeacon { //# [AttributeValue](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html) //# MUST be type "SS" StringSet. && (ret.value.Some? ==> ret.value.value.SS?) - //= specification/searchable-encryption/beacons.md#value-for-a-set-standard-beacon - //= type=implication - //# * The resulting set MUST NOT contain duplicates. + //= specification/searchable-encryption/beacons.md#value-for-a-set-standard-beacon + //= type=implication + //# * The resulting set MUST NOT contain duplicates. && (ret.value.Some? ==> HasNoDuplicates(ret.value.value.SS)) - //= specification/searchable-encryption/beacons.md#asset-initialization - //= type=implication - //# * Writing an item MUST fail if the item contains this beacon's attribute, - //# and that attribute is not of type Set. + //= specification/searchable-encryption/beacons.md#asset-initialization + //= type=implication + //# * Writing an item MUST fail if the item contains this beacon's attribute, + //# and that attribute is not of type Set. && var value := TermLoc.TermToAttr(loc, item, None); && (value.Some? && !(value.value.SS? || value.value.NS? || value.value.BS?) ==> ret.Failure?) { @@ -272,14 +272,14 @@ module BaseBeacon { //= type=implication //# * This operation MUST return no value if the associated field does not exist in the record && (bytes.None? ==> ret.value.None?) - //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon - //= type=implication - //# * This operation MUST convert the attribute value of the associated field to - //# a sequence of bytes, as per [attribute serialization](../dynamodb-encryption-client/ddb-attribute-serialization.md). + //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon + //= type=implication + //# * This operation MUST convert the attribute value of the associated field to + //# a sequence of bytes, as per [attribute serialization](../dynamodb-encryption-client/ddb-attribute-serialization.md). && (bytes.Some? ==> ret.value.Some? && hash(bytes.value, key).Success? && ret.value.value == DDB.AttributeValue.S(hash(bytes.value, key).value)) - //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon - //= type=implication - //# * This operation MUST return the [basicHash](#basichash) of the resulting bytes and the configured [beacon length](#beacon-length). + //= specification/searchable-encryption/beacons.md#value-for-a-non-set-standard-beacon + //= type=implication + //# * This operation MUST return the [basicHash](#basichash) of the resulting bytes and the configured [beacon length](#beacon-length). && (bytes.Some? ==> ret.value.Some? && base.hash(bytes.value, key, length).Success? && ret.value.value == DDB.AttributeValue.S(base.hash(bytes.value, key, length).value)) { var bytes :- VirtToBytes(loc, item, vf); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy index 275f008fc..e8fc00e35 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy @@ -397,9 +397,9 @@ module CompoundBeacon { //= type=implication //# * If a string is returned, it MUST NOT be empty. && |res.value.value| > 0 - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST iterate through all constructors, in order, using the first that succeeds. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST iterate through all constructors, in order, using the first that succeeds. && TryConstructors(construct, item, vf, keys).Success? { TryConstructors(construct, item, vf, keys) @@ -578,9 +578,9 @@ module CompoundBeacon { ensures part.Signed? && ret.Success? ==> && ret.value == part.prefix + data && 0 < |ret.value| - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST fail if any plaintext value used in the construction contains the split character. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST fail if any plaintext value used in the construction contains the split character. && split !in data //= specification/searchable-encryption/beacons.md#part-value-calculation @@ -593,9 +593,9 @@ module CompoundBeacon { && keys.Keys? && part.beacon.hashStr(data, keys.value).Success? && ret.value == part.prefix + part.beacon.hashStr(data, keys.value).value - //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon - //= type=implication - //# * This operation MUST fail if any plaintext value used in the construction contains the split character. + //= specification/searchable-encryption/beacons.md#value-for-a-compound-beacon + //= type=implication + //# * This operation MUST fail if any plaintext value used in the construction contains the split character. && split !in data { :- Need(split !in data, E("Value '" + data + "' for beacon part " + part.getName() + " contains the split character '" + [split] + "'.")); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 79d2442e2..3973606e4 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -504,10 +504,10 @@ module SearchConfigToInfo { //= type=implication //# Initialization MUST fail if two standard beacons are configured with the same location. && FindBeaconWithThisLocation(converted, loc).None? - //= specification/searchable-encryption/virtual.md#virtual-field-initialization - //= type=implication - //# Initialization MUST fail if a virtual field is defined with only one location, - //# and also a [standard beacon](beacons.md#standard-beacon) is defined with that same location. + //= specification/searchable-encryption/virtual.md#virtual-field-initialization + //= type=implication + //# Initialization MUST fail if a virtual field is defined with only one location, + //# and also a [standard beacon](beacons.md#standard-beacon) is defined with that same location. && FindVirtualFieldWithThisLocation(virtualFields, {loc}).None? { if |beacons| == 0 { @@ -636,10 +636,10 @@ module SearchConfigToInfo { //# or is not `signed`. ensures (&& 0 < |parts| - //= specification/searchable-encryption/beacons.md#signed-part-initialization - //= type=implication - //# If no [terminal location](virtual.md#terminal-location) is provided, - //# the `name` MUST be used as the [terminal location](virtual.md#terminal-location). + //= specification/searchable-encryption/beacons.md#signed-part-initialization + //= type=implication + //# If no [terminal location](virtual.md#terminal-location) is provided, + //# the `name` MUST be used as the [terminal location](virtual.md#terminal-location). && GetLoc(parts[0].name, parts[0].loc).Success? && var loc := GetLoc(parts[0].name, parts[0].loc).value; && !IsSignOnly(outer, CB.Signed(parts[0].prefix, parts[0].name, loc).loc)) @@ -721,10 +721,10 @@ module SearchConfigToInfo { ensures ret.Success? ==> && |ret.value| == 1 && |ret.value[0].parts| == |parts| + |converted| - //= specification/searchable-encryption/beacons.md#default-construction - //= type=implication - //# * This default constructor MUST be all of the signed parts, - //# followed by all the encrypted parts, all parts being required. + //= specification/searchable-encryption/beacons.md#default-construction + //= type=implication + //# * This default constructor MUST be all of the signed parts, + //# followed by all the encrypted parts, all parts being required. && CB.OrderedParts(allParts, numNon) && (forall i | 0 <= i < |ret.value[0].parts| :: && ret.value[0].parts[i].part == allParts[i] @@ -1109,14 +1109,14 @@ module SearchConfigToInfo { ensures ret.Success? && 0 < |names| && data[names[0]].Standard? && data[names[0]].std.share.Some? ==> && var share := data[names[0]].std.share.value; && IsValidShare(data, names[0], data[names[0]].std.length, share).Success? - //= specification/searchable-encryption/beacons.md#shared-initialization - //= type=implication - //# This name MUST be the name of a previously defined Standard Beacon. + //= specification/searchable-encryption/beacons.md#shared-initialization + //= type=implication + //# This name MUST be the name of a previously defined Standard Beacon. && share in data && data[share].Standard? - //= specification/searchable-encryption/beacons.md#shared-initialization - //= type=implication - //# This beacon's [length](#beacon-length) MUST be equal to the `other` beacon's [length](#beacon-length). + //= specification/searchable-encryption/beacons.md#shared-initialization + //= type=implication + //# This beacon's [length](#beacon-length) MUST be equal to the `other` beacon's [length](#beacon-length). && data[share].std.length == data[names[0]].std.length { if |names| == 0 then diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy index 90dd701a9..616dfe404 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy @@ -584,10 +584,10 @@ module DynamoToStruct { ensures ret.Success? ==> && U32ToBigEndian(|b|).Success? && |ret.value| == LENGTH_LEN + |b| - //= specification/dynamodb-encryption-client/ddb-attribute-serialization.md#set-entry-length - //= type=implication - //# Set Entry Length MUST be a big-endian unsigned integer - //# equal to the length of [Set Entry Value](#set-entry-value). + //= specification/dynamodb-encryption-client/ddb-attribute-serialization.md#set-entry-length + //= type=implication + //# Set Entry Length MUST be a big-endian unsigned integer + //# equal to the length of [Set Entry Value](#set-entry-value). && ret.value[0..LENGTH_LEN] == U32ToBigEndian(|b|).value && ret.value[LENGTH_LEN..] == b { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy index 33b937ea2..2f2a2cff9 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy @@ -1289,9 +1289,9 @@ module DynamoDBFilterExpr { a[0] < b[0] else bIsHighSurrogate - // we know aIsHighSurrogate != bIsHighSurrogate and a[0] != b[0] - // so if bIsHighSurrogate then a is less - // and if aIsHighSurrogate then a is greater + // we know aIsHighSurrogate != bIsHighSurrogate and a[0] != b[0] + // so if bIsHighSurrogate then a is less + // and if aIsHighSurrogate then a is greater } predicate method UnicodeLessOrEqual(a : string, b : string) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 9cc900063..0b72cbd20 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -73,11 +73,11 @@ module SearchableEncryptionInfo { //# MUST be generated in accordance with [HMAC Key Generation](#hmac-key-generation). var newKey :- GetBeaconKey(client, key, keysLeft[0]); reveal Seq.HasNoDuplicates(); - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# [Beacon Key Materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#beacon-key-materials) MUST be generated - //# with the [beacon key id](#beacon-key-id) equal to the `beacon key id` - //# and the [HMAC Keys](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#hmac-keys) equal to a map - //# of every [standard beacons](beacons.md#standard-beacon-initialization) name to its generated HMAC key. + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //# [Beacon Key Materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#beacon-key-materials) MUST be generated + //# with the [beacon key id](#beacon-key-id) equal to the `beacon key id` + //# and the [HMAC Keys](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#hmac-keys) equal to a map + //# of every [standard beacons](beacons.md#standard-beacon-initialization) name to its generated HMAC key. output := GetHmacKeys(client, allKeys, keysLeft[1..], key, acc[keysLeft[0] := newKey]); } } @@ -107,14 +107,14 @@ module SearchableEncryptionInfo { && hkdfInput.digestAlgorithm == Prim.SHA_512 && hkdfInput.salt == None && hkdfInput.ikm == key - //= specification/searchable-encryption/search-config.md#hmac-key-generation - //= type=implication - //# The `info` MUST be the concatenation of "AWS_DBE_SCAN_BEACON" encoded as UTF8 - //# and the beacon name. + //= specification/searchable-encryption/search-config.md#hmac-key-generation + //= type=implication + //# The `info` MUST be the concatenation of "AWS_DBE_SCAN_BEACON" encoded as UTF8 + //# and the beacon name. && hkdfInput.info == info - //= specification/searchable-encryption/search-config.md#hmac-key-generation - //= type=implication - //# The `expectedLength` MUST be 64 bytes. + //= specification/searchable-encryption/search-config.md#hmac-key-generation + //= type=implication + //# The `expectedLength` MUST be 64 bytes. && hkdfInput.expectedLength == 64 { var info :- UTF8.Encode("AWS_DBE_SCAN_BEACON" + name).MapFailure(e => E(e)); @@ -234,8 +234,8 @@ module SearchableEncryptionInfo { && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? - // TODO - why is this verifying? - && cacheInput.identifier == UTF8.Encode(keyId).value + // TODO - why is this verifying? + && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication @@ -250,9 +250,9 @@ module SearchableEncryptionInfo { && var oldGetHistory := old(store.History.GetBeaconKey); && var newGetHistory := store.History.GetBeaconKey; && |newGetHistory| == |oldGetHistory|+1 - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //= type=implication - //# If `GetBeaconKey` fails get beacon key MUST fail. + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //= type=implication + //# If `GetBeaconKey` fails get beacon key MUST fail. && Seq.Last(newGetHistory).output.Success? && var storeInput := Seq.Last(newGetHistory).input; && var storeOutput := Seq.Last(newGetHistory).output; diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy index 163726a44..c3cdbf5f6 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/Virtual.dfy @@ -171,10 +171,10 @@ module DdbVirtualFields { //# If the position provided is positive, it MUST be the zero-based index from the start of the list. && (0 <= pos < limit ==> ret == pos) && (limit <= pos ==> ret == limit-1) - //= specification/searchable-encryption/virtual.md#position-definition - //= type=implication - //# If the position provided is negative, it's absolute value MUST be the - //# one-based index from the end of the list, that is, -1 refers to the last item in the list. + //= specification/searchable-encryption/virtual.md#position-definition + //= type=implication + //# If the position provided is negative, it's absolute value MUST be the + //# one-based index from the end of the list, that is, -1 refers to the last item in the list. && (0 < -pos < limit ==> ret == limit + pos) && (limit <= -pos ==> ret == 0) { @@ -259,10 +259,10 @@ module DdbVirtualFields { //= type=implication //# If index is greater than the number of items in the list, an empty string MUST be returned. && (index >= |parts| || -index > |parts| ==> ret == "") - //= specification/searchable-encryption/virtual.md#getsegment-transform-initialization - //= type=implication - //# The GetSegment transform MUST split the input string on the given character, - //# and return the item in the resulting list the corresponds to the given position. + //= specification/searchable-encryption/virtual.md#getsegment-transform-initialization + //= type=implication + //# The GetSegment transform MUST split the input string on the given character, + //# and return the item in the resulting list the corresponds to the given position. && (index < |parts| && -index <= |parts| ==> ret == parts[GetPos(index, |parts|)]) { var parts := Split(s, split); @@ -285,9 +285,9 @@ module DdbVirtualFields { //# GetSegments MUST return the range of parts from low (inclusive) to high (exclusive), //# joined on the `split` character. && (lo < hi ==> ret == Join(parts[lo..hi], [split])) - //= specification/searchable-encryption/virtual.md#getsegments-transform-initialization - //= type=implication - //# If high is less than or equal to low, an empty string MUST be returned. + //= specification/searchable-encryption/virtual.md#getsegments-transform-initialization + //= type=implication + //# If high is less than or equal to low, an empty string MUST be returned. && (lo >= hi ==> ret == "") { var parts := Split(s, split); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy index 5139337ea..6a63e318f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.transform import AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import AwsCryptographyMaterialProvidersTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy index 90ec75d68..e2c4036f7 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DdbMiddlewareConfig.dfy @@ -72,9 +72,9 @@ module DdbMiddlewareConfig { { && (forall tableName <- config.tableEncryptionConfigs :: config.tableEncryptionConfigs[tableName].physicalTableName == tableName) - //= specification/dynamodb-encryption-client/ddb-table-encryption-config.md#logical-table-name - //# When mapping [DynamoDB Table Names](#dynamodb-table-name) to [logical table name](#logical-table-name) - //# there MUST a one to one mapping between the two. + //= specification/dynamodb-encryption-client/ddb-table-encryption-config.md#logical-table-name + //# When mapping [DynamoDB Table Names](#dynamodb-table-name) to [logical table name](#logical-table-name) + //# there MUST a one to one mapping between the two. && (forall c1 <- config.tableEncryptionConfigs.Values, c2 <- config.tableEncryptionConfigs.Values diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy index 00e9427a3..8d2ce7e19 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/DynamoDbMiddlewareSupport.dfy @@ -128,11 +128,11 @@ module DynamoDbMiddlewareSupport { (ret : Result, Error>) ensures ret.Success? && config.search.Some? && config.search.value.curr().keySource.keyLoc.MultiLoc? ==> && output.parsedHeader.Some? - //= specification/searchable-encryption/search-config.md#get-beacon-key-id-from-parsed-header - //= type=implication - //# If the [Parsed Header](../dynamodb-encryption-client/encrypt-item.md#parsed-header)'s encrypted data keys - //# do not contain only one encrypted data key - //# this function MUST fail. + //= specification/searchable-encryption/search-config.md#get-beacon-key-id-from-parsed-header + //= type=implication + //# If the [Parsed Header](../dynamodb-encryption-client/encrypt-item.md#parsed-header)'s encrypted data keys + //# do not contain only one encrypted data key + //# this function MUST fail. && var keys := output.parsedHeader.value.encryptedDataKeys; && |keys| == 1 diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy index 478190680..852c819d6 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/UpdateItemTransform.dfy @@ -188,8 +188,8 @@ module UpdateItemTransform { || input.originalInput.ReturnValues.value.ALL_OLD?) ) { - // This error should not be possible to reach if we assume the DDB API contract is correct. - // We include this runtime check for defensive purposes. + // This error should not be possible to reach if we assume the DDB API contract is correct. + // We include this runtime check for defensive purposes. :- Need(forall k <- attributes.Keys :: !IsSigned(tableConfig, k), E("UpdateItems response contains signed attributes, but does not include the entire item which is required for verification.")); diff --git a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy index e712158c7..2a2c00d7f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/Model/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorTypes.dfy @@ -17,7 +17,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.dynamodb.itemencry import AwsCryptographyMaterialProvidersTypes import AwsCryptographyPrimitivesTypes import ComAmazonawsDynamodbTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy index 411b1496b..c1b9681fa 100644 --- a/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbItemEncryptor/src/AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations.dfy @@ -54,11 +54,11 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs || (unauthenticatedAttributes.Some? && attr in unauthenticatedAttributes.value) || (unauthenticatedPrefix.Some? && unauthenticatedPrefix.value <= attr) || ReservedPrefix <= attr - // Attributes with the reserved prefix are "allowed unauthenticated" in that - // they are not specified as signed within attributeActionsOnEncrypt. - // These attributes MAY still be authenticated via other methods, - // such as "aws_dbe_head" which is explicitly added to the canonical hash - // used in signing. + // Attributes with the reserved prefix are "allowed unauthenticated" in that + // they are not specified as signed within attributeActionsOnEncrypt. + // These attributes MAY still be authenticated via other methods, + // such as "aws_dbe_head" which is explicitly added to the canonical hash + // used in signing. } //= specification/dynamodb-encryption-client/decrypt-item.md#signature-scope @@ -126,9 +126,9 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs { && InSignatureScope(config, attr) && attr !in config.attributeActionsOnEncrypt - // Attributes in signature scope MUST be configured in attributeActionsOnEncrypt - // so these two lines are saying "in scope && not in scope" - // and that's why it's an error + // Attributes in signature scope MUST be configured in attributeActionsOnEncrypt + // so these two lines are saying "in scope && not in scope" + // and that's why it's an error } // Is the attribute name in signature scope? @@ -176,9 +176,9 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs //= type=implication //# If the Version Number is 2, then the base context MUST be the [version 2](./encrypt-item.md#dynamodb-item-base-context-version-2) context. && (header[0] == 2 ==> ret == MakeEncryptionContextV2(config, item)) - //= specification/dynamodb-encryption-client/decrypt-item.md#dynamodb-item-base-context - //= type=implication - //# If the Version Number is 1, the base context MUST be the [version 1](./encrypt-item.md#dynamodb-item-base-context-version-1) context. + //= specification/dynamodb-encryption-client/decrypt-item.md#dynamodb-item-base-context + //= type=implication + //# If the Version Number is 1, the base context MUST be the [version 1](./encrypt-item.md#dynamodb-item-base-context-version-1) context. && (header[0] == 1 ==> ret == MakeEncryptionContextV1(config, item)) && ((header[0] == 1) || (header[0] == 2)) @@ -441,7 +441,7 @@ module AwsCryptographyDbEncryptionSdkDynamoDbItemEncryptorOperations refines Abs // The partition key MUST be CSE.SIGN_ONLY && config.partitionKeyName in config.attributeActionsOnEncrypt && config.attributeActionsOnEncrypt[config.partitionKeyName] == KeyActionFromVersion(config.version) - // The sort key MUST be CSE.SIGN_ONLY + // The sort key MUST be CSE.SIGN_ONLY && (config.sortKeyName.Some? ==> && config.sortKeyName.value in config.attributeActionsOnEncrypt && config.attributeActionsOnEncrypt[config.sortKeyName.value] == KeyActionFromVersion(config.version)) diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy index 0ad88e609..7c14e9f87 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/Model/AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes.dfy @@ -11,7 +11,7 @@ module {:extern "software.amazon.cryptography.dbencryptionsdk.structuredencrypti import opened UTF8 import AwsCryptographyMaterialProvidersTypes import AwsCryptographyPrimitivesTypes - // Generic helpers for verification of mock/unit tests. + // Generic helpers for verification of mock/unit tests. datatype DafnyCallEvent = DafnyCallEvent(input: I, output: O) // Begin Generated Types diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy index ff18410eb..e2aef0b72 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations.dfy @@ -715,9 +715,9 @@ module AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations refines Abst assume {:axiom} input.cmm.Modifies !! {config.materialProviders.History}; var newEncryptionContext :- GetV2EncryptionContext(input.plaintextStructure); if |newEncryptionContext| != 0 { - //= specification/structured-encryption/encrypt-path-structure.md#create-new-encryption-context-and-cmm - //# An error MUST be returned if any of the entries added to the encryption context in this step - //# have the same key as any entry already in the encryption context. + //= specification/structured-encryption/encrypt-path-structure.md#create-new-encryption-context-and-cmm + //# An error MUST be returned if any of the entries added to the encryption context in this step + //# have the same key as any entry already in the encryption context. :- Need(encryptionContext.Keys !! newEncryptionContext.Keys, E("Internal Error - Structured Encryption encryption context overlaps with Item Encryptor encryption context.")); encryptionContext := encryptionContext + newEncryptionContext; @@ -1070,9 +1070,9 @@ module AwsCryptographyDbEncryptionSdkStructuredEncryptionOperations refines Abst //# in the input record, plus the Legend. var newEncryptionContext :- GetV2EncryptionContext(UnCanon(canonData)); if |newEncryptionContext| != 0 { - //= specification/structured-encryption/decrypt-path-structure.md#create-new-encryption-context-and-cmm - //# An error MUST be returned if any of the entries added to the encryption context in this step - //# have the same key as any entry already in the encryption context. + //= specification/structured-encryption/decrypt-path-structure.md#create-new-encryption-context-and-cmm + //# An error MUST be returned if any of the entries added to the encryption context in this step + //# have the same key as any entry already in the encryption context. :- Need(encryptionContext.Keys !! newEncryptionContext.Keys, E("Internal Error - Structured Encryption encryption context overlaps with Item Encryptor encryption context.")); encryptionContext := encryptionContext + newEncryptionContext; diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy index 8361e68b4..306f63521 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Canonize.dfy @@ -552,7 +552,7 @@ module {:options "/functionSyntax:4" } Canonize { // This says vcs_split_on_every_assert, because it has many many asserts, each of which is cheap opaque function {:vcs_split_on_every_assert} - DoResolveLegend(canonSorted : CanonAuthList, legend: Header.Legend, ghost tableName : GoodString, ghost data : AuthList) : (ret : Result) + DoResolveLegend(canonSorted : CanonAuthList, legend: Header.Legend, ghost tableName : GoodString, ghost data : AuthList) : (ret : Result) requires CanonAuthMatchesAuthList(tableName, data, canonSorted) requires Relations.SortedBy(canonSorted, SortCanon.AuthBelow) ensures ret.Success? ==> diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy index 0c009e4e7..f8c178764 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Crypt.dfy @@ -26,7 +26,7 @@ module StructuredEncryptionCrypt { import AesKdfCtr import Seq import SortCanon - // import Relations + // import Relations import opened Canonize function method FieldKey(HKDFOutput : Bytes, offset : uint32) @@ -455,9 +455,9 @@ module StructuredEncryptionCrypt { //# The `Cipherkey` MUST be the first 32 bytes of the `FieldKey` && KeySize == 32 && encryptInput.key == fieldKey[0..KeySize] - //= specification/structured-encryption/encrypt-path-structure.md#calculate-cipherkey-and-nonce - //= type=implication - //# The `Nonce` MUST be the remaining 12 bytes of the `FieldKey` + //= specification/structured-encryption/encrypt-path-structure.md#calculate-cipherkey-and-nonce + //= type=implication + //# The `Nonce` MUST be the remaining 12 bytes of the `FieldKey` && NonceSize == 12 && |fieldKey| - KeySize == 12 && encryptInput.iv == fieldKey[KeySize..] @@ -510,10 +510,10 @@ module StructuredEncryptionCrypt { returns (ret : Result) ensures ret.Success? ==> && |data.value| >= (AuthTagSize+2) - //= specification/structured-encryption/decrypt-path-structure.md#terminal-data-decryption - //= type=implication - //# The output Terminal Data MUST have a [Terminal Type Id](./structures.md#terminal-type-id) - //# equal to the deserialized Terminal Type Id. + //= specification/structured-encryption/decrypt-path-structure.md#terminal-data-decryption + //= type=implication + //# The output Terminal Data MUST have a [Terminal Type Id](./structures.md#terminal-type-id) + //# equal to the deserialized Terminal Type Id. && ret.value.typeId == data.value[0..TYPEID_LEN] && ret.value != data diff --git a/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy b/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy index 66c93b86e..66169eea6 100644 --- a/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy +++ b/DynamoDbEncryption/dafny/StructuredEncryption/src/Header.dfy @@ -114,17 +114,17 @@ module StructuredEncryptionHeader { function method {:opaque} serialize() : (ret : Bytes) ensures && PREFIX_LEN <= |ret| - //= specification/structured-encryption/header.md#partial-header - //= type=implication - //# The Partial Header MUST be - // | Length (bytes) | Meaning | - // |---|---| - // | 1 | [Format Version](#format-version) | - // | 1 | [Format Flavor](#format-flavor) | - // | 32 | [Message ID](#message-id) | - // | Variable | [Encrypt Legend](#encrypt-legend) | - // | Variable | [Encryption Context](#encryption-context) | - // | Variable | [Encrypted Data Keys](#encrypted-data-keys) | + //= specification/structured-encryption/header.md#partial-header + //= type=implication + //# The Partial Header MUST be + // | Length (bytes) | Meaning | + // |---|---| + // | 1 | [Format Version](#format-version) | + // | 1 | [Format Flavor](#format-flavor) | + // | 32 | [Message ID](#message-id) | + // | Variable | [Encrypt Legend](#encrypt-legend) | + // | Variable | [Encryption Context](#encryption-context) | + // | Variable | [Encrypted Data Keys](#encrypted-data-keys) | && ret == ( [version] + [flavor] @@ -321,10 +321,10 @@ module StructuredEncryptionHeader { requires ValidSuite(alg) ensures ret.Success? ==> && |ret.value| == COMMITMENT_LEN - //= specification/structured-encryption/header.md#commitment-calculation - //= type=implication - //# The Header Commitment MUST be calculated as a the first 32 bytes of an HmacSha384, - //# with the serialized partial header as the message, and the Commit Key as the key. + //= specification/structured-encryption/header.md#commitment-calculation + //= type=implication + //# The Header Commitment MUST be calculated as a the first 32 bytes of an HmacSha384, + //# with the serialized partial header as the message, and the Commit Key as the key. && var input := Prim.HMacInput( digestAlgorithm := alg.commitment.HKDF.hmac, key := commitKey, diff --git a/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy b/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy index 2aff57584..cb0d63baa 100644 --- a/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy +++ b/TestVectors/dafny/DDBEncryption/Model/AwsCryptographyDynamoDbEncryptionTypesWrapped.dfy @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // Do not modify this file. This file is machine generated, and any changes to it will be overwritten. include "../../../../submodules/MaterialProviders/StandardLibrary/src/Index.dfy" -// BEGIN MANUAL EDIT + // BEGIN MANUAL EDIT include "../../../../DynamoDbEncryption/dafny/DynamoDbEncryption/src/Index.dfy" include "../../../../submodules/MaterialProviders/TestVectorsAwsCryptographicMaterialProviders/dafny/KeyVectors/src/Index.dfy" -// END MANUAL EDIT + // END MANUAL EDIT abstract module WrappedAbstractAwsCryptographyDynamoDbEncryptionService { import opened Wrappers import opened StandardLibrary.UInt From 5ababd4a2bd41be3760e2a27e68d44be77810b05 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 13 Dec 2024 20:40:44 -0800 Subject: [PATCH 09/29] fix Dafny verification --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 6 +++ .../DynamoDbEncryption/src/SearchInfo.dfy | 45 +++++++++++-------- .../test/BeaconTestFixtures.dfy | 2 +- DynamoDbEncryption/runtimes/java/.gitignore | 3 ++ 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 3973606e4..520346e7a 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -136,6 +136,12 @@ module SearchConfigToInfo { MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1000)) else if config.single.cache.Some? then + // Ideally, we only want to pass a cache here with entryCapacity = 1 + // because the SingleKeyStore caches only one value. + // That is, we SHOULD add a check here for entryCapacity = 1. + // However, that requires us to write an if block for each CacheType. + // Also, it does NOT matter what the entryCapacity is, because the cache + // can only hold one element at a time. config.single.cache.value else MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1)); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 0b72cbd20..f37319956 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -157,10 +157,13 @@ module SearchableEncryptionInfo { partitionIdBytes : seq ) { function Modifies() : set { - client.Modifies + store.Modifies + client.Modifies + store.Modifies + cache.Modifies } predicate ValidState() { - client.ValidState() && store.ValidState() + && client.ValidState() + && store.ValidState() + && cache.ValidState() + && store.Modifies !! cache.Modifies } method getKeyMap(stdNames : seq, keyId : MaybeKeyId) returns (output : Result) requires Seq.HasNoDuplicates(stdNames) @@ -170,7 +173,8 @@ module SearchableEncryptionInfo { { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); - var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes); + var now := Time.GetCurrent(); + var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -180,7 +184,7 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must not be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes); return Success(Keys(theMap)); + case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } } } @@ -213,7 +217,8 @@ module SearchableEncryptionInfo { stdNames : seq, keyId : string, cacheTTL : MP.PositiveLong, - partitionIdBytes : seq + partitionIdBytes : seq, + now : MP.PositiveLong ) returns (output : Result) requires Seq.HasNoDuplicates(stdNames) @@ -230,23 +235,29 @@ module SearchableEncryptionInfo { && var oldHistory := old(cache.History.GetCacheEntry); && var newHistory := cache.History.GetCacheEntry; && |newHistory| == |oldHistory|+1 - && Seq.Last(newHistory).output.Success? && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? - // TODO - why is this verifying? && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# If a [cache entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-entry) //# exists, get beacon key MUST return the [entry materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#materials). - && (cacheOutput.Success? ==> + && (cacheOutput.Success? && cacheEntryWithinLimits( + creationTime := cacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) ==> && cacheOutput.value.materials.BeaconKey? && cacheOutput.value.materials.BeaconKey.hmacKeys.Some? && output.value == cacheOutput.value.materials.BeaconKey.hmacKeys.value) - && (cacheOutput.Failure? ==> + && (cacheOutput.Failure? || !cacheEntryWithinLimits( + creationTime := cacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) ==> && var oldGetHistory := old(store.History.GetBeaconKey); && var newGetHistory := store.History.GetBeaconKey; && |newGetHistory| == |oldGetHistory|+1 @@ -263,14 +274,12 @@ module SearchableEncryptionInfo { && var oldPutHistory := old(cache.History.PutCacheEntry); && var newPutHistory := cache.History.PutCacheEntry; && |newPutHistory| == |oldPutHistory|+1 - && Seq.Last(newPutHistory).output.Success? && var storeInput := Seq.Last(newPutHistory).input; && var storeOutput := Seq.Last(newPutHistory).output; //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# These cached materials MUST be returned. - && storeInput.materials.BeaconKey.hmacKeys == Some(output.value) - + && storeInput.materials.BeaconKey? ==> storeInput.materials.BeaconKey.hmacKeys == Some(output.value) ) { @@ -290,16 +299,12 @@ module SearchableEncryptionInfo { var getCacheInput := MP.GetCacheEntryInput(identifier := identifier, bytesUsed := None); verifyValidStateCache(cache); - assume {:axiom} cache.Modifies == {}; var getCacheOutput := cache.GetCacheEntry(getCacheInput); - // If error is not EntryDoesNotExist, return failure if (getCacheOutput.Failure? && !getCacheOutput.error.EntryDoesNotExist?) { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=getCacheOutput.error)); } - var now := Time.GetCurrent(); - // //= specification/searchable-encryption/search-config.md# //# If using a `Shared` cache across multiple Beacon Key Sources, //# different Key Sources having the same `beaconKey` can have different TTLs. @@ -332,18 +337,20 @@ module SearchableEncryptionInfo { //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) //# equal to now + configured [cacheTTL](#cachettl). - var now := Time.GetCurrent(); + :- expect Need( + (now as int + cacheTTL as int) < UInt.INT64_MAX_LIMIT, + MP.AwsCryptographicMaterialProvidersException(message := "INT64 Overflow when putting cache entry.") + ); var putCacheEntryInput:= MP.PutCacheEntryInput( identifier := identifier, materials := MP.Materials.BeaconKey(beaconKeyMaterials), creationTime := now, - expiryTime := now+cacheTTL as MP.PositiveLong, + expiryTime := now + cacheTTL, messagesUsed := None, bytesUsed := None ); verifyValidStateCache(cache); - assume {:axiom} cache.Modifies == {}; var putResult := cache.PutCacheEntry(putCacheEntryInput); if (putResult.Failure? && !putResult.error.EntryAlreadyExists?) { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index 30a04e3e1..b40e097f8 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -247,7 +247,7 @@ module BeaconTestFixtures { return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0, partitionIdBytes); } - method GetMultiSource(keyName : string, version : BeaconVersion, partitionId: Option>, shared_cache: Option) returns (output : SI.KeySource) + method GetMultiSource(keyName : string, version : BeaconVersion) returns (output : SI.KeySource) requires version.keyStore.ValidState() ensures output.ValidState() ensures version.keyStore == output.store diff --git a/DynamoDbEncryption/runtimes/java/.gitignore b/DynamoDbEncryption/runtimes/java/.gitignore index fe5c7a5ac..58db26ec3 100644 --- a/DynamoDbEncryption/runtimes/java/.gitignore +++ b/DynamoDbEncryption/runtimes/java/.gitignore @@ -4,6 +4,9 @@ # Ignore Gradle build output directory build +# Ignore bin +bin + # JetBrains .idea/* *.iml From ee868b033499508db2605125a7909084026f25d1 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 13 Dec 2024 20:42:25 -0800 Subject: [PATCH 10/29] format --- .../DynamoDbEncryption/src/SearchInfo.dfy | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index f37319956..da328b980 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -245,19 +245,19 @@ module SearchableEncryptionInfo { //# If a [cache entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-entry) //# exists, get beacon key MUST return the [entry materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#materials). && (cacheOutput.Success? && cacheEntryWithinLimits( - creationTime := cacheOutput.value.creationTime, - now := now, - ttlSeconds := cacheTTL - ) ==> + creationTime := cacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) ==> && cacheOutput.value.materials.BeaconKey? && cacheOutput.value.materials.BeaconKey.hmacKeys.Some? && output.value == cacheOutput.value.materials.BeaconKey.hmacKeys.value) && (cacheOutput.Failure? || !cacheEntryWithinLimits( - creationTime := cacheOutput.value.creationTime, - now := now, - ttlSeconds := cacheTTL - ) ==> + creationTime := cacheOutput.value.creationTime, + now := now, + ttlSeconds := cacheTTL + ) ==> && var oldGetHistory := old(store.History.GetBeaconKey); && var newGetHistory := store.History.GetBeaconKey; && |newGetHistory| == |oldGetHistory|+1 @@ -333,10 +333,10 @@ module SearchableEncryptionInfo { var keyMap :- getAllKeys(stdNames, key.value); var beaconKeyMaterials := rawBeaconKeyMaterials.beaconKeyMaterials.(beaconKey := None, hmacKeys := Some(keyMap)); - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) - //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) - //# equal to now + configured [cacheTTL](#cachettl). + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) + //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) + //# equal to now + configured [cacheTTL](#cachettl). :- expect Need( (now as int + cacheTTL as int) < UInt.INT64_MAX_LIMIT, MP.AwsCryptographicMaterialProvidersException(message := "INT64 Overflow when putting cache entry.") From 2fac140b641a893f8086b64c516d3298f022a287 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 17 Dec 2024 16:38:51 -0800 Subject: [PATCH 11/29] almost fix verification --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 21 +++++++++++++++++++ .../DynamoDbEncryption/src/SearchInfo.dfy | 10 +++++---- .../test/BeaconTestFixtures.dfy | 6 ++++++ .../src/Index.dfy | 2 ++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 520346e7a..7cdfa821d 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -38,6 +38,7 @@ module SearchConfigToInfo { method Convert(outer : DynamoDbTableEncryptionConfig) returns (output : Result, Error>) requires ValidSearchConfig(outer.search) + requires outer.search.Some? ==> ValidSharedCache(outer.search.value.versions[0].keySource) ensures output.Success? && output.value.Some? ==> && output.value.value.ValidState() && fresh(output.value.value.versions[0].keySource.client) @@ -74,6 +75,19 @@ module SearchConfigToInfo { forall b <- config.value.versions :: ValidBeaconVersion(b) } + // Valid state of the provided shared cache, if it exists + predicate {:opaque} ValidSharedCache(config: BeaconKeySource) + { + && (&& config.single? + && config.single.cache.Some? + && config.single.cache.value.Shared? + ==> && config.single.cache.value.Shared.ValidState()) + && (&& config.multi? + && config.multi.cache.Some? + && config.multi.cache.value.Shared? + ==> && config.multi.cache.value.Shared.ValidState()) + } + // return true if, `keyFieldName` should be deleted from an item before writing function method ShouldDeleteKeyField(outer : DynamoDbTableEncryptionConfig, keyFieldName : string) : (ret : Result) @@ -102,6 +116,7 @@ module SearchConfigToInfo { returns (output : Result) modifies client.Modifies requires client.ValidState() + requires ValidSharedCache(config) ensures client.ValidState() ensures output.Success? ==> && output.value.ValidState() @@ -149,6 +164,11 @@ module SearchConfigToInfo { var cache; if cacheType.Shared? { cache := cacheType.Shared; + reveal ValidSharedCache(config, keyStore); + + // This axiom is important because it is not easy to prove + // keyStore.Modifies !! cache.Modifies for a shared cache. + assume {:axiom} keyStore.Modifies !! cache.Modifies; } else { //= specification/searchable-encryption/search-config.md#key-store-cache //# For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) @@ -196,6 +216,7 @@ module SearchConfigToInfo { method ConvertVersion(outer : DynamoDbTableEncryptionConfig, config : BeaconVersion) returns (output : Result) requires ValidBeaconVersion(config) + requires ValidSharedCache(config.keySource) ensures output.Success? ==> && output.value.ValidState() && fresh(output.value.keySource.client) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index da328b980..359011ccc 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -274,6 +274,11 @@ module SearchableEncryptionInfo { && var oldPutHistory := old(cache.History.PutCacheEntry); && var newPutHistory := cache.History.PutCacheEntry; && |newPutHistory| == |oldPutHistory|+1 + && ( + var storeOutput := Seq.Last(newPutHistory).output; + || storeOutput.Success? + || storeOutput.error.EntryAlreadyExists? + ) && var storeInput := Seq.Last(newPutHistory).input; && var storeOutput := Seq.Last(newPutHistory).output; //= specification/searchable-encryption/search-config.md#get-beacon-key-materials @@ -333,14 +338,11 @@ module SearchableEncryptionInfo { var keyMap :- getAllKeys(stdNames, key.value); var beaconKeyMaterials := rawBeaconKeyMaterials.beaconKeyMaterials.(beaconKey := None, hmacKeys := Some(keyMap)); + expect now < UInt.BoundedInts.INT64_MAX - cacheTTL; //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) //# equal to now + configured [cacheTTL](#cachettl). - :- expect Need( - (now as int + cacheTTL as int) < UInt.INT64_MAX_LIMIT, - MP.AwsCryptographicMaterialProvidersException(message := "INT64 Overflow when putting cache entry.") - ); var putCacheEntryInput:= MP.PutCacheEntryInput( identifier := identifier, materials := MP.Materials.BeaconKey(beaconKeyMaterials), diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index b40e097f8..5f468d663 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -231,6 +231,9 @@ module BeaconTestFixtures { ensures output.ValidState() ensures version.keyStore == output.store ensures fresh(output.client.Modifies) + ensures + && fresh(output.cache) + && fresh(output.cache.Modifies) { var client :- expect Primitives.AtomicPrimitives(); @@ -252,6 +255,9 @@ module BeaconTestFixtures { ensures output.ValidState() ensures version.keyStore == output.store ensures fresh(output.client.Modifies) + ensures + && fresh(output.cache) + && fresh(output.cache.Modifies) { var client :- expect Primitives.AtomicPrimitives(); var mpl :- expect MaterialProviders.MaterialProviders(); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy index 4bdb3256a..c475fbcb8 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy @@ -154,6 +154,8 @@ module assert SearchConfigToInfo.ValidSearchConfig(inputConfig.search); SearchInModifies(config, tableName); + reveal SearchConfigToInfo.ValidSharedCache(); + assume {:axiom} inputConfig.search.Some? ==> SearchConfigToInfo.ValidSharedCache(inputConfig.search.value.versions[0].keySource); var searchR := SearchConfigToInfo.Convert(inputConfig); var search :- searchR.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e)); assert search.None? || search.value.ValidState(); From c90ec36be09a7d1af4fca29c5e8a79450975b53a Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 17 Dec 2024 16:45:24 -0800 Subject: [PATCH 12/29] fix main merge --- .../dafny/DynamoDbEncryption/src/ConfigToInfo.dfy | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 893013e81..26419815a 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -134,18 +134,6 @@ module SearchConfigToInfo { && config.multi.keyFieldName in outer.attributeActionsOnEncrypt && outer.attributeActionsOnEncrypt[config.multi.keyFieldName] == SE.ENCRYPT_AND_SIGN ==> output.Failure? - // Not in Spec, but for now, SE does not support the Shared Cache Type - ensures - && config.multi? - && config.multi.cache.Some? - && config.multi.cache.value.Shared? - ==> - && output.Failure? - // If the failure was NOT caused by booting up the MPL - && !output.error.AwsCryptographyMaterialProviders? - ==> - && output.error.DynamoDbEncryptionException? - && output.error.message == "Searchable Encryption does not support the Shared Cache type at this time." { // TODO-FutureCleanUp : https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1510 // It is not-good that the MPL is initialized here; From 59862ee2b9afd12128a8cbb1662b02994a106956 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Tue, 17 Dec 2024 16:48:21 -0800 Subject: [PATCH 13/29] format --- .../dafny/DynamoDbEncryption/src/SearchInfo.dfy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 359011ccc..5567e9157 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -275,10 +275,10 @@ module SearchableEncryptionInfo { && var newPutHistory := cache.History.PutCacheEntry; && |newPutHistory| == |oldPutHistory|+1 && ( - var storeOutput := Seq.Last(newPutHistory).output; - || storeOutput.Success? - || storeOutput.error.EntryAlreadyExists? - ) + var storeOutput := Seq.Last(newPutHistory).output; + || storeOutput.Success? + || storeOutput.error.EntryAlreadyExists? + ) && var storeInput := Seq.Last(newPutHistory).input; && var storeOutput := Seq.Last(newPutHistory).output; //= specification/searchable-encryption/search-config.md#get-beacon-key-materials @@ -339,10 +339,10 @@ module SearchableEncryptionInfo { var beaconKeyMaterials := rawBeaconKeyMaterials.beaconKeyMaterials.(beaconKey := None, hmacKeys := Some(keyMap)); expect now < UInt.BoundedInts.INT64_MAX - cacheTTL; - //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) - //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) - //# equal to now + configured [cacheTTL](#cachettl). + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //# These materials MUST be put into the associated [Key Store Cache](#key-store-cache) + //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) + //# equal to now + configured [cacheTTL](#cachettl). var putCacheEntryInput:= MP.PutCacheEntryInput( identifier := identifier, materials := MP.Materials.BeaconKey(beaconKeyMaterials), From be3e77c581abb21bbd6e39692dd20503c0dac78b Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 23 Dec 2024 15:50:15 -0800 Subject: [PATCH 14/29] add logicalKeyStoreName --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 21 +++++++++--- .../DynamoDbEncryption/src/SearchInfo.dfy | 20 ++++++------ .../test/BeaconTestFixtures.dfy | 32 ++++++++++++++++--- .../src/Index.dfy | 2 ++ .../dafny/DDBEncryption/src/JsonConfig.dfy | 16 ++++++++-- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 26419815a..060b6a689 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -39,6 +39,7 @@ module SearchConfigToInfo { returns (output : Result, Error>) requires ValidSearchConfig(outer.search) requires outer.search.Some? ==> ValidSharedCache(outer.search.value.versions[0].keySource) + modifies if outer.search.Some? then outer.search.value.versions[0].keyStore.Modifies else {} ensures output.Success? && output.value.Some? ==> && output.value.value.ValidState() && fresh(output.value.value.versions[0].keySource.client) @@ -57,7 +58,8 @@ module SearchConfigToInfo { } else { :- Need(outer.search.value.writeVersion == 1, E("writeVersion must be '1'.")); :- Need(|outer.search.value.versions| == 1, E("search config must be have exactly one version.")); - var version :- ConvertVersion(outer, outer.search.value.versions[0]); + var beaconVersionConfig := outer.search.value.versions[0]; + var version :- ConvertVersion(outer, beaconVersionConfig); var info := I.MakeSearchInfo(version); return Success(Some(info)); } @@ -115,6 +117,7 @@ module SearchConfigToInfo { ) returns (output : Result) modifies client.Modifies + modifies keyStore.Modifies requires client.ValidState() requires ValidSharedCache(config) ensures client.ValidState() @@ -203,16 +206,25 @@ module SearchConfigToInfo { ); } else { - partitionIdBytes :- I.GeneratePartitionId(); + partitionIdBytes :- I.GenerateUuidBytes(); } + var getKeyStoreInfoOutput? := keyStore.GetKeyStoreInfo(); + var getKeyStoreInfoOutput :- getKeyStoreInfoOutput?.MapFailure(e => Error.AwsCryptographyKeyStore(e)); + var logicalKeyStoreName : string := getKeyStoreInfoOutput.logicalKeyStoreName; + var logicalKeyStoreNameBytes : seq :- UTF8.Encode(logicalKeyStoreName) + .MapFailure( + e => Error.DynamoDbEncryptionException( + message := "Could not UTF-8 Encode Logical Key Store Name: " + e + ) + ); if config.multi? { :- Need(0 < config.multi.cacheTTL, E("Beacon Cache TTL must be at least 1.")); var deleteKey :- ShouldDeleteKeyField(outer, config.multi.keyFieldName); - output := Success(I.KeySource(client, keyStore, I.MultiLoc(config.multi.keyFieldName, deleteKey), cache, config.multi.cacheTTL as uint32, partitionIdBytes)); + output := Success(I.KeySource(client, keyStore, I.MultiLoc(config.multi.keyFieldName, deleteKey), cache, config.multi.cacheTTL as uint32, partitionIdBytes, logicalKeyStoreNameBytes)); } else { :- Need(0 < config.single.cacheTTL, E("Beacon Cache TTL must be at least 1.")); - output := Success(I.KeySource(client, keyStore, I.SingleLoc(config.single.keyId), cache, config.single.cacheTTL as uint32, partitionIdBytes)); + output := Success(I.KeySource(client, keyStore, I.SingleLoc(config.single.keyId), cache, config.single.cacheTTL as uint32, partitionIdBytes, logicalKeyStoreNameBytes)); } } @@ -221,6 +233,7 @@ module SearchConfigToInfo { returns (output : Result) requires ValidBeaconVersion(config) requires ValidSharedCache(config.keySource) + modifies config.keyStore.Modifies ensures output.Success? ==> && output.value.ValidState() && fresh(output.value.keySource.client) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 5567e9157..878c4124f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -129,18 +129,18 @@ module SearchableEncryptionInfo { return Success(newKey); } - // Generates a new partitionId, which is a random UUID - method GeneratePartitionId() returns (output : Result, Error>) + // Generates a new random UUID converted to UTF8 bytes + method GenerateUuidBytes() returns (output : Result, Error>) { var uuid? := UUID.GenerateUUID(); var uuid :- uuid? .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); - var partitionIdBytes: seq :- UUID.ToByteArray(uuid) + var uuidBytes: seq :- UUID.ToByteArray(uuid) .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); - output := Success(partitionIdBytes); + output := Success(uuidBytes); } datatype KeyLocation = @@ -154,7 +154,8 @@ module SearchableEncryptionInfo { keyLoc : KeyLocation, cache : MP.ICryptographicMaterialsCache, cacheTTL : uint32, - partitionIdBytes : seq + partitionIdBytes : seq, + logicalKeyStoreNameBytes : seq ) { function Modifies() : set { client.Modifies + store.Modifies + cache.Modifies @@ -174,7 +175,7 @@ module SearchableEncryptionInfo { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); var now := Time.GetCurrent(); - var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, now as MP.PositiveLong); + var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -184,7 +185,7 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must not be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, now as MP.PositiveLong); return Success(Keys(theMap)); + case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } } } @@ -218,6 +219,7 @@ module SearchableEncryptionInfo { keyId : string, cacheTTL : MP.PositiveLong, partitionIdBytes : seq, + logicalKeyStoreNameBytes : seq, now : MP.PositiveLong ) returns (output : Result) @@ -238,7 +240,7 @@ module SearchableEncryptionInfo { && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? - && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + UTF8.Encode(keyId).value + && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + logicalKeyStoreNameBytes + NULL_BYTE + UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication @@ -297,7 +299,7 @@ module SearchableEncryptionInfo { // Create the Suffix var keyIdBytesR := UTF8.Encode(keyId); var keyIdBytes :- keyIdBytesR.MapFailure(e => E(e)); - var suffix : seq := keyIdBytes; + var suffix : seq := logicalKeyStoreNameBytes + NULL_BYTE + keyIdBytes; // Append Resource Id, Scope Id, Partition Id, and Suffix to create the cache identifier var identifier := resourceId + NULL_BYTE + scopeId + NULL_BYTE + partitionIdBytes + NULL_BYTE + suffix; diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index 5f468d663..4bd26a3c6 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -246,8 +246,20 @@ module BeaconTestFixtures { ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); // Create a test partitionIdBytes - var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); - return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0, partitionIdBytes); + var partitionIdBytes : seq :- expect SI.GenerateUuidBytes(); + + // Create a random logicalKeyStoreNameBytes + // Ideally, this should be taken from the KeyStore version.keyStore, + // but logicalKeyStoreName variable doesn't exist in the + // trait AwsCryptographyKeyStoreTypes.IKeyStoreClient + // Therefore, the only way to get logicalKeyStoreName is + // to call GetKeyStoreInfo, which we don't need to do here + // since this method does NOT test the shared cache + // which is the only place logicalKeyStoreName is used + // (in the cache identifier) + var logicalKeyStoreNameBytes : seq :- expect SI.GenerateUuidBytes(); + + return SI.KeySource(client, version.keyStore, SI.LiteralLoc(keys), cache, 0, partitionIdBytes, logicalKeyStoreNameBytes); } method GetMultiSource(keyName : string, version : BeaconVersion) returns (output : SI.KeySource) @@ -266,8 +278,20 @@ module BeaconTestFixtures { ); var cache :- expect mpl.CreateCryptographicMaterialsCache(input); // Create a test partitionIdBytes - var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); - return SI.KeySource(client, version.keyStore, SI.MultiLoc(keyName, false), cache, 0, partitionIdBytes); + var partitionIdBytes : seq :- expect SI.GenerateUuidBytes(); + + // Create a random logicalKeyStoreNameBytes + // Ideally, this should be taken from the KeyStore version.keyStore, + // but logicalKeyStoreName variable doesn't exist in the + // trait AwsCryptographyKeyStoreTypes.IKeyStoreClient + // Therefore, the only way to get logicalKeyStoreName is + // to call GetKeyStoreInfo, which we don't need to do here + // since this method does NOT test the shared cache + // which is the only place logicalKeyStoreName is used + // (in the cache identifier) + var logicalKeyStoreNameBytes : seq :- expect SI.GenerateUuidBytes(); + + return SI.KeySource(client, version.keyStore, SI.MultiLoc(keyName, false), cache, 0, partitionIdBytes, logicalKeyStoreNameBytes); } const SimpleItem : DDB.AttributeMap := map[ diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy index c475fbcb8..fc245359a 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy @@ -130,6 +130,7 @@ module (if tableConfig.keyring.Some? then tableConfig.keyring.value.Modifies else {}) + (if tableConfig.cmm.Some? then tableConfig.cmm.value.Modifies else {}) + (if tableConfig.legacyOverride.Some? then tableConfig.legacyOverride.value.encryptor.Modifies else {}) + + (if tableConfig.search.Some? then tableConfig.search.value.versions[0].keyStore.Modifies else {}) ) :: o; @@ -155,6 +156,7 @@ module assert SearchConfigToInfo.ValidSearchConfig(inputConfig.search); SearchInModifies(config, tableName); reveal SearchConfigToInfo.ValidSharedCache(); + // TODO: remove assume axiom; this should be generated by smithy-dafny assume {:axiom} inputConfig.search.Some? ==> SearchConfigToInfo.ValidSharedCache(inputConfig.search.value.versions[0].keySource); var searchR := SearchConfigToInfo.Convert(inputConfig); var search :- searchR.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e)); diff --git a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy index cddf36526..46605a425 100644 --- a/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy +++ b/TestVectors/dafny/DDBEncryption/src/JsonConfig.dfy @@ -526,8 +526,20 @@ module {:options "-functionSyntax:4"} JsonConfig { var client :- expect Primitives.AtomicPrimitives(); // Create a test partitionIdBytes - var partitionIdBytes : seq :- expect SI.GeneratePartitionId(); - var src := SI.KeySource(client, store, SI.SingleLoc("foo"), cache, 100 as uint32, partitionIdBytes); + var partitionIdBytes : seq :- expect SI.GenerateUuidBytes(); + + // Create a random logicalKeyStoreNameBytes + // Ideally, this should be taken from the KeyStore store, + // but logicalKeyStoreName variable doesn't exist in the + // trait AwsCryptographyKeyStoreTypes.IKeyStoreClient + // Therefore, the only way to get logicalKeyStoreName is + // to call GetKeyStoreInfo, which we don't need to do here + // since this method does NOT test the shared cache + // which is the only place logicalKeyStoreName is used + // (in the cache identifier) + var logicalKeyStoreNameBytes : seq :- expect SI.GenerateUuidBytes(); + + var src := SI.KeySource(client, store, SI.SingleLoc("foo"), cache, 100 as uint32, partitionIdBytes, logicalKeyStoreNameBytes); var bv :- expect SI.MakeBeaconVersion(1, src, map[], map[], map[]); return Success(bv); From c6755023d808509603d72559532691273270fc5c Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 23 Dec 2024 16:06:12 -0800 Subject: [PATCH 15/29] format --- .../dafny/DynamoDbEncryption/src/ConfigToInfo.dfy | 10 +++++----- .../dafny/DynamoDbEncryptionTransforms/src/Index.dfy | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 060b6a689..02e13104f 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -212,11 +212,11 @@ module SearchConfigToInfo { var getKeyStoreInfoOutput :- getKeyStoreInfoOutput?.MapFailure(e => Error.AwsCryptographyKeyStore(e)); var logicalKeyStoreName : string := getKeyStoreInfoOutput.logicalKeyStoreName; var logicalKeyStoreNameBytes : seq :- UTF8.Encode(logicalKeyStoreName) - .MapFailure( - e => Error.DynamoDbEncryptionException( - message := "Could not UTF-8 Encode Logical Key Store Name: " + e - ) - ); + .MapFailure( + e => Error.DynamoDbEncryptionException( + message := "Could not UTF-8 Encode Logical Key Store Name: " + e + ) + ); if config.multi? { :- Need(0 < config.multi.cacheTTL, E("Beacon Cache TTL must be at least 1.")); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy index fc245359a..f08693770 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy @@ -156,7 +156,7 @@ module assert SearchConfigToInfo.ValidSearchConfig(inputConfig.search); SearchInModifies(config, tableName); reveal SearchConfigToInfo.ValidSharedCache(); - // TODO: remove assume axiom; this should be generated by smithy-dafny + // TODO: remove assume axiom; this should be generated by smithy-dafny assume {:axiom} inputConfig.search.Some? ==> SearchConfigToInfo.ValidSharedCache(inputConfig.search.value.versions[0].keySource); var searchR := SearchConfigToInfo.Convert(inputConfig); var search :- searchR.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e)); From fe1402482e1744abbb107c0d2f3227837451ab3e Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 14:55:07 -0800 Subject: [PATCH 16/29] add tests --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 2 +- .../DynamoDbEncryption/src/SearchInfo.dfy | 2 +- .../dafny/DynamoDbEncryption/test/Beacon.dfy | 219 ++++++++++++++++++ .../test/BeaconTestFixtures.dfy | 146 ++++++++++++ ...DbEncryptionSdkDynamoDbTransformsTypes.dfy | 168 ++++++++------ .../src/Index.dfy | 2 - .../test/PutItemTransform.dfy | 4 +- .../test/TestFixtures.dfy | 2 +- 8 files changed, 471 insertions(+), 74 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 02e13104f..ce29bf1b1 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -171,7 +171,7 @@ module SearchConfigToInfo { var cache; if cacheType.Shared? { cache := cacheType.Shared; - reveal ValidSharedCache(config, keyStore); + reveal ValidSharedCache(config); // This axiom is important because it is not easy to prove // keyStore.Modifies !! cache.Modifies for a shared cache. diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 878c4124f..f4b81db05 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -183,7 +183,7 @@ module SearchableEncryptionInfo { return Success(Keys(theMap)); } else { match keyId { - case DontUseKeyId => return Failure(E("KeyID must not be supplied with a MultiKeyStore")); + case DontUseKeyId => return Failure(E("KeyID must be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy index 7c4391d40..40b6ca477 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy @@ -105,6 +105,225 @@ module TestBaseBeacon { expect goodAttrs["aws_dbe_b_std2"] == newAttrs["aws_dbe_b_std2"]; } + method {:test} TestSharedCacheBeaconsSingleKeyStoreWithSamePartitionId() + { + var partitionId : string := "partitionId"; + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var badVersion := GetLotsaBeaconsSingleWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := Some(partitionId)); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var version := GetLotsaBeaconsSingleWithSharedCache(cache := sharedCache, partitionId := Some(partitionId)); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + + // This is expected to pass now because the cache already has cached material for this Branch Key ID. + // This is a hack to test that the correct material is cached. + var badAttrsNowCached :- expect badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + } + + method {:test} TestSharedCacheBeaconsSingleKeyStoreWithDifferentPartitionId() + { + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var partitionIdBadVersion : string := "partitionIdBadVersion"; + var badVersion := GetLotsaBeaconsSingleWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := Some(partitionIdBadVersion)); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var partitionIdGoodVersion : string := "partitionIdGoodVersion"; + var version := GetLotsaBeaconsSingleWithSharedCache(cache := sharedCache, partitionId := Some(partitionIdGoodVersion)); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + + // This is still expected to fail because the partitionId for the cached material is different. + var badAttrsNowCached := badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + expect badAttrsNowCached.Failure?; + } + + method {:test} TestSharedCacheBeaconsSingleKeyStoreWithUnspecifiedPartitionId() + { + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var badVersion := GetLotsaBeaconsSingleWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := None); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var version := GetLotsaBeaconsSingleWithSharedCache(cache := sharedCache, partitionId := None); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + + // This is still expected to fail because the partitionId for the cached material is different. + // If the user does NOT specify the partitionId, it is set to a random UUID + var badAttrsNowCached := badBv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId); + expect badAttrsNowCached.Failure?; + } + + method {:test} TestSharedCacheBeaconsMultiKeyStoreWithSamePartitionId() + { + var partitionId : string := "partitionId"; + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var badVersion := GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := Some(partitionId)); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var version := GetLotsaBeaconsMultiWithSharedCache(cache := sharedCache, partitionId := Some(partitionId)); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + + // This is expected to pass now because the cache already has cached material for this Branch Key ID. + // This is a hack to test that the correct material is cached. + var badAttrsNowCached :- expect badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + } + + + method {:test} TestSharedCacheBeaconsMultiKeyStoreWithDifferentPartitionId() + { + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var partitionIdBadVersion : string := "partitionIdBadVersion"; + var badVersion := GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := Some(partitionIdBadVersion)); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var partitionIdGoodVersion : string := "partitionIdGoodVersion"; + var version := GetLotsaBeaconsMultiWithSharedCache(cache := sharedCache, partitionId := Some(partitionIdGoodVersion)); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + + // This is still expected to fail because the partitionId for the cached material is different. + var badAttrsNowCached := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + expect badAttrsNowCached.Failure?; + } + + method {:test} TestSharedCacheBeaconsMultiKeyStoreWithUnspecifiedPartitionId() + { + var sharedCache : MPT.CacheType := GetSharedCache(); + var primitives :- expect Primitives.AtomicPrimitives(); + + // Verification by adding assume statements in "by" passes, but there's an error while transpiling code to runtimes + // Dafny is working on a fix. Update this test when Dafny releases `4.9.2`. + // Using `assume{:axiom} false;` for this test for now. + // var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives) by { + // assume {:axiom} C.ValidSharedCache(version.keySource); + // } + // var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + // var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, DontUseKeyId) by { + // assume{:axiom} false; + // } + assume{:axiom} false; + + // This call is expected to fail because we are providing a Bad KeyStore which does NOT exist + var badVersion := GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := None); + var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); + var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + expect badAttrs.Failure?; + + // This is expected to pass because we pass a valid KeyStore + var version := GetLotsaBeaconsMultiWithSharedCache(cache := sharedCache, partitionId := None); + var src :- expect C.MakeKeySource(FullTableConfig, version.keyStore, version.keySource, primitives); + var bv :- expect C.ConvertVersionWithSource(FullTableConfig, version, src); + var goodAttrs :- expect bv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + + // This is still expected to fail because the partitionId for the cached material is different. + // If the user does NOT specify the partitionId, it is set to a random UUID + var badAttrsNowCached := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); + expect badAttrsNowCached.Failure?; + } + method {:test} TestBeaconValues() { var version := GetLotsaBeacons(); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index 4bd26a3c6..207a7bf26 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -141,6 +141,36 @@ module BeaconTestFixtures { return store; } + // Method to create a bad keyStore with the same LogicalKeyStoreName as + // the keyStore generated by the GetKeyStore method + // Used in the Shared Cache to verify cache hits + // If the bv.GenerateEncryptedBeacons() method succeeds in Beacon.dfy + // with even the bad keyStore, that verifies that there's a cache hit. + method GetBadKeyStore() returns (output : SI.ValidStore) + ensures fresh(output.Modifies) + { + var kmsClient :- expect KMS.KMSClient(); + var ddbClient :- expect DDBC.DynamoDBClient(); + var kmsConfig := KTypes.KMSConfiguration.kmsKeyArn( + "arn:aws:kms:us-west-2:370957321024:key/bad-arn" + ); + var keyStoreConfig := KTypes.KeyStoreConfig( + id := None, + kmsConfiguration := kmsConfig, + // This needs to be the same as logicalKeyStoreName of KeyStore + // generated by GetKeyStore to have the same cache identifier + logicalKeyStoreName := "KeyStoreDdbTable", + grantTokens := None, + // This is the bad-table-name which does NOT exist + ddbTableName := "bad-table-name", + ddbClient := Some(ddbClient), + kmsClient := Some(kmsClient) + ); + + var store :- expect KeyStore.KeyStore(keyStoreConfig); + return store; + } + method GetEmptyBeacons() returns (output : BeaconVersion) ensures output.keyStore.ValidState() ensures fresh(output.keyStore.Modifies) @@ -198,6 +228,122 @@ module BeaconTestFixtures { ); } + method GetLotsaBeaconsSingleWithSharedCache(cache: MPT.CacheType, partitionId: Option) returns (output : BeaconVersion) + ensures output.keyStore.ValidState() + ensures fresh(output.keyStore.Modifies) + ensures output.version == 1 + ensures + && output.keySource.single? + && output.keySource.single.cache.Some? + && output.keySource.single.cache.value == cache + { + var store := GetKeyStore(); + return BeaconVersion ( + version := 1, + keyStore := store, + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable + // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, + // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource + keySource := single(SingleKeyStore(keyId := "040a32a8-3737-4f16-a3ba-bd4449556d73", cacheTTL := 42, cache := Some(cache), partitionId := partitionId)), + standardBeacons := [std2, std4, std6, NameTitleBeacon, NameB, TitleB], + compoundBeacons := Some([NameTitle, YearName, Mixed, JustSigned]), + virtualFields := Some([NameTitleField]), + encryptedParts := None, + signedParts := None + ); + } + + method GetLotsaBeaconsSingleWithSharedCacheWithBadKeyStore(cache: MPT.CacheType, partitionId: Option) returns (output : BeaconVersion) + ensures output.keyStore.ValidState() + ensures fresh(output.keyStore.Modifies) + ensures output.version == 1 + ensures + && output.keySource.single? + && output.keySource.single.cache.Some? + { + var store := GetBadKeyStore(); + return BeaconVersion ( + version := 1, + keyStore := store, + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable + // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, + // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource. + // Even though this method creates a BeaconVersion with a Bad Key Store which does NOT exist, + // we need the keyId to be valid for the cache to pick up the correct material to confirm a cache hit in + // the test TestSharedCacheBeaconsSingleKeyStore in Beacon.dfy + keySource := single(SingleKeyStore(keyId := "040a32a8-3737-4f16-a3ba-bd4449556d73", cacheTTL := 42, cache := Some(cache), partitionId := partitionId)), + standardBeacons := [std2, std4, std6, NameTitleBeacon, NameB, TitleB], + compoundBeacons := Some([NameTitle, YearName, Mixed, JustSigned]), + virtualFields := Some([NameTitleField]), + encryptedParts := None, + signedParts := None + ); + } + + method GetLotsaBeaconsMultiWithSharedCache(cache: MPT.CacheType, partitionId: Option) returns (output : BeaconVersion) + ensures output.keyStore.ValidState() + ensures fresh(output.keyStore.Modifies) + ensures output.version == 1 + ensures + && output.keySource.multi? + && output.keySource.multi.cache.Some? + { + var store := GetKeyStore(); + return BeaconVersion ( + version := 1, + keyStore := store, + keySource := multi(MultiKeyStore(keyFieldName := "TheKeyField", cacheTTL := 42, cache := Some(cache), partitionId := partitionId)), + standardBeacons := [std2, std4, std6, NameTitleBeacon, NameB, TitleB], + compoundBeacons := Some([NameTitle, YearName, Mixed, JustSigned]), + virtualFields := Some([NameTitleField]), + encryptedParts := None, + signedParts := None + ); + } + + method GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache: MPT.CacheType, partitionId: Option) returns (output : BeaconVersion) + ensures output.keyStore.ValidState() + ensures fresh(output.keyStore.Modifies) + ensures output.version == 1 + ensures + && output.keySource.multi? + && output.keySource.multi.cache.Some? + { + var store := GetBadKeyStore(); + return BeaconVersion ( + version := 1, + keyStore := store, + keySource := multi(MultiKeyStore(keyFieldName := "TheKeyField", cacheTTL := 42, cache := Some(cache), partitionId := partitionId)), + standardBeacons := [std2, std4, std6, NameTitleBeacon, NameB, TitleB], + compoundBeacons := Some([NameTitle, YearName, Mixed, JustSigned]), + virtualFields := Some([NameTitleField]), + encryptedParts := None, + signedParts := None + ); + } + + method GetSharedCache() + returns (cache: MPT.CacheType) + ensures cache.Shared? && fresh(cache.Shared.Modifies) + { + var mpl :- expect MaterialProviders.MaterialProviders(); + // Initialize the shared Cryptographic Materials Cache + var sharedCache :- expect mpl.CreateCryptographicMaterialsCache( + MPT.CreateCryptographicMaterialsCacheInput( + cache := MPT.CacheType.Default( + MPT.DefaultCache( + entryCapacity := 100 + ) + ) + ) + ); + + // Wrap the initialized shared Cryptographic Materials Cache as a `Shared` CacheType object + cache := MPT.CacheType.Shared( + sharedCache + ); + } + const EmptyTableConfig := DynamoDbTableEncryptionConfig ( logicalTableName := "Foo", partitionKeyName := "foo", diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy index 6a63e318f..de056cf74 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/Model/AwsCryptographyDbEncryptionSdkDynamoDbTransformsTypes.dfy @@ -777,89 +777,123 @@ abstract module AbstractAwsCryptographyDbEncryptionSdkDynamoDbTransformsService tmp6.keySource.single.cache.Some? ==> tmp6.keySource.single.cache.value.Shared? ==> tmp6.keySource.single.cache.value.Shared.ValidState() - modifies set tmps7 <- set t7 <- config.tableEncryptionConfigs.Values | true - && t7.keyring.Some? - :: t7.keyring.value, - obj <- tmps7.Modifies | obj in tmps7.Modifies :: obj - modifies set tmps8 <- set t8 <- config.tableEncryptionConfigs.Values | true - && t8.cmm.Some? - :: t8.cmm.value, - obj <- tmps8.Modifies | obj in tmps8.Modifies :: obj + requires var tmps7 := set t7 | t7 in config.tableEncryptionConfigs.Values; + forall tmp7 :: tmp7 in tmps7 ==> + tmp7.search.Some? ==> + var tmps8 := set t8 | t8 in tmp7.search.value.versions; + forall tmp8 :: tmp8 in tmps8 ==> + tmp8.keySource.multi? ==> + tmp8.keySource.multi.cache.Some? ==> + tmp8.keySource.multi.cache.value.Shared? ==> + tmp8.keySource.multi.cache.value.Shared.ValidState() modifies set tmps9 <- set t9 <- config.tableEncryptionConfigs.Values | true - && t9.legacyOverride.Some? - :: t9.legacyOverride.value.encryptor, + && t9.keyring.Some? + :: t9.keyring.value, obj <- tmps9.Modifies | obj in tmps9.Modifies :: obj modifies set tmps10 <- set t10 <- config.tableEncryptionConfigs.Values | true - && t10.search.Some? - , t11 <- t10.search.value.versions | true - :: t11.keyStore, + && t10.cmm.Some? + :: t10.cmm.value, obj <- tmps10.Modifies | obj in tmps10.Modifies :: obj + modifies set tmps11 <- set t11 <- config.tableEncryptionConfigs.Values | true + && t11.legacyOverride.Some? + :: t11.legacyOverride.value.encryptor, + obj <- tmps11.Modifies | obj in tmps11.Modifies :: obj modifies set tmps12 <- set t12 <- config.tableEncryptionConfigs.Values | true && t12.search.Some? , t13 <- t12.search.value.versions | true - && t13.keySource.single? - && t13.keySource.single.cache.Some? - && t13.keySource.single.cache.value.Shared? - :: t13.keySource.single.cache.value.Shared, + :: t13.keyStore, obj <- tmps12.Modifies | obj in tmps12.Modifies :: obj + modifies set tmps14 <- set t14 <- config.tableEncryptionConfigs.Values | true + && t14.search.Some? + , t15 <- t14.search.value.versions | true + && t15.keySource.single? + && t15.keySource.single.cache.Some? + && t15.keySource.single.cache.value.Shared? + :: t15.keySource.single.cache.value.Shared, + obj <- tmps14.Modifies | obj in tmps14.Modifies :: obj + modifies set tmps16 <- set t16 <- config.tableEncryptionConfigs.Values | true + && t16.search.Some? + , t17 <- t16.search.value.versions | true + && t17.keySource.multi? + && t17.keySource.multi.cache.Some? + && t17.keySource.multi.cache.value.Shared? + :: t17.keySource.multi.cache.value.Shared, + obj <- tmps16.Modifies | obj in tmps16.Modifies :: obj ensures res.Success? ==> && fresh(res.value) && fresh(res.value.Modifies - - ( set tmps14 <- set t14 <- config.tableEncryptionConfigs.Values | true - && t14.keyring.Some? - :: t14.keyring.value, - obj <- tmps14.Modifies | obj in tmps14.Modifies :: obj - ) - ( set tmps15 <- set t15 <- config.tableEncryptionConfigs.Values | true - && t15.cmm.Some? - :: t15.cmm.value, - obj <- tmps15.Modifies | obj in tmps15.Modifies :: obj - ) - ( set tmps16 <- set t16 <- config.tableEncryptionConfigs.Values | true - && t16.legacyOverride.Some? - :: t16.legacyOverride.value.encryptor, - obj <- tmps16.Modifies | obj in tmps16.Modifies :: obj - ) - ( set tmps17 <- set t17 <- config.tableEncryptionConfigs.Values | true - && t17.search.Some? - , t18 <- t17.search.value.versions | true - :: t18.keyStore, - obj <- tmps17.Modifies | obj in tmps17.Modifies :: obj + - ( set tmps18 <- set t18 <- config.tableEncryptionConfigs.Values | true + && t18.keyring.Some? + :: t18.keyring.value, + obj <- tmps18.Modifies | obj in tmps18.Modifies :: obj ) - ( set tmps19 <- set t19 <- config.tableEncryptionConfigs.Values | true - && t19.search.Some? - , t20 <- t19.search.value.versions | true - && t20.keySource.single? - && t20.keySource.single.cache.Some? - && t20.keySource.single.cache.value.Shared? - :: t20.keySource.single.cache.value.Shared, + && t19.cmm.Some? + :: t19.cmm.value, obj <- tmps19.Modifies | obj in tmps19.Modifies :: obj + ) - ( set tmps20 <- set t20 <- config.tableEncryptionConfigs.Values | true + && t20.legacyOverride.Some? + :: t20.legacyOverride.value.encryptor, + obj <- tmps20.Modifies | obj in tmps20.Modifies :: obj + ) - ( set tmps21 <- set t21 <- config.tableEncryptionConfigs.Values | true + && t21.search.Some? + , t22 <- t21.search.value.versions | true + :: t22.keyStore, + obj <- tmps21.Modifies | obj in tmps21.Modifies :: obj + ) - ( set tmps23 <- set t23 <- config.tableEncryptionConfigs.Values | true + && t23.search.Some? + , t24 <- t23.search.value.versions | true + && t24.keySource.single? + && t24.keySource.single.cache.Some? + && t24.keySource.single.cache.value.Shared? + :: t24.keySource.single.cache.value.Shared, + obj <- tmps23.Modifies | obj in tmps23.Modifies :: obj + ) - ( set tmps25 <- set t25 <- config.tableEncryptionConfigs.Values | true + && t25.search.Some? + , t26 <- t25.search.value.versions | true + && t26.keySource.multi? + && t26.keySource.multi.cache.Some? + && t26.keySource.multi.cache.value.Shared? + :: t26.keySource.multi.cache.value.Shared, + obj <- tmps25.Modifies | obj in tmps25.Modifies :: obj ) ) && fresh(res.value.History) && res.value.ValidState() - ensures var tmps21 := set t21 | t21 in config.tableEncryptionConfigs.Values; - forall tmp21 :: tmp21 in tmps21 ==> - tmp21.keyring.Some? ==> - tmp21.keyring.value.ValidState() - ensures var tmps22 := set t22 | t22 in config.tableEncryptionConfigs.Values; - forall tmp22 :: tmp22 in tmps22 ==> - tmp22.cmm.Some? ==> - tmp22.cmm.value.ValidState() - ensures var tmps23 := set t23 | t23 in config.tableEncryptionConfigs.Values; - forall tmp23 :: tmp23 in tmps23 ==> - tmp23.legacyOverride.Some? ==> - tmp23.legacyOverride.value.encryptor.ValidState() - ensures var tmps24 := set t24 | t24 in config.tableEncryptionConfigs.Values; - forall tmp24 :: tmp24 in tmps24 ==> - tmp24.search.Some? ==> - var tmps25 := set t25 | t25 in tmp24.search.value.versions; - forall tmp25 :: tmp25 in tmps25 ==> - tmp25.keyStore.ValidState() - ensures var tmps26 := set t26 | t26 in config.tableEncryptionConfigs.Values; - forall tmp26 :: tmp26 in tmps26 ==> - tmp26.search.Some? ==> - var tmps27 := set t27 | t27 in tmp26.search.value.versions; - forall tmp27 :: tmp27 in tmps27 ==> - tmp27.keySource.single? ==> - tmp27.keySource.single.cache.Some? ==> - tmp27.keySource.single.cache.value.Shared? ==> - tmp27.keySource.single.cache.value.Shared.ValidState() + ensures var tmps27 := set t27 | t27 in config.tableEncryptionConfigs.Values; + forall tmp27 :: tmp27 in tmps27 ==> + tmp27.keyring.Some? ==> + tmp27.keyring.value.ValidState() + ensures var tmps28 := set t28 | t28 in config.tableEncryptionConfigs.Values; + forall tmp28 :: tmp28 in tmps28 ==> + tmp28.cmm.Some? ==> + tmp28.cmm.value.ValidState() + ensures var tmps29 := set t29 | t29 in config.tableEncryptionConfigs.Values; + forall tmp29 :: tmp29 in tmps29 ==> + tmp29.legacyOverride.Some? ==> + tmp29.legacyOverride.value.encryptor.ValidState() + ensures var tmps30 := set t30 | t30 in config.tableEncryptionConfigs.Values; + forall tmp30 :: tmp30 in tmps30 ==> + tmp30.search.Some? ==> + var tmps31 := set t31 | t31 in tmp30.search.value.versions; + forall tmp31 :: tmp31 in tmps31 ==> + tmp31.keyStore.ValidState() + ensures var tmps32 := set t32 | t32 in config.tableEncryptionConfigs.Values; + forall tmp32 :: tmp32 in tmps32 ==> + tmp32.search.Some? ==> + var tmps33 := set t33 | t33 in tmp32.search.value.versions; + forall tmp33 :: tmp33 in tmps33 ==> + tmp33.keySource.single? ==> + tmp33.keySource.single.cache.Some? ==> + tmp33.keySource.single.cache.value.Shared? ==> + tmp33.keySource.single.cache.value.Shared.ValidState() + ensures var tmps34 := set t34 | t34 in config.tableEncryptionConfigs.Values; + forall tmp34 :: tmp34 in tmps34 ==> + tmp34.search.Some? ==> + var tmps35 := set t35 | t35 in tmp34.search.value.versions; + forall tmp35 :: tmp35 in tmps35 ==> + tmp35.keySource.multi? ==> + tmp35.keySource.multi.cache.Some? ==> + tmp35.keySource.multi.cache.value.Shared? ==> + tmp35.keySource.multi.cache.value.Shared.ValidState() // Helper functions for the benefit of native code to create a Success(client) without referring to Dafny internals function method CreateSuccessOfClient(client: IDynamoDbEncryptionTransformsClient): Result { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy index f08693770..77f3b7aab 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/src/Index.dfy @@ -156,8 +156,6 @@ module assert SearchConfigToInfo.ValidSearchConfig(inputConfig.search); SearchInModifies(config, tableName); reveal SearchConfigToInfo.ValidSharedCache(); - // TODO: remove assume axiom; this should be generated by smithy-dafny - assume {:axiom} inputConfig.search.Some? ==> SearchConfigToInfo.ValidSharedCache(inputConfig.search.value.versions[0].keySource); var searchR := SearchConfigToInfo.Convert(inputConfig); var search :- searchR.MapFailure(e => AwsCryptographyDbEncryptionSdkDynamoDb(e)); assert search.None? || search.value.ValidState(); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/PutItemTransform.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/PutItemTransform.dfy index 467ef6c87..8ee8feebf 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/PutItemTransform.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/PutItemTransform.dfy @@ -32,7 +32,7 @@ module PutItemTransformTest { method TestPutItemInputMultiFail(plaintextOverride : Option) { assume {:axiom} false; - var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMutli(plaintextOverride); + var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMulti(plaintextOverride); var tableName := GetTableName("foo"); var input := DDB.PutItemInput( TableName := tableName, @@ -57,7 +57,7 @@ module PutItemTransformTest { method {:test} TestPutItemInputMultiForceAllow() { assume {:axiom} false; - var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMutli( + var middlewareUnderTest := TestFixtures.GetDynamoDbEncryptionTransformsMulti( Some(PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ) ); var tableName := GetTableName("foo"); diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/TestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/TestFixtures.dfy index 6a6a1ef7b..f287a4c47 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/TestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryptionTransforms/test/TestFixtures.dfy @@ -279,7 +279,7 @@ module TestFixtures { "TheKeyField" := SE.SIGN_ONLY ] - method GetDynamoDbEncryptionTransformsMutli(plaintextOverride : Option) + method GetDynamoDbEncryptionTransformsMulti(plaintextOverride : Option) returns (encryption: DynamoDbEncryptionTransforms.DynamoDbEncryptionTransformsClient) ensures encryption.ValidState() ensures fresh(encryption) From 94c421ed75e522dbf8c27a753827be5b9ae5af5c Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 15:12:44 -0800 Subject: [PATCH 17/29] update --- .../dafny/DynamoDbEncryption/test/Beacon.dfy | 6 ++++++ .../DynamoDbEncryption/test/BeaconTestFixtures.dfy | 11 ++++++----- submodules/MaterialProviders | 2 +- submodules/smithy-dafny | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy index 40b6ca477..a812cdc0e 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/Beacon.dfy @@ -273,6 +273,9 @@ module TestBaseBeacon { var badVersion := GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := Some(partitionIdBadVersion)); var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable. + // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, + // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource. var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); expect badAttrs.Failure?; @@ -309,6 +312,9 @@ module TestBaseBeacon { var badVersion := GetLotsaBeaconsMultiWithSharedCacheWithBadKeyStore(cache := sharedCache, partitionId := None); var badSrc :- expect C.MakeKeySource(FullTableConfig, badVersion.keyStore, badVersion.keySource, primitives); var badBv :- expect C.ConvertVersionWithSource(FullTableConfig, badVersion, badSrc); + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable. + // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, + // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource. var badAttrs := badBv.GenerateEncryptedBeacons(SimpleItem, KeyId("040a32a8-3737-4f16-a3ba-bd4449556d73")); expect badAttrs.Failure?; diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy index 207a7bf26..2b16f9c8b 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/test/BeaconTestFixtures.dfy @@ -151,6 +151,7 @@ module BeaconTestFixtures { { var kmsClient :- expect KMS.KMSClient(); var ddbClient :- expect DDBC.DynamoDBClient(); + // This is the bad-arn key which does NOT exist. var kmsConfig := KTypes.KMSConfiguration.kmsKeyArn( "arn:aws:kms:us-west-2:370957321024:key/bad-arn" ); @@ -158,10 +159,10 @@ module BeaconTestFixtures { id := None, kmsConfiguration := kmsConfig, // This needs to be the same as logicalKeyStoreName of KeyStore - // generated by GetKeyStore to have the same cache identifier + // generated by GetKeyStore to have the same cache identifier. logicalKeyStoreName := "KeyStoreDdbTable", grantTokens := None, - // This is the bad-table-name which does NOT exist + // This is the bad-table-name which does NOT exist. ddbTableName := "bad-table-name", ddbClient := Some(ddbClient), kmsClient := Some(kmsClient) @@ -241,9 +242,9 @@ module BeaconTestFixtures { return BeaconVersion ( version := 1, keyStore := store, - // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable. // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, - // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource + // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource. keySource := single(SingleKeyStore(keyId := "040a32a8-3737-4f16-a3ba-bd4449556d73", cacheTTL := 42, cache := Some(cache), partitionId := partitionId)), standardBeacons := [std2, std4, std6, NameTitleBeacon, NameB, TitleB], compoundBeacons := Some([NameTitle, YearName, Mixed, JustSigned]), @@ -265,7 +266,7 @@ module BeaconTestFixtures { return BeaconVersion ( version := 1, keyStore := store, - // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable + // This KeyId is a valid branch_key_id present in the KeyStoreDdbTable. // Providing a valid branch_key_id is important in this method because unlike other tests in Beacon.dfy, // this is used in a test which actually fetches data from DynamoDb without using a Literal KeySource. // Even though this method creates a BeaconVersion with a Bad Key Store which does NOT exist, diff --git a/submodules/MaterialProviders b/submodules/MaterialProviders index 1915a11bb..9c80544bb 160000 --- a/submodules/MaterialProviders +++ b/submodules/MaterialProviders @@ -1 +1 @@ -Subproject commit 1915a11bb84d3d9135b89d2a46ec9d6dff27b493 +Subproject commit 9c80544bbfeac5547915aa93795f9b79eac7db6b diff --git a/submodules/smithy-dafny b/submodules/smithy-dafny index f67c21075..84523f4cb 160000 --- a/submodules/smithy-dafny +++ b/submodules/smithy-dafny @@ -1 +1 @@ -Subproject commit f67c21075ebac40e052204b6827a4db641e65862 +Subproject commit 84523f4cb6e76503c7f2cd4ac9e67adea709d013 From 38b93dd394fe2f067c65be7c62dbef986810b929 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 15:21:06 -0800 Subject: [PATCH 18/29] increase stack size --- .github/workflows/library_rust_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/library_rust_tests.yml b/.github/workflows/library_rust_tests.yml index 10fe5dfcf..e921284a8 100644 --- a/.github/workflows/library_rust_tests.yml +++ b/.github/workflows/library_rust_tests.yml @@ -27,7 +27,7 @@ jobs: id-token: write contents: read env: - RUST_MIN_STACK: 104857600 + RUST_MIN_STACK: 404857600 steps: - name: Support longpaths on Git checkout run: | From ccb525dc4c0867f0b826c755773980efcf12e9cf Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 16:23:21 -0800 Subject: [PATCH 19/29] hash cache id --- .../Model/DynamoDbEncryption.smithy | 5 +++- .../DynamoDbEncryption/src/SearchInfo.dfy | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy index 77f11c466..0750ae637 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy @@ -708,6 +708,8 @@ structure KeyStoreReference {} //# On initialization of a Single Key Store, the caller MUST provide: //# - [Beacon Key Id](#beacon-key-id) //# - [cacheTTL](#cachettl) +//# - [cache](#key-store-cache) +//# - [partition-id](#partition-id) @javadoc("The configuration for using a single Beacon Key.") structure SingleKeyStore { @@ -728,7 +730,8 @@ structure SingleKeyStore { //# On initialization of a Multi Key Store, the caller MUST provide: //# - [Beacon Key Field Name](#beacon-key-field-name) //# - [cacheTTL](#cachettl) -//# - [max cache size](#max-cache-size) +//# - [cache](#key-store-cache) +//# - [partition-id](#partition-id) @javadoc("The configuration for using multiple Beacon Keys.") structure MultiKeyStore { diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index f4b81db05..d8ec44514 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -28,6 +28,7 @@ module SearchableEncryptionInfo { import SE = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes import opened CacheConstants import UUID + import Digest //= specification/searchable-encryption/search-config.md#version-number //= type=implication @@ -175,7 +176,7 @@ module SearchableEncryptionInfo { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); var now := Time.GetCurrent(); - var theMap :- getKeysCache(stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); + var theMap :- getKeysCache(client, stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -185,7 +186,7 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); + case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(client, stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); } } } @@ -215,6 +216,7 @@ module SearchableEncryptionInfo { } method getKeysCache( + client : Primitives.AtomicPrimitivesClient, stdNames : seq, keyId : string, cacheTTL : MP.PositiveLong, @@ -240,7 +242,8 @@ module SearchableEncryptionInfo { && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? - && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + logicalKeyStoreNameBytes + NULL_BYTE + UTF8.Encode(keyId).value + // This is no longer true since we're taking a SHA384 of the identifier + // && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + logicalKeyStoreNameBytes + NULL_BYTE + UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication @@ -304,7 +307,23 @@ module SearchableEncryptionInfo { // Append Resource Id, Scope Id, Partition Id, and Suffix to create the cache identifier var identifier := resourceId + NULL_BYTE + scopeId + NULL_BYTE + partitionIdBytes + NULL_BYTE + suffix; - var getCacheInput := MP.GetCacheEntryInput(identifier := identifier, bytesUsed := None); + // Take a SHA384 of the cache identifier + var hashAlgorithm := Prim.DigestAlgorithm.SHA_384; + + var identifierDigestInput := Prim.DigestInput( + digestAlgorithm := hashAlgorithm, message := identifier + ); + var maybeCacheDigest := Digest.Digest(identifierDigestInput); + var cacheDigest :- maybeCacheDigest.MapFailure(e => AwsCryptographyPrimitives(e)); + + :- Need( + |cacheDigest| == Digest.Length(hashAlgorithm), + Error.DynamoDbEncryptionException( + message := "Digest generated a message not equal to the expected length.") + ); + + // Use the SHA384 of the identifier as the cache identifier + var getCacheInput := MP.GetCacheEntryInput(identifier := cacheDigest, bytesUsed := None); verifyValidStateCache(cache); var getCacheOutput := cache.GetCacheEntry(getCacheInput); // If error is not EntryDoesNotExist, return failure From 453f0faa276ef070c847496fee5bb0b4b8e98cab Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 16:53:28 -0800 Subject: [PATCH 20/29] m --- .../dafny/DynamoDbEncryption/src/SearchInfo.dfy | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index d8ec44514..f3d5db1cc 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -242,8 +242,6 @@ module SearchableEncryptionInfo { && var cacheInput := Seq.Last(newHistory).input; && var cacheOutput := Seq.Last(newHistory).output; && UTF8.Encode(keyId).Success? - // This is no longer true since we're taking a SHA384 of the identifier - // && cacheInput.identifier == RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + logicalKeyStoreNameBytes + NULL_BYTE + UTF8.Encode(keyId).value //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication @@ -365,7 +363,7 @@ module SearchableEncryptionInfo { //# with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) //# equal to now + configured [cacheTTL](#cachettl). var putCacheEntryInput:= MP.PutCacheEntryInput( - identifier := identifier, + identifier := cacheDigest, materials := MP.Materials.BeaconKey(beaconKeyMaterials), creationTime := now, expiryTime := now + cacheTTL, From 320de24a99d3d79464fd895f811ffcaaf3af029b Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 17:06:45 -0800 Subject: [PATCH 21/29] fix Duvet --- .../DynamoDbEncryption/src/SearchInfo.dfy | 4 +- .../searchable-encryption/search-config.md | 217 ++++++++++++++++-- 2 files changed, 197 insertions(+), 24 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index f3d5db1cc..6c4d293cd 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -329,9 +329,9 @@ module SearchableEncryptionInfo { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=getCacheOutput.error)); } - // //= specification/searchable-encryption/search-config.md# + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //# If using a `Shared` cache across multiple Beacon Key Sources, - //# different Key Sources having the same `beaconKey` can have different TTLs. + //# different Beacon Key Sources having the same `branchKey` can have different TTLs. //# In such a case, the expiry time in the cache is set according to the Beacon Key Source that populated the cache. //# There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, //# `time.now() - cacheEntryCreationTime <= ttlSeconds` is true and diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index 3c7c4a474..1e298b4dd 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -5,7 +5,12 @@ ## Version -1.0.0 +- 1.1.0 + - [Update Cache Entry Identifier Formulas to shared cache across multiple Beacon Key Sources](../../changes/2024-09-13_cache-across-hierarchical-keyrings/change.md) + - New optional parameter `Partition ID` used to distinguish Cryptographic Material Providers (i.e: Beacon Key Sources) writing to a cache + - New optional parameter `cache` allowed while creating a `SingleKeyStore` +- 1.0.0 + - Initial record ### Changelog @@ -164,10 +169,10 @@ This can also be described as single tenant. On initialization of a Single Key Store, the caller MUST provide: -TODO: Update - - [Beacon Key Id](#beacon-key-id) - [cacheTTL](#cachettl) +- [cache](#key-store-cache) +- [partition-id](#partition-id) ### Multi Key Store Initialization @@ -177,11 +182,10 @@ This can also be described as multi tenant. On initialization of a Multi Key Store, the caller MUST provide: -TODO: Update - - [Beacon Key Field Name](#beacon-key-field-name) - [cacheTTL](#cachettl) -- [max cache size](#max-cache-size) +- [cache](#key-store-cache) +- [partition-id](#partition-id) ### Field descriptions @@ -212,28 +216,32 @@ and used to extract a beacon key id from a query. The [cacheTTL](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#time-to-live-ttl) for how long a beacon key should exist locally before reauthorization. -#### max cache size +### Key Store Cache -TODO: Remove and add partition ID +For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) +MUST be created. +For a [Single Key Store](#single-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +equal to 1. +For a [Multi Key Store](#multi-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +equal to 1000. -The [max cache size](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#entry-capacity) -that the [Key Store Cache](#key-store-cache) will be configured to. +The Key Store Cache MUST be shared across different [Beacon Key Sources](#beacon-key-source) if and only if a `Shared` cache is used. +In all other cases, the Key Store Cache MUST be bound to the [Beacon Key Source](#beacon-key-source). +In either case, Cache Identifiers MUST be unique across all key sources. +Cache Identifiers for Searchable Encryption MUST be set as per the section [Searchable Encryption Cache Identifier](#searchable-encryption-cache-identifier). -### Key Store Cache +### Partition ID -TODO: Update +An optional string that uniquely identifies the respective [Beacon Key Source](#beacon-key-source) +and is used to avoid collisions with other [Beacon Key Sources](#beacon-key-source). -For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) -MUST be created. -For a [Single Key Store](#single-key-store-initialization) the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) -MUST be 1 -For a [Multi Key Store](#multi-key-store-initialization) the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) -MUST be key store's max cache size. +PartitionId can be a string provided by the user. If provided, it MUST be interpreted as UTF8 bytes. +If the PartitionId is NOT provided by the user, it MUST be set to the 16 byte representation of a v4 UUID. + +The Partition ID MUST NOT be changed after initialization. -The Key Store Cache MUST be bound to the Beacon Key Source. -This is currently invariant because we construct the cache for each search config -It is easy for beacon key ids to be unique within a single key source, -this may not be true across all key sources. +Please see [Shared Cache Considerations](#shared-cache-considerations) on how to provide the +Partition ID and Logical Key Store Name while providing a Shared Cache to the [Beacon Key Source](#beacon-key-source). ## Beacon Keys @@ -328,6 +336,14 @@ These materials MUST be put into the associated [Key Store Cache](#key-store-cac with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) equal to now + configured [cacheTTL](#cachettl). +If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), +different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. +In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. +There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, +`time.now() - cacheEntryCreationTime <= ttlSeconds` is true and +valid for TTL of the [Beacon Key Source](#beacon-key-source) getting the cache entry. +If this is NOT true, then we MUST treat the cache entry as expired. + These cached materials MUST be returned. ### HMAC Key Generation @@ -340,3 +356,160 @@ using the beacon key retrieved above as the initial key material with no salt. The `info` MUST be the concatenation of "AWS_DBE_SCAN_BEACON" encoded as UTF8 and the beacon name. The `expectedLength` MUST be 64 bytes. + +## Searchable Encryption Cache Identifier + +When accessing the underlying cryptographic materials cache, +Searchable Encryption MUST use the formulas specified in this section +in order to compute the [cache entry identifier](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-identifier). + +### Preliminaries + +Each of the cache entry identifier formulas includes serialized information related to the branch key, +as defined in the [Key Provider Info](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#key-provider-information). + +We establish the following definitions for the Cache Entry Identifier formula: + +#### Resource Identifier + +A Hex value that indicates if an element is from a Caching_CMM, Hierarchical_Keyring, or some other future resource. + +``` +Caching_CMM : 0x01 (0001) +Hierarchical_Keyring : 0x02 (0010) +``` + +#### Scope Identifier + +A Hex value that indicates if an element is used for Encryption, Decryption, Searchable Encryption, or some other future purpose. + +``` +Encrypt : 0x01 (0001) +Decrypt : 0x02 (0010) +Searchable Encryption : 0x03 (0011) +``` + +#### Partition ID + +Partition ID is an optional parameter provided to the [Beacon Key Source](#beacon-key-source) input, which distinguishes +Cryptographic Material Providers (i.e: [Beacon Key Sources](#beacon-key-source)) writing to a cache. +It can either be a String provided by the user, which MUST be interpreted as the bytes of +UTF-8 Encoding of the String, or a v4 UUID, which SHOULD be interpreted as the 16 byte representation of the UUID. + +Note: The cache will not know if the Partition ID is a String set by the user or the UUID. +The constructor of the [Beacon Key Source](#beacon-key-source) MUST record these bytes at construction time. + +Please see [Shared Cache Considerations](#shared-cache-considerations) on how to provide the +Partition ID and Logical Key Store Name of the [beacon versions](#beacon-version-initialization) +while providing a Shared Cache to the [Beacon Key Source](#beacon-key-source). + +#### Resource Suffix + +The resource suffixes for the Searchable Encryption is as follows: + + ``` + logicalKeyStoreName + NULL_BYTE + UTF8Encode(branchKeyId) + ``` + +The aforementioned 4 definitions ([Resource Identifier](#resource-identifier), +[Scope Identifier](#scope-identifier), [Partition ID](#partition-id-1), and +[Resource Suffix](#resource-suffix)) MUST be appended together with the null byte, 0x00, +and the SHA384 of the result should be taken as the final cache identifier. + +When the [Beacon Key Source](#beacon-key-source) receives a `getKeyMap` request, +the cache entry identifier MUST be calculated as the +SHA-384 hash of the following byte strings, in the order listed: + +- MUST be the Resource ID for the Hierarchical Keyring (0x02) +- MUST be the Scope ID for Searchable Encryption (0x03) +- MUST be the Partition ID for the [Beacon Key Source](#beacon-key-source) +- Resource Suffix + - MUST be the UTF8 encoded Logical Key Store Name of the keystore for the [beacon versions](#beacon-version-initialization) + - MUST be the UTF8 encoded branch-key-id + +All the above fields must be separated by a single NULL_BYTE `0x00`. + +| Field | Length (bytes) | Interpreted as | +| ---------------------- | -------------- | ------------------- | +| Resource ID | 1 | bytes | +| Null Byte | 1 | `0x00` | +| Scope ID | 1 | bytes | +| Null Byte | 1 | `0x00` | +| Partition ID | Variable | bytes | +| Null Byte | 1 | `0x00` | +| Logical Key Store Name | Variable | UTF-8 Encoded Bytes | +| Null Byte | 1 | `0x00` | +| Branch Key ID | Variable | UTF-8 Encoded Bytes | + +As a formula: + +``` +resource-id = [0x02] +scope-id = [0x03] +logical-key-store-name = UTF8Encode(beaconVersion.keystore.LogicalKeyStoreName) +branch-key-id = UTF8Encode(BranchKeyIdentifier) +NULL_BYTE = [0x00] + +ENTRY_ID = SHA384( + resource-id + + NULL_BYTE + + scope-id + + NULL_BYTE + + partition-id + + NULL_BYTE + + logical-key-store-name + + NULL_BYTE + + branch-key-id +) +``` + +## Shared Cache Considerations + +If a user has two or more [beacon versions](#beacon-version-initialization) with: + +- Same Partition ID +- Same Logical Key Store Name of the Key Store +- Same Branch Key ID + +then they WILL share the cache entries in the `Shared` Cache. + +Any keyring that has access to the `Shared` cache MAY be able to use materials +that it MAY or MAY NOT have direct access to. + +Users MUST make sure that all of Partition ID, Logical Key Store Name of the Key Store +and Branch Key ID are set to be the same for two [beacon versions](#beacon-version-initialization) if and only they want the keyrings to share +cache entries. + +Therefore, there are two important parameters that users need to carefully set while providing the shared cache: + +### Partition ID + +Partition ID is an optional parameter provided to the [Beacon Key Source](#beacon-key-source) input, +which distinguishes Cryptographic Material Providers (i.e: [Beacon Key Sources](#beacon-key-source)) writing to a cache. + +- (Default) A a random 16-byte UUID, which makes + it unique for every [Beacon Key Source](#beacon-key-source). In this case, two [Beacon Key Sources](#beacon-key-source) (or another Material Provider) + CANNOT share the same cache entries in the cache. +- If the Partition ID is set by the user and is the same for two [Beacon Key Sources](#beacon-key-source) (or another Material Provider), + they CAN share the same cache entries in the cache. +- If the Partition ID is set by the user and is different for two [Beacon Key Sources](#beacon-key-source) (or another Material Provider), + they CANNOT share the same cache entries in the cache. + +### Logical Key Store Name + +> Note: Users MUST NEVER have two different physical Key Stores with the same Logical Key Store Name. + +Logical Key Store Name is set by the user when configuring the Key Store for +the [beacon versions](#beacon-version-initialization). This is a logical name for the key store. +Logical Key Store Name MUST be converted to UTF8 Bytes to be used in +the cache identifiers. + +Suppose there's a physical Key Store on DynamoDB (K). Two Key Store clients of K (K1 and K2) are created. +Now, two [beacon versions](#beacon-version-initialization) (BV1 and BV2) are created with these Key Store clients (K1 and K2 respectively). + +- If we want to share cache entries across these two BeaconVersions BV1 and BV2, the Logical Key Store Names + for both the Key Store clients (K1 and K2) should be set to be the same. +- If we set the Logical Key Store Names for K1 and K2 to be different, BV1 (which uses Key Store client K1) + and BV2 (which uses Key Store client K2) will NOT be able to share cache entries. + +Notice that both K1 and K2 are clients for the same physical Key Store (K). From ca446e35ae4c457103db215f5919e344d204d503 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Mon, 13 Jan 2025 17:17:40 -0800 Subject: [PATCH 22/29] fix --- .../dafny/DynamoDbEncryption/src/ConfigToInfo.dfy | 10 ++++++---- .../dafny/DynamoDbEncryption/src/SearchInfo.dfy | 8 ++++---- specification/searchable-encryption/search-config.md | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index ce29bf1b1..2b58b09cf 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -146,10 +146,12 @@ module SearchConfigToInfo { var mpl :- mplR.MapFailure(e => AwsCryptographyMaterialProviders(e)); //= specification/searchable-encryption/search-config.md#key-store-cache - //# For a [Single Key Store](#single-key-store-initialization) the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) - //# MUST be 1 - //# For a [Multi Key Store](#multi-key-store-initialization) the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) - //# MUST be key store's max cache size. + //# For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) + //# MUST be created. + //# For a [Single Key Store](#single-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# equal to 1. + //# For a [Multi Key Store](#multi-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# equal to 1000. var cacheType : MPT.CacheType := if config.multi? then if config.multi.cache.Some? then diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 6c4d293cd..8e70e0e34 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -330,12 +330,12 @@ module SearchableEncryptionInfo { } //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# If using a `Shared` cache across multiple Beacon Key Sources, - //# different Beacon Key Sources having the same `branchKey` can have different TTLs. - //# In such a case, the expiry time in the cache is set according to the Beacon Key Source that populated the cache. + //# If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), + //# different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. + //# In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. //# There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, //# `time.now() - cacheEntryCreationTime <= ttlSeconds` is true and - //# valid for TTL of the Beacon Key Source getting the cache entry. + //# valid for TTL of the [Beacon Key Source](#beacon-key-source) getting the cache entry. //# If this is NOT true, then we MUST treat the cache entry as expired. if getCacheOutput.Failure? || !cacheEntryWithinLimits( creationTime := getCacheOutput.value.creationTime, diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index 1e298b4dd..ee90c49aa 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -407,9 +407,9 @@ while providing a Shared Cache to the [Beacon Key Source](#beacon-key-source). The resource suffixes for the Searchable Encryption is as follows: - ``` - logicalKeyStoreName + NULL_BYTE + UTF8Encode(branchKeyId) - ``` +``` +logicalKeyStoreName + NULL_BYTE + UTF8Encode(branchKeyId) +``` The aforementioned 4 definitions ([Resource Identifier](#resource-identifier), [Scope Identifier](#scope-identifier), [Partition ID](#partition-id-1), and From 155a74c25fdf391bc3e9f3018a065f841042b827 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 16 Jan 2025 17:36:51 -0800 Subject: [PATCH 23/29] address comments --- .../Model/DynamoDbEncryption.smithy | 2 +- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 24 +++-- .../DynamoDbEncryption/src/SearchInfo.dfy | 94 ++++++++++++------- .../dynamodb/model/SingleKeyStore.java | 8 +- .../searchable-encryption/search-config.md | 19 ++-- 5 files changed, 92 insertions(+), 55 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy index 0750ae637..87716dcc5 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/Model/DynamoDbEncryption.smithy @@ -719,7 +719,7 @@ structure SingleKeyStore { @required @javadoc("How long (in seconds) the beacon key material is cached locally before it is re-retrieved from DynamoDB and re-authed with AWS KMS.") cacheTTL: Integer, - @documentation("Provide the Shared Cache for Searchable Encryption.") + @documentation("Which type of local cache to use. Please see the [spec](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/specification/searchable-encryption/search-config.md#key-store-cache) on how to provide a cache for a SingleKeyStore.") cache : CacheType, @documentation("Partition ID to distinguish Beacon Key Sources writing to a Shared cache. If the Partition ID is the same for two Beacon Key Sources, they can share the same cache entries in the Shared cache.") partitionId: String diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 2b58b09cf..4ec60dbf5 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -40,6 +40,7 @@ module SearchConfigToInfo { requires ValidSearchConfig(outer.search) requires outer.search.Some? ==> ValidSharedCache(outer.search.value.versions[0].keySource) modifies if outer.search.Some? then outer.search.value.versions[0].keyStore.Modifies else {} + ensures outer.search.Some? ==> ValidSharedCache(outer.search.value.versions[0].keySource) ensures output.Success? && output.value.Some? ==> && output.value.value.ValidState() && fresh(output.value.value.versions[0].keySource.client) @@ -120,6 +121,7 @@ module SearchConfigToInfo { modifies keyStore.Modifies requires client.ValidState() requires ValidSharedCache(config) + ensures ValidSharedCache(config) ensures client.ValidState() ensures output.Success? ==> && output.value.ValidState() @@ -148,9 +150,10 @@ module SearchConfigToInfo { //= specification/searchable-encryption/search-config.md#key-store-cache //# For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) //# MUST be created. - //# For a [Single Key Store](#single-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) - //# equal to 1. - //# For a [Multi Key Store](#multi-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# equal to 1. If the user provides a cache which is not `Shared`, they SHOULD set the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# of the provided `CacheType` to 1, because the [Single Key Store](#single-key-store-initialization) only ever caches one entry. Even if the user provides an entryCapacity > 1, the [Single Key Store](#single-key-store-initialization) will only cache one entry. + //# For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) //# equal to 1000. var cacheType : MPT.CacheType := if config.multi? then @@ -160,8 +163,9 @@ module SearchConfigToInfo { MPT.Default(Default := MPT.DefaultCache(entryCapacity := 1000)) else if config.single.cache.Some? then - // Ideally, we only want to pass a cache here with entryCapacity = 1 - // because the SingleKeyStore caches only one value. + // If the user provides a CacheType, and it is NOT Shared, + // we SHOULD only allow an entryCapacity = 1 + // because the SingleKeyStore only ever caches one value. // That is, we SHOULD add a check here for entryCapacity = 1. // However, that requires us to write an if block for each CacheType. // Also, it does NOT matter what the entryCapacity is, because the cache @@ -174,10 +178,6 @@ module SearchConfigToInfo { if cacheType.Shared? { cache := cacheType.Shared; reveal ValidSharedCache(config); - - // This axiom is important because it is not easy to prove - // keyStore.Modifies !! cache.Modifies for a shared cache. - assume {:axiom} keyStore.Modifies !! cache.Modifies; } else { //= specification/searchable-encryption/search-config.md#key-store-cache //# For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) @@ -228,6 +228,11 @@ module SearchConfigToInfo { :- Need(0 < config.single.cacheTTL, E("Beacon Cache TTL must be at least 1.")); output := Success(I.KeySource(client, keyStore, I.SingleLoc(config.single.keyId), cache, config.single.cacheTTL as uint32, partitionIdBytes, logicalKeyStoreNameBytes)); } + assert output.value.ValidState() by { + // This axiom is important because it is not easy to prove + // keyStore.Modifies !! cache.Modifies for a shared cache. + assume {:axiom} keyStore.Modifies !! cache.Modifies; + } } // convert configured BeaconVersion to internal BeaconVersion @@ -236,6 +241,7 @@ module SearchConfigToInfo { requires ValidBeaconVersion(config) requires ValidSharedCache(config.keySource) modifies config.keyStore.Modifies + ensures ValidSharedCache(config.keySource) ensures output.Success? ==> && output.value.ValidState() && fresh(output.value.keySource.client) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 8e70e0e34..aedc2dbe3 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -138,10 +138,8 @@ module SearchableEncryptionInfo { var uuid :- uuid? .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); - var uuidBytes: seq :- UUID.ToByteArray(uuid) - .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); - - output := Success(uuidBytes); + output := UUID.ToByteArray(uuid) + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); } datatype KeyLocation = @@ -176,7 +174,15 @@ module SearchableEncryptionInfo { if keyLoc.SingleLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a SingleKeyStore")); var now := Time.GetCurrent(); - var theMap :- getKeysCache(client, stdNames, keyLoc.keyId, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); + var theMap :- getKeysCache( + client, + stdNames, + keyLoc.keyId, + cacheTTL as MP.PositiveLong, + partitionIdBytes, + logicalKeyStoreNameBytes, + now as MP.PositiveLong + ); return Success(Keys(theMap)); } else if keyLoc.LiteralLoc? { :- Need(keyId.DontUseKeyId?, E("KeyID should not be supplied with a LiteralKeyStore")); @@ -186,7 +192,18 @@ module SearchableEncryptionInfo { match keyId { case DontUseKeyId => return Failure(E("KeyID must be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); - case KeyId(id) => var now := Time.GetCurrent(); var theMap :- getKeysCache(client, stdNames, id, cacheTTL as MP.PositiveLong, partitionIdBytes, logicalKeyStoreNameBytes, now as MP.PositiveLong); return Success(Keys(theMap)); + case KeyId(id) => + var now := Time.GetCurrent(); + var theMap :- getKeysCache( + client, + stdNames, + id, + cacheTTL as MP.PositiveLong, + partitionIdBytes, + logicalKeyStoreNameBytes, + now as MP.PositiveLong + ); + return Success(Keys(theMap)); } } } @@ -227,37 +244,40 @@ module SearchableEncryptionInfo { returns (output : Result) requires Seq.HasNoDuplicates(stdNames) requires ValidState() + requires client.ValidState() modifies Modifies() + modifies client.Modifies ensures ValidState() + ensures client.ValidState() //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# Get beacon key MUST Call the associated [Key Store Cache](#key-store-cache) //# [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) - //# with the `beacon key id`. + //# with the cache identifier defined in the [Searchable Encryption Cache Identifier](#searchable-encryption-cache-identifier) section. ensures output.Success? ==> - && var oldHistory := old(cache.History.GetCacheEntry); - && var newHistory := cache.History.GetCacheEntry; - && |newHistory| == |oldHistory|+1 - && var cacheInput := Seq.Last(newHistory).input; - && var cacheOutput := Seq.Last(newHistory).output; + && var oldGetCacheHistory := old(cache.History.GetCacheEntry); + && var newGetCacheHistory := cache.History.GetCacheEntry; + && |newGetCacheHistory| == |oldGetCacheHistory|+1 + && var getCacheInput := Seq.Last(newGetCacheHistory).input; + && var getCacheOutput := Seq.Last(newGetCacheHistory).output; && UTF8.Encode(keyId).Success? //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# If a [cache entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-entry) //# exists, get beacon key MUST return the [entry materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#materials). - && (cacheOutput.Success? && cacheEntryWithinLimits( - creationTime := cacheOutput.value.creationTime, + && (getCacheOutput.Success? && cacheEntryWithinLimits( + creationTime := getCacheOutput.value.creationTime, now := now, ttlSeconds := cacheTTL ) ==> - && cacheOutput.value.materials.BeaconKey? - && cacheOutput.value.materials.BeaconKey.hmacKeys.Some? - && output.value == cacheOutput.value.materials.BeaconKey.hmacKeys.value) + && getCacheOutput.value.materials.BeaconKey? + && getCacheOutput.value.materials.BeaconKey.hmacKeys.Some? + && output.value == getCacheOutput.value.materials.BeaconKey.hmacKeys.value) - && (cacheOutput.Failure? || !cacheEntryWithinLimits( - creationTime := cacheOutput.value.creationTime, + && (getCacheOutput.Failure? || !cacheEntryWithinLimits( + creationTime := getCacheOutput.value.creationTime, now := now, ttlSeconds := cacheTTL ) ==> @@ -274,20 +294,30 @@ module SearchableEncryptionInfo { //= type=implication //# The `beacon key id` MUST be passed to the configured `KeyStore`'s `GetBeaconKey` operation. && storeInput.branchKeyIdentifier == keyId - && var oldPutHistory := old(cache.History.PutCacheEntry); - && var newPutHistory := cache.History.PutCacheEntry; - && |newPutHistory| == |oldPutHistory|+1 + && var oldPutCacheHistory := old(cache.History.PutCacheEntry); + && var newPutCacheHistory := cache.History.PutCacheEntry; + && |newPutCacheHistory| == |oldPutCacheHistory|+1 && ( - var storeOutput := Seq.Last(newPutHistory).output; - || storeOutput.Success? - || storeOutput.error.EntryAlreadyExists? + var putCacheOutput := Seq.Last(newPutCacheHistory).output; + || putCacheOutput.Success? + || putCacheOutput.error.EntryAlreadyExists? ) - && var storeInput := Seq.Last(newPutHistory).input; - && var storeOutput := Seq.Last(newPutHistory).output; + && var putCacheInput := Seq.Last(newPutCacheHistory).input; + && var putCacheOutput := Seq.Last(newPutCacheHistory).output; + + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials + //# The Searchable Encryption cache identifier for [Key Store Cache](#key-store-cache) + //# [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) + //# and the [Key Store Cache](#key-store-cache) + //# [Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) + //# MUST be the same. + && putCacheInput.identifier == getCacheInput.identifier + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# These cached materials MUST be returned. - && storeInput.materials.BeaconKey? ==> storeInput.materials.BeaconKey.hmacKeys == Some(output.value) + && putCacheInput.materials.BeaconKey? + && putCacheInput.materials.BeaconKey.hmacKeys == Some(output.value) ) { @@ -311,15 +341,9 @@ module SearchableEncryptionInfo { var identifierDigestInput := Prim.DigestInput( digestAlgorithm := hashAlgorithm, message := identifier ); - var maybeCacheDigest := Digest.Digest(identifierDigestInput); + var maybeCacheDigest := client.Digest(identifierDigestInput); var cacheDigest :- maybeCacheDigest.MapFailure(e => AwsCryptographyPrimitives(e)); - :- Need( - |cacheDigest| == Digest.Length(hashAlgorithm), - Error.DynamoDbEncryptionException( - message := "Digest generated a message not equal to the expected length.") - ); - // Use the SHA384 of the identifier as the cache identifier var getCacheInput := MP.GetCacheEntryInput(identifier := cacheDigest, bytesUsed := None); verifyValidStateCache(cache); diff --git a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java index 4627c3f5f..0c16361bf 100644 --- a/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java +++ b/DynamoDbEncryption/runtimes/java/src/main/smithy-generated/software/amazon/cryptography/dbencryptionsdk/dynamodb/model/SingleKeyStore.java @@ -22,7 +22,7 @@ public class SingleKeyStore { private final Integer cacheTTL; /** - * Provide the Shared Cache for Searchable Encryption. + * Which type of local cache to use. Please see the [spec](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/specification/searchable-encryption/search-config.md#key-store-cache) on how to provide a cache for a SingleKeyStore. */ private final CacheType cache; @@ -53,7 +53,7 @@ public Integer cacheTTL() { } /** - * @return Provide the Shared Cache for Searchable Encryption. + * @return Which type of local cache to use. Please see the [spec](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/specification/searchable-encryption/search-config.md#key-store-cache) on how to provide a cache for a SingleKeyStore. */ public CacheType cache() { return this.cache; @@ -96,12 +96,12 @@ public interface Builder { Integer cacheTTL(); /** - * @param cache Provide the Shared Cache for Searchable Encryption. + * @param cache Which type of local cache to use. Please see the [spec](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/specification/searchable-encryption/search-config.md#key-store-cache) on how to provide a cache for a SingleKeyStore. */ Builder cache(CacheType cache); /** - * @return Provide the Shared Cache for Searchable Encryption. + * @return Which type of local cache to use. Please see the [spec](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/specification/searchable-encryption/search-config.md#key-store-cache) on how to provide a cache for a SingleKeyStore. */ CacheType cache(); diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index ee90c49aa..c58ef5be2 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -220,9 +220,10 @@ for how long a beacon key should exist locally before reauthorization. For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) MUST be created. -For a [Single Key Store](#single-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) -equal to 1. -For a [Multi Key Store](#multi-key-store-initialization), either the customer provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +equal to 1. If the user provides a cache which is not `Shared`, they SHOULD set the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +of the provided `CacheType` to 1, because the [Single Key Store](#single-key-store-initialization) only ever caches one entry. Even if the user provides an entryCapacity > 1, the [Single Key Store](#single-key-store-initialization) will only cache one entry. +For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) equal to 1000. The Key Store Cache MUST be shared across different [Beacon Key Sources](#beacon-key-source) if and only if a `Shared` cache is used. @@ -312,11 +313,11 @@ and the result returned for Get beacon key for query. ### Get Beacon Key Materials -Takes a `beacon key id`, [Key Store Cache](#key-store-cache), and a `KeyStore` +Takes a `beacon key id`, [Key Store Cache](#key-store-cache), and a `KeyStore`. Get beacon key MUST Call the associated [Key Store Cache](#key-store-cache) [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) -with the `beacon key id`. +with the cache identifier defined in the [Searchable Encryption Cache Identifier](#searchable-encryption-cache-identifier) section. If a [cache entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-entry) exists, get beacon key MUST return the [entry materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#materials). @@ -324,7 +325,7 @@ exists, get beacon key MUST return the [entry materials](../../submodules/Materi The `beacon key id` MUST be passed to the configured `KeyStore`'s `GetBeaconKey` operation. If `GetBeaconKey` fails get beacon key MUST fail. -For every [standard beacons](beacons.md#standard-beacon-initialization) an HMAC key +For every [standard beacons](beacons.md#standard-beacon-initialization), an HMAC key MUST be generated in accordance with [HMAC Key Generation](#hmac-key-generation). [Beacon Key Materials](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/structures.md#beacon-key-materials) MUST be generated @@ -336,6 +337,12 @@ These materials MUST be put into the associated [Key Store Cache](#key-store-cac with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) equal to now + configured [cacheTTL](#cachettl). +The Searchable Encryption cache identifier for [Key Store Cache](#key-store-cache) +[Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) +and the [Key Store Cache](#key-store-cache) +[Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) +MUST be the same. + If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. From 10555e1ca6431b59af5a86f0e85ea5e5271620be Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 16 Jan 2025 17:43:21 -0800 Subject: [PATCH 24/29] format --- .../dafny/DynamoDbEncryption/src/SearchInfo.dfy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index aedc2dbe3..b7379fc24 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -139,7 +139,7 @@ module SearchableEncryptionInfo { .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); output := UUID.ToByteArray(uuid) - .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); + .MapFailure(e => Error.DynamoDbEncryptionException(message := e)); } datatype KeyLocation = @@ -341,7 +341,7 @@ module SearchableEncryptionInfo { var identifierDigestInput := Prim.DigestInput( digestAlgorithm := hashAlgorithm, message := identifier ); - var maybeCacheDigest := client.Digest(identifierDigestInput); + var maybeCacheDigest := client.Digest(identifierDigestInput); var cacheDigest :- maybeCacheDigest.MapFailure(e => AwsCryptographyPrimitives(e)); // Use the SHA384 of the identifier as the cache identifier From 20e0259858bded425b46afa7f5ff3d735d6ba134 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 17 Jan 2025 09:55:43 -0800 Subject: [PATCH 25/29] use ubuntu-22.04 --- .github/workflows/ci_test_vector_java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_test_vector_java.yml b/.github/workflows/ci_test_vector_java.yml index 08cb684b9..5c6fee636 100644 --- a/.github/workflows/ci_test_vector_java.yml +++ b/.github/workflows/ci_test_vector_java.yml @@ -31,7 +31,7 @@ jobs: java-version: [8, 11, 16, 17] os: [ # Run on ubuntu image that comes pre-configured with docker - ubuntu-latest, + ubuntu-22.04, ] runs-on: ${{ matrix.os }} permissions: From 1868de5ed6cdfa4a5bcd1b5e5db35c74e477d283 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Fri, 17 Jan 2025 13:42:56 -0800 Subject: [PATCH 26/29] resolve comments --- DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index b7379fc24..dd9a7f54e 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -190,7 +190,7 @@ module SearchableEncryptionInfo { return Success(Keys(theMap)); } else { match keyId { - case DontUseKeyId => return Failure(E("KeyID must be supplied with a MultiKeyStore")); + case DontUseKeyId => return Failure(E("Fixed KeyID must not be supplied with a MultiKeyStore")); case ShouldHaveKeyId => return Success(ShouldHaveKeys); case KeyId(id) => var now := Time.GetCurrent(); From 5e4a1150469c8420cbc6fa5ee6d8bed96e0c948e Mon Sep 17 00:00:00 2001 From: Ritvik Kapila <61410899+RitvikKapila@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:22:38 -0800 Subject: [PATCH 27/29] fix(verification): Verify cache identifier (#1578) --- .../DynamoDbEncryption/src/SearchInfo.dfy | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index dd9a7f54e..30b423a15 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -43,6 +43,7 @@ module SearchableEncryptionInfo { requires Seq.HasNoDuplicates(stdNames) modifies client.Modifies requires client.ValidState() + ensures client.History.Digest == old(client.History.Digest) ensures client.ValidState() { var newKeys :- GetHmacKeys(client, stdNames, stdNames, key); @@ -61,6 +62,7 @@ module SearchableEncryptionInfo { requires Seq.HasNoDuplicates(keysLeft) requires forall k <- allKeys :: k in keysLeft || k in acc requires forall k <- keysLeft :: k in allKeys + ensures client.History.Digest == old(client.History.Digest) ensures output.Success? ==> forall k <- allKeys :: k in output.value modifies client.Modifies requires client.ValidState() @@ -89,6 +91,7 @@ module SearchableEncryptionInfo { modifies client.Modifies requires client.ValidState() ensures client.ValidState() + ensures client.History.Digest == old(client.History.Digest) ensures output.Success? ==> && var fullName := "AWS_DBE_SCAN_BEACON" + name; @@ -263,6 +266,15 @@ module SearchableEncryptionInfo { && var getCacheOutput := Seq.Last(newGetCacheHistory).output; && UTF8.Encode(keyId).Success? + && var oldClientHistory := old(client.History.Digest); + && var newClientHistory := client.History.Digest; + && |newClientHistory| == |oldClientHistory|+1 + && var identifier := RESOURCE_ID_HIERARCHICAL_KEYRING + NULL_BYTE + SCOPE_ID_SEARCHABLE_ENCRYPTION + NULL_BYTE + partitionIdBytes + NULL_BYTE + logicalKeyStoreNameBytes + NULL_BYTE + UTF8.Encode(keyId).value; + && var digestInput := Seq.Last(newClientHistory).input; + && var digestOutput := Seq.Last(newClientHistory).output; + && digestInput.message == identifier + && (digestOutput.Success? ==> (getCacheInput.identifier == digestOutput.value)) + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# If a [cache entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-entry) @@ -316,8 +328,7 @@ module SearchableEncryptionInfo { //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //= type=implication //# These cached materials MUST be returned. - && putCacheInput.materials.BeaconKey? - && putCacheInput.materials.BeaconKey.hmacKeys == Some(output.value) + && (putCacheInput.materials.BeaconKey? ==> putCacheInput.materials.BeaconKey.hmacKeys == Some(output.value)) ) { @@ -353,6 +364,14 @@ module SearchableEncryptionInfo { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=getCacheOutput.error)); } + assert + && |client.History.Digest| == |old(client.History.Digest)| + 1 + && Seq.Last(client.History.Digest).input.message == identifier + && Seq.Last(cache.History.GetCacheEntry).input.identifier == Seq.Last(client.History.Digest).output.value + by { + assume{:axiom} client.Modifies !! cache.Modifies; + } + //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //# If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), //# different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. @@ -373,6 +392,13 @@ module SearchableEncryptionInfo { branchKeyIdentifier := keyId ) ); + assert + && |client.History.Digest| == |old(client.History.Digest)| + 1 + && Seq.Last(client.History.Digest).input.message == identifier + && Seq.Last(cache.History.GetCacheEntry).input.identifier == Seq.Last(client.History.Digest).output.value + by { + assume{:axiom} client.Modifies !! store.Modifies; + } var rawBeaconKeyMaterials :- maybeRawBeaconKeyMaterials .MapFailure(e => AwsCryptographyKeyStore(AwsCryptographyKeyStore := e)); @@ -401,6 +427,13 @@ module SearchableEncryptionInfo { if (putResult.Failure? && !putResult.error.EntryAlreadyExists?) { return Failure(AwsCryptographyMaterialProviders(AwsCryptographyMaterialProviders:=putResult.error)); } + assert + && |client.History.Digest| == |old(client.History.Digest)| + 1 + && Seq.Last(client.History.Digest).input.message == identifier + && Seq.Last(cache.History.GetCacheEntry).input.identifier == Seq.Last(client.History.Digest).output.value + by { + assume{:axiom} client.Modifies !! cache.Modifies; + } return Success(keyMap); } else { :- Need( @@ -418,6 +451,7 @@ module SearchableEncryptionInfo { requires Seq.HasNoDuplicates(stdNames) modifies client.Modifies requires client.ValidState() + ensures client.History.Digest == old(client.History.Digest) ensures client.ValidState() { output := GetAllKeys(client, stdNames, key); From 7d3d21186106f1e2a55e2564d2f43b0a4bb3eb54 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Wed, 22 Jan 2025 16:51:33 -0800 Subject: [PATCH 28/29] fix spec --- .../DynamoDbEncryption/src/ConfigToInfo.dfy | 7 +- .../DynamoDbEncryption/src/SearchInfo.dfy | 11 ++-- .../searchable-encryption/search-config.md | 64 ++++++++++--------- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy index 4ec60dbf5..6e974d03c 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy @@ -150,10 +150,9 @@ module SearchConfigToInfo { //= specification/searchable-encryption/search-config.md#key-store-cache //# For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) //# MUST be created. - //# For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) - //# equal to 1. If the user provides a cache which is not `Shared`, they SHOULD set the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) - //# of the provided `CacheType` to 1, because the [Single Key Store](#single-key-store-initialization) only ever caches one entry. Even if the user provides an entryCapacity > 1, the [Single Key Store](#single-key-store-initialization) will only cache one entry. - //# For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that has [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) + //# equal to 1. + //# For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that has [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) //# equal to 1000. var cacheType : MPT.CacheType := if config.multi? then diff --git a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy index 30b423a15..bedbe19c7 100644 --- a/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy +++ b/DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy @@ -318,11 +318,10 @@ module SearchableEncryptionInfo { && var putCacheOutput := Seq.Last(newPutCacheHistory).output; //= specification/searchable-encryption/search-config.md#get-beacon-key-materials - //# The Searchable Encryption cache identifier for [Key Store Cache](#key-store-cache) - //# [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) - //# and the [Key Store Cache](#key-store-cache) - //# [Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) - //# MUST be the same. + //# The Searchable Encryption cache identifier + //# used to [Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) + //# MUST be the same + //# as the identifier that was used to attempt [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry). && putCacheInput.identifier == getCacheInput.identifier //= specification/searchable-encryption/search-config.md#get-beacon-key-materials @@ -374,7 +373,7 @@ module SearchableEncryptionInfo { //= specification/searchable-encryption/search-config.md#get-beacon-key-materials //# If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), - //# different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. + //# different [Beacon Key Sources](#beacon-key-source) having the same `beaconKey` can have different TTLs. //# In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. //# There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, //# `time.now() - cacheEntryCreationTime <= ttlSeconds` is true and diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index c58ef5be2..25cab61a4 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -220,14 +220,13 @@ for how long a beacon key should exist locally before reauthorization. For a Beacon Key Source a [CMC](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md) MUST be created. -For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) -equal to 1. If the user provides a cache which is not `Shared`, they SHOULD set the [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) -of the provided `CacheType` to 1, because the [Single Key Store](#single-key-store-initialization) only ever caches one entry. Even if the user provides an entryCapacity > 1, the [Single Key Store](#single-key-store-initialization) will only cache one entry. -For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that MUST have [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +For a [Single Key Store](#single-key-store-initialization), either the user provides a cache, or we create a cache that has [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) +equal to 1. +For a [Multi Key Store](#multi-key-store-initialization), either the user provides a cache, or we create a cache that has [Entry Capacity](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#entry-capacity) equal to 1000. -The Key Store Cache MUST be shared across different [Beacon Key Sources](#beacon-key-source) if and only if a `Shared` cache is used. -In all other cases, the Key Store Cache MUST be bound to the [Beacon Key Source](#beacon-key-source). +The Key Store Cache is shared across different [Beacon Key Sources](#beacon-key-source) if and only if a `Shared` cache is used. +In all other cases, the Key Store Cache is bound to the [Beacon Key Source](#beacon-key-source). In either case, Cache Identifiers MUST be unique across all key sources. Cache Identifiers for Searchable Encryption MUST be set as per the section [Searchable Encryption Cache Identifier](#searchable-encryption-cache-identifier). @@ -325,6 +324,14 @@ exists, get beacon key MUST return the [entry materials](../../submodules/Materi The `beacon key id` MUST be passed to the configured `KeyStore`'s `GetBeaconKey` operation. If `GetBeaconKey` fails get beacon key MUST fail. +If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), +different [Beacon Key Sources](#beacon-key-source) having the same `beaconKey` can have different TTLs. +In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. +There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, +`time.now() - cacheEntryCreationTime <= ttlSeconds` is true and +valid for TTL of the [Beacon Key Source](#beacon-key-source) getting the cache entry. +If this is NOT true, then we MUST treat the cache entry as expired. + For every [standard beacons](beacons.md#standard-beacon-initialization), an HMAC key MUST be generated in accordance with [HMAC Key Generation](#hmac-key-generation). @@ -337,19 +344,10 @@ These materials MUST be put into the associated [Key Store Cache](#key-store-cac with an [Expiry Time](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#expiry-time) equal to now + configured [cacheTTL](#cachettl). -The Searchable Encryption cache identifier for [Key Store Cache](#key-store-cache) -[Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry) -and the [Key Store Cache](#key-store-cache) -[Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) -MUST be the same. - -If using a `Shared` cache across multiple [Beacon Key Sources](#beacon-key-source), -different [Beacon Key Sources](#beacon-key-source) having the same `branchKey` can have different TTLs. -In such a case, the expiry time in the cache is set according to the [Beacon Key Source](#beacon-key-source) that populated the cache. -There MUST be a check (cacheEntryWithinLimits) to make sure that for the cache entry found, who's TTL has NOT expired, -`time.now() - cacheEntryCreationTime <= ttlSeconds` is true and -valid for TTL of the [Beacon Key Source](#beacon-key-source) getting the cache entry. -If this is NOT true, then we MUST treat the cache entry as expired. +The Searchable Encryption cache identifier +used to [Put Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#put-cache-entry) +MUST be the same +as the identifier that was used to attempt [Get Cache Entry](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/local-cryptographic-materials-cache.md#get-cache-entry). These cached materials MUST be returned. @@ -366,6 +364,10 @@ The `expectedLength` MUST be 64 bytes. ## Searchable Encryption Cache Identifier +This section is consistent with the Cache Entry Identifier formulas for the [Hierarchical Keyring](https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-hierarchical-keyring.md#appendix-a-cache-entry-identifier-formulas) +and [Caching CMM](https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/caching-cmm.md#appendix-a-cache-entry-identifier-formulas) in the Material Providers spec. These cache identifier formulas should +not be changed independently. + When accessing the underlying cryptographic materials cache, Searchable Encryption MUST use the formulas specified in this section in order to compute the [cache entry identifier](../../submodules/MaterialProviders/aws-encryption-sdk-specification/framework/cryptographic-materials-cache.md#cache-identifier). @@ -480,12 +482,12 @@ If a user has two or more [beacon versions](#beacon-version-initialization) with then they WILL share the cache entries in the `Shared` Cache. -Any keyring that has access to the `Shared` cache MAY be able to use materials -that it MAY or MAY NOT have direct access to. +Any keyring that has access to the `Shared` cache may be able to use materials +that it may or may not have direct access to. -Users MUST make sure that all of Partition ID, Logical Key Store Name of the Key Store -and Branch Key ID are set to be the same for two [beacon versions](#beacon-version-initialization) if and only they want the keyrings to share -cache entries. +Users should make sure that all of Partition ID, Logical Key Store Name of the Key Store +and Branch Key ID are set to be the same for two [beacon versions](#beacon-version-initialization) +if and only they want the keyrings to share cache entries. Therefore, there are two important parameters that users need to carefully set while providing the shared cache: @@ -495,19 +497,21 @@ Partition ID is an optional parameter provided to the [Beacon Key Source](#beaco which distinguishes Cryptographic Material Providers (i.e: [Beacon Key Sources](#beacon-key-source)) writing to a cache. - (Default) A a random 16-byte UUID, which makes - it unique for every [Beacon Key Source](#beacon-key-source). In this case, two [Beacon Key Sources](#beacon-key-source) (or another Material Provider) - CANNOT share the same cache entries in the cache. + it unique for every [Beacon Key Source](#beacon-key-source). + In this case, two [Beacon Key Sources](#beacon-key-source) (or another Material Provider) + WILL NOT share the same cache entries in the cache. - If the Partition ID is set by the user and is the same for two [Beacon Key Sources](#beacon-key-source) (or another Material Provider), - they CAN share the same cache entries in the cache. + they MAY share the same cache entries in the cache. - If the Partition ID is set by the user and is different for two [Beacon Key Sources](#beacon-key-source) (or another Material Provider), - they CANNOT share the same cache entries in the cache. + they WILL NOT share the same cache entries in the cache. ### Logical Key Store Name -> Note: Users MUST NEVER have two different physical Key Stores with the same Logical Key Store Name. +> Note: Users should not have two different physical Key Stores with the same Logical Key Store Name. Logical Key Store Name is set by the user when configuring the Key Store for -the [beacon versions](#beacon-version-initialization). This is a logical name for the key store. +the [beacon versions](#beacon-version-initialization). +This is a logical name for the key store. Logical Key Store Name MUST be converted to UTF8 Bytes to be used in the cache identifiers. From 48b33e5e56f8c70f55667dec8dc9373e0f86cee8 Mon Sep 17 00:00:00 2001 From: Ritvik Kapila Date: Thu, 23 Jan 2025 13:22:40 -0800 Subject: [PATCH 29/29] point resource and scope id to mpl spec --- .../searchable-encryption/search-config.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/specification/searchable-encryption/search-config.md b/specification/searchable-encryption/search-config.md index 25cab61a4..e12aefea4 100644 --- a/specification/searchable-encryption/search-config.md +++ b/specification/searchable-encryption/search-config.md @@ -381,22 +381,11 @@ We establish the following definitions for the Cache Entry Identifier formula: #### Resource Identifier -A Hex value that indicates if an element is from a Caching_CMM, Hierarchical_Keyring, or some other future resource. - -``` -Caching_CMM : 0x01 (0001) -Hierarchical_Keyring : 0x02 (0010) -``` +Resource Identifier is defined in the [Material Providers specification](https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-hierarchical-keyring.md#resource-identifier). #### Scope Identifier -A Hex value that indicates if an element is used for Encryption, Decryption, Searchable Encryption, or some other future purpose. - -``` -Encrypt : 0x01 (0001) -Decrypt : 0x02 (0010) -Searchable Encryption : 0x03 (0011) -``` +Scope Identifier is defined in the [Material Providers specification](https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/aws-kms/aws-kms-hierarchical-keyring.md#scope-identifier). #### Partition ID