Skip to content

Commit c64abc0

Browse files
auto commit
2 parents d0c7828 + ffb0c38 commit c64abc0

22 files changed

+1323
-45
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## [3.9.0](https://github.com/aws/aws-database-encryption-sdk-dynamodb/compare/v3.8.1...v3.9.0) (2025-06-25)
4+
5+
This release is available in the following languages:
6+
7+
- Java
8+
9+
### Maintenance
10+
11+
- **dafny:** Add ExecuteStatement test ([#1932](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1932)) ([66a19ab](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/66a19ab7d9ddea1018cbb01da053020148e4a1e7))
12+
- **dafny:** Add Update and delete test ([#1942](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1942)) ([3bd48ba](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/3bd48bacc6e9ca458931031062ee97f351f9de32))
13+
- **dafny:** change nat to uint64 in many places ([#1852](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1852)) ([ec22b7d](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/ec22b7d316678f4d836837e751bdfcf41f461441))
14+
- **dafny:** further performance enhancements ([#1834](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1834)) ([ea94693](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/ea9469327109a61e544425479dfb2c6be514ce5a))
15+
- **dafny:** improve performance ([#1900](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1900)) ([ccf61d6](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/ccf61d6a9c49385f1cccde0cda9c0609ba050455))
16+
- **dafny:** improve performance of searchable encryption ([#1931](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1931)) ([8b71004](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/8b710042e1bc0516b70a9f9674edab7f775d236f))
17+
- **dafny:** reduce use of BigInteger ([#1872](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1872)) ([eb7679a](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/eb7679afa9fb6383a76fd71f9e7a4e40bbe53c8a))
18+
- **dafny:** test ExecuteTransaction and BatchExecuteStatement ([#1941](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1941)) ([69c37c6](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/69c37c6a8491b2a5aadf74c4f80355b0593aed32))
19+
- **deps:** Bump MPL version to 1.11.0 ([#1945](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1945)) ([efdd373](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/efdd373ab91fc7465be0f0d05d8018f59131ee6f))
20+
- further performance improvements ([#1826](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1826)) ([3194054](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/3194054eea95c640d0ac469911e950fc76953dd6))
21+
- improve performance ([#1622](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1622)) ([8ca2883](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/8ca288345fe7371711c1c51e1d064538686e25f1))
22+
- update README for missing info ([#1939](https://github.com/aws/aws-database-encryption-sdk-dynamodb/issues/1939)) ([354f4f6](https://github.com/aws/aws-database-encryption-sdk-dynamodb/commit/354f4f6c48ed6c60914f5ce0feaba64de7ed2587))
23+
324
## [3.8.1](https://github.com/aws/aws-database-encryption-sdk-dynamodb/compare/v3.8.0...v3.8.1) (2025-04-01)
425

526
This release is available in the following languages:

DynamoDbEncryption/runtimes/java/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,8 @@ nexusPublishing {
250250
// https://github.com/gradle-nexus/publish-plugin/
251251
repositories {
252252
sonatype {
253-
nexusUrl.set(uri("https://aws.oss.sonatype.org/service/local/"))
254-
snapshotRepositoryUrl.set(uri("https://aws.oss.sonatype.org/content/repositories/snapshots/"))
253+
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
254+
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
255255
username.set(System.getenv("SONA_USERNAME"))
256256
password.set(System.getenv("SONA_PASSWORD"))
257257
}

DynamoDbEncryption/runtimes/net/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
[assembly: AssemblyTitle("AWS.Cryptography.DbEncryptionSDK.DynamoDb")]
44

55
// This should be kept in sync with the version number in MPL.csproj
6-
[assembly: AssemblyVersion("3.8.1")]
6+
[assembly: AssemblyVersion("3.9.0")]
77

DynamoDbEncryption/runtimes/net/DynamoDbEncryption.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
66
<IsPackable>true</IsPackable>
77

8-
<Version>3.8.1</Version>
8+
<Version>3.9.0</Version>
99

1010
<AssemblyName>AWS.Cryptography.DbEncryptionSDK.DynamoDb</AssemblyName>
1111
<PackageId>AWS.Cryptography.DbEncryptionSDK.DynamoDb</PackageId>
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package keyring
5+
6+
import (
7+
"context"
8+
"encoding/base64"
9+
"fmt"
10+
"os"
11+
"path/filepath"
12+
"reflect"
13+
"strings"
14+
15+
mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated"
16+
mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes"
17+
dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes"
18+
dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes"
19+
"github.com/aws/aws-database-encryption-sdk-dynamodb/dbesdkmiddleware"
20+
"github.com/aws/aws-database-encryption-sdk-dynamodb/examples/utils"
21+
22+
"github.com/aws/aws-sdk-go-v2/aws"
23+
"github.com/aws/aws-sdk-go-v2/config"
24+
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
25+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
26+
"github.com/aws/aws-sdk-go-v2/service/kms"
27+
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
28+
)
29+
30+
/*
31+
This example sets up DynamoDb Encryption for the AWS SDK client
32+
using the KMS RSA Keyring. This keyring uses a KMS RSA key pair to
33+
encrypt and decrypt records. The client uses the downloaded public key
34+
to encrypt items it adds to the table.
35+
The keyring uses the private key to decrypt existing table items it retrieves,
36+
by calling KMS' decrypt API.
37+
38+
Running this example requires access to the DDB Table whose name
39+
is provided in CLI arguments.
40+
This table must be configured with the following
41+
primary key configuration:
42+
- Partition key is named "partition_key" with type (S)
43+
- Sort key is named "sort_key" with type (S)
44+
This example also requires access to a KMS RSA key.
45+
Our tests provide a KMS RSA ARN that anyone can use, but you
46+
can also provide your own KMS RSA key.
47+
To use your own KMS RSA key, you must have either:
48+
- Its public key downloaded in a UTF-8 encoded PEM file
49+
- kms:GetPublicKey permissions on that key
50+
If you do not have the public key downloaded, running this example
51+
through its main method will download the public key for you
52+
by calling kms:GetPublicKey.
53+
You must also have kms:Decrypt permissions on the KMS RSA key.
54+
*/
55+
56+
func KmsRsaKeyringExample(ddbTableName, rsaKeyArn, rsaPublicKeyFilename string) {
57+
// You may provide your own RSA public key at rsaPublicKeyFilename.
58+
// This must be the public key for the RSA key represented at rsaKeyArn.
59+
// If this file is not present, this will write a UTF-8 encoded PEM file for you.
60+
if !utils.FileExists(rsaPublicKeyFilename) {
61+
writePublicKeyPemForRsaKey(rsaKeyArn, rsaPublicKeyFilename)
62+
}
63+
64+
// 1. Load UTF-8 encoded public key PEM file.
65+
// You may have an RSA public key file already defined.
66+
// If not, the main method in this class will call
67+
// the KMS RSA key, retrieve its public key, and store it
68+
// in a PEM file for example use.
69+
publicKeyUtf8EncodedBytes, err := os.ReadFile(rsaPublicKeyFilename)
70+
utils.HandleError(err)
71+
72+
// 2. Create a KMS RSA keyring.
73+
// This keyring takes in:
74+
// - kmsClient
75+
// - kmsKeyId: Must be an ARN representing a KMS RSA key
76+
// - publicKey: A ByteBuffer of a UTF-8 encoded PEM file representing the public
77+
// key for the key passed into kmsKeyId
78+
// - encryptionAlgorithm: Must be either RSAES_OAEP_SHA_256 or RSAES_OAEP_SHA_1
79+
cfg, err := config.LoadDefaultConfig(context.TODO())
80+
utils.HandleError(err)
81+
kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) {
82+
o.Region = "us-west-2"
83+
})
84+
matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{})
85+
utils.HandleError(err)
86+
87+
createAwsKmsRsaKeyringInput := mpltypes.CreateAwsKmsRsaKeyringInput{
88+
KmsClient: kmsClient,
89+
KmsKeyId: rsaKeyArn,
90+
PublicKey: publicKeyUtf8EncodedBytes,
91+
EncryptionAlgorithm: kmstypes.EncryptionAlgorithmSpecRsaesOaepSha256,
92+
}
93+
awsKmsRsaKeyring, err := matProv.CreateAwsKmsRsaKeyring(context.Background(), createAwsKmsRsaKeyringInput)
94+
utils.HandleError(err)
95+
96+
// 3. Configure which attributes are encrypted and/or signed when writing new items.
97+
// For each attribute that may exist on the items we plan to write to our DynamoDbTable,
98+
// we must explicitly configure how they should be treated during item encryption:
99+
// - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature
100+
// - SIGN_ONLY: The attribute not encrypted, but is still included in the signature
101+
// - DO_NOTHING: The attribute is not encrypted and not included in the signature
102+
attributeActions := map[string]dbesdkstructuredencryptiontypes.CryptoAction{
103+
"partition_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Our partition attribute must be SIGN_ONLY
104+
"sort_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Our sort attribute must be SIGN_ONLY
105+
"sensitive_data": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign,
106+
}
107+
108+
// 4. Configure which attributes we expect to be included in the signature
109+
// when reading items. There are two options for configuring this:
110+
//
111+
// - (Recommended) Configure `allowedUnsignedAttributesPrefix`:
112+
// When defining your DynamoDb schema and deciding on attribute names,
113+
// choose a distinguishing prefix (such as ":") for all attributes that
114+
// you do not want to include in the signature.
115+
// This has two main benefits:
116+
// - It is easier to reason about the security and authenticity of data within your item
117+
// when all unauthenticated data is easily distinguishable by their attribute name.
118+
// - If you need to add new unauthenticated attributes in the future,
119+
// you can easily make the corresponding update to your `attributeActions`
120+
// and immediately start writing to that new attribute, without
121+
// any other configuration update needed.
122+
// Once you configure this field, it is not safe to update it.
123+
//
124+
// - Configure `allowedUnsignedAttributes`: You may also explicitly list
125+
// a set of attributes that should be considered unauthenticated when encountered
126+
// on read. Be careful if you use this configuration. Do not remove an attribute
127+
// name from this configuration, even if you are no longer writing with that attribute,
128+
// as old items may still include this attribute, and our configuration needs to know
129+
// to continue to exclude this attribute from the signature scope.
130+
// If you add new attribute names to this field, you must first deploy the update to this
131+
// field to all readers in your host fleet before deploying the update to start writing
132+
// with that new attribute.
133+
//
134+
// For this example, we currently authenticate all attributes. To make it easier to
135+
// add unauthenticated attributes in the future, we define a prefix ":" for such attributes.
136+
unsignAttrPrefix := ":"
137+
138+
// 5. Create the DynamoDb Encryption configuration for the table we will be writing to.
139+
// Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite
140+
// that does not use asymmetric signing.
141+
partitionKey := "partition_key"
142+
sortKeyName := "sort_key"
143+
// Specify algorithmSuite without asymmetric signing here
144+
// The only supported algorithmSuite without asymmetric signing is
145+
// ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY_SYMSIG_HMAC_SHA384.
146+
algorithmSuiteID := mpltypes.DBEAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeySymsigHmacSha384
147+
148+
tableConfig := dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig{
149+
LogicalTableName: ddbTableName,
150+
PartitionKeyName: partitionKey,
151+
SortKeyName: &sortKeyName,
152+
AttributeActionsOnEncrypt: attributeActions,
153+
Keyring: awsKmsRsaKeyring,
154+
AllowedUnsignedAttributePrefix: &unsignAttrPrefix,
155+
AlgorithmSuiteId: &algorithmSuiteID,
156+
}
157+
158+
tableConfigsMap := make(map[string]dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig)
159+
tableConfigsMap[ddbTableName] = tableConfig
160+
listOfTableConfigs := dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig{
161+
TableEncryptionConfigs: tableConfigsMap,
162+
}
163+
164+
// 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor
165+
dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs)
166+
utils.HandleError(err)
167+
ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware())
168+
169+
// 7. Put an item into our table using the above client.
170+
// Before the item gets sent to DynamoDb, it will be encrypted
171+
// client-side, according to our configuration.
172+
item := map[string]types.AttributeValue{
173+
"partition_key": &types.AttributeValueMemberS{Value: "awsKmsRsaKeyringItem"},
174+
"sort_key": &types.AttributeValueMemberN{Value: "0"},
175+
"sensitive_data": &types.AttributeValueMemberS{Value: "encrypt and sign me!"},
176+
}
177+
178+
putInput := &dynamodb.PutItemInput{
179+
TableName: aws.String(ddbTableName),
180+
Item: item,
181+
}
182+
183+
_, err = ddb.PutItem(context.TODO(), putInput)
184+
utils.HandleError(err)
185+
186+
// 8. Get the item back from our table using the client.
187+
// The client will decrypt the item client-side using the RSA keyring
188+
// and return the original item.
189+
keyToGet := map[string]types.AttributeValue{
190+
"partition_key": &types.AttributeValueMemberS{Value: "awsKmsRsaKeyringItem"},
191+
"sort_key": &types.AttributeValueMemberN{Value: "0"},
192+
}
193+
194+
getInput := &dynamodb.GetItemInput{
195+
Key: keyToGet,
196+
TableName: aws.String(ddbTableName),
197+
// In this example we configure a strongly consistent read
198+
// because we perform a read immediately after a write (for demonstrative purposes).
199+
// By default, reads are only eventually consistent.
200+
// Read our docs to determine which read consistency to use for your application:
201+
// https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
202+
ConsistentRead: aws.Bool(true),
203+
}
204+
205+
getResponse, err := ddb.GetItem(context.TODO(), getInput)
206+
utils.HandleError(err)
207+
208+
returnedItem := getResponse.Item
209+
if !reflect.DeepEqual(item, returnedItem) {
210+
panic("Decrypted item does not match original item")
211+
}
212+
213+
fmt.Println("AWS KMS RSA Keyring Example successful.")
214+
}
215+
216+
func writePublicKeyPemForRsaKey(rsaKeyArn, rsaPublicKeyFilename string) {
217+
// This code will call KMS to get the public key for the KMS RSA key.
218+
// You must have kms:GetPublicKey permissions on the key for this to succeed.
219+
// The public key will be written to the file rsaPublicKeyFilename.
220+
cfg, err := config.LoadDefaultConfig(context.TODO())
221+
utils.HandleError(err)
222+
223+
getterForPublicKey := kms.NewFromConfig(cfg, func(o *kms.Options) {
224+
o.Region = "us-west-2"
225+
})
226+
227+
response, err := getterForPublicKey.GetPublicKey(context.TODO(), &kms.GetPublicKeyInput{
228+
KeyId: aws.String(rsaKeyArn),
229+
})
230+
utils.HandleError(err)
231+
232+
publicKeyByteArray := response.PublicKey
233+
234+
// Create PEM formatted public key
235+
pemContent := fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
236+
encodeToPemBase64(publicKeyByteArray))
237+
238+
// Ensure directory exists
239+
dir := filepath.Dir(rsaPublicKeyFilename)
240+
if err := os.MkdirAll(dir, 0755); err != nil {
241+
utils.HandleError(err)
242+
}
243+
244+
// Write to file
245+
err = os.WriteFile(rsaPublicKeyFilename, []byte(pemContent), 0644)
246+
utils.HandleError(err)
247+
}
248+
249+
// encodeToPemBase64 encodes bytes to base64 with proper line breaks for PEM format
250+
func encodeToPemBase64(data []byte) string {
251+
const lineLength = 64
252+
encoded := base64.StdEncoding.EncodeToString(data)
253+
254+
// Add line breaks every 64 characters
255+
var result strings.Builder
256+
for i := 0; i < len(encoded); i += lineLength {
257+
end := i + lineLength
258+
if end > len(encoded) {
259+
end = len(encoded)
260+
}
261+
result.WriteString(encoded[i:end])
262+
if end < len(encoded) {
263+
result.WriteString("\n")
264+
}
265+
}
266+
267+
return result.String()
268+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package keyring
5+
6+
import (
7+
"fmt"
8+
9+
dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes"
10+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
11+
)
12+
13+
// ExampleBranchKeyIdSupplier is used in the 'HierarchicalKeyringExample'.
14+
// In that example, we have a table where we distinguish multiple tenants
15+
// by a tenant ID that is stored in our partition attribute.
16+
// The expectation is that this does not produce a confused deputy
17+
// because the tenants are separated by partition.
18+
// In order to create a Hierarchical Keyring that is capable of encrypting or
19+
// decrypting data for either tenant, we implement this interface
20+
// to map the correct branch key ID to the correct tenant ID.
21+
type ExampleBranchKeyIdSupplier struct {
22+
branchKeyIdForTenant1 string
23+
branchKeyIdForTenant2 string
24+
}
25+
26+
// NewExampleBranchKeyIdSupplier creates a new instance of ExampleBranchKeyIdSupplier
27+
func NewExampleBranchKeyIdSupplier(tenant1Id, tenant2Id string) *ExampleBranchKeyIdSupplier {
28+
return &ExampleBranchKeyIdSupplier{
29+
branchKeyIdForTenant1: tenant1Id,
30+
branchKeyIdForTenant2: tenant2Id,
31+
}
32+
}
33+
34+
// GetBranchKeyIdFromDdbKey implements the DynamoDbKeyBranchKeyIdSupplier interface
35+
func (s *ExampleBranchKeyIdSupplier) GetBranchKeyIdFromDdbKey(
36+
input dbesdkdynamodbencryptiontypes.GetBranchKeyIdFromDdbKeyInput,
37+
) (*dbesdkdynamodbencryptiontypes.GetBranchKeyIdFromDdbKeyOutput, error) {
38+
if input.DdbKey == nil {
39+
return nil, fmt.Errorf("DdbKey cannot be nil")
40+
}
41+
42+
key := input.DdbKey
43+
44+
partitionKeyAttr, exists := key["partition_key"]
45+
if !exists {
46+
return nil, fmt.Errorf("item invalid, does not contain expected partition key attribute")
47+
}
48+
49+
// Extract the string value from the AttributeValue
50+
var tenantKeyId string
51+
switch attr := partitionKeyAttr.(type) {
52+
case *types.AttributeValueMemberS:
53+
tenantKeyId = attr.Value
54+
default:
55+
return nil, fmt.Errorf("partition_key must be a string attribute")
56+
}
57+
58+
var branchKeyId string
59+
switch tenantKeyId {
60+
case "tenant1Id":
61+
branchKeyId = s.branchKeyIdForTenant1
62+
case "tenant2Id":
63+
branchKeyId = s.branchKeyIdForTenant2
64+
default:
65+
return nil, fmt.Errorf("item does not contain valid tenant ID")
66+
}
67+
68+
return &dbesdkdynamodbencryptiontypes.GetBranchKeyIdFromDdbKeyOutput{
69+
BranchKeyId: branchKeyId,
70+
}, nil
71+
}

0 commit comments

Comments
 (0)