diff --git a/.github/workflows/ci_test_go.yml b/.github/workflows/ci_test_go.yml index 683720c2e..30fd1d363 100644 --- a/.github/workflows/ci_test_go.yml +++ b/.github/workflows/ci_test_go.yml @@ -133,3 +133,9 @@ jobs: working-directory: ./${{ matrix.library }} run: | make test_go + + - name: Test Examples + if: matrix.library == 'DynamoDbEncryption' + working-directory: ./Examples/runtimes/go + run: | + go run main.go diff --git a/Examples/runtimes/go/go.mod b/Examples/runtimes/go/go.mod new file mode 100644 index 000000000..02a5e12ed --- /dev/null +++ b/Examples/runtimes/go/go.mod @@ -0,0 +1,46 @@ +module github.com/aws/aws-database-encryption-sdk-dynamodb/examples + +go 1.23.2 + +replace github.com/aws/aws-database-encryption-sdk-dynamodb => ../../../DynamoDbEncryption/runtimes/go/ImplementationFromDafny-go + +replace github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl => ../../../submodules/MaterialProviders/AwsCryptographicMaterialProviders/runtimes/go/ImplementationFromDafny-go/ + +replace github.com/aws/aws-cryptographic-material-providers-library/releases/go/primitives => ../../../submodules/MaterialProviders/AwsCryptographyPrimitives/runtimes/go/ImplementationFromDafny-go/ + +replace github.com/aws/aws-cryptographic-material-providers-library/releases/go/dynamodb => ../../../submodules/MaterialProviders/ComAmazonawsDynamodb/runtimes/go/ImplementationFromDafny-go/ + +replace github.com/aws/aws-cryptographic-material-providers-library/releases/go/kms => ../../../submodules/MaterialProviders/ComAmazonawsKms/runtimes/go/ImplementationFromDafny-go/ + +replace github.com/aws/aws-cryptographic-material-providers-library/releases/go/smithy-dafny-standard-library => ../../../submodules/MaterialProviders/StandardLibrary/runtimes/go/ImplementationFromDafny-go/ + +require ( + github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl v0.0.0 + github.com/aws/aws-database-encryption-sdk-dynamodb v0.0.0 + github.com/aws/aws-sdk-go-v2 v1.32.8 + github.com/aws/aws-sdk-go-v2/config v1.28.10 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.2 + github.com/aws/aws-sdk-go-v2/service/kms v1.36.0 +) + +require ( + github.com/aws/aws-cryptographic-material-providers-library/releases/go/dynamodb v0.1.0 // indirect + github.com/aws/aws-cryptographic-material-providers-library/releases/go/kms v0.0.1 // indirect + github.com/aws/aws-cryptographic-material-providers-library/releases/go/primitives v0.0.1 // indirect + github.com/aws/aws-cryptographic-material-providers-library/releases/go/smithy-dafny-standard-library v0.1.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.51 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 // indirect + github.com/aws/smithy-go v1.22.1 // indirect + github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect +) diff --git a/Examples/runtimes/go/go.sum b/Examples/runtimes/go/go.sum new file mode 100644 index 000000000..213ca1d92 --- /dev/null +++ b/Examples/runtimes/go/go.sum @@ -0,0 +1,50 @@ +github.com/aws/aws-cryptographic-material-providers-library/releases/go/smithy-dafny-standard-library v0.1.0 h1:Nw3zDK7jQ/ylj1isG91PdsEKdojIlI+iX3I43h6uj1I= +github.com/aws/aws-cryptographic-material-providers-library/releases/go/smithy-dafny-standard-library v0.1.0/go.mod h1:m3mzHKiNiSC0LWeWX6ZAxSe6mKbJHgliux1Yu/sjCYI= +github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo= +github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg= +github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.2 h1:XcdIh35yg1J8bAiUOLtL/PoPMSGsD72Zanwmim8jEXc= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.39.2/go.mod h1:516U/KQM3zdcahNBjHUZKGWNfNnIYyt7sxLeqOx78b0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.8 h1:h56mLNgpqWIL7RZOIQO634Xr569bXGTlIE83t/a0LSE= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.8/go.mod h1:kK04550Xx95KI0sNmwoB7ciS9QkRwt9TojhoTMXyJdo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE= +github.com/aws/aws-sdk-go-v2/service/kms v1.36.0 h1:jwWMpQ/1obJRdHaix9k10zWSnSMZGdDTZIDiS5CGzq8= +github.com/aws/aws-sdk-go-v2/service/kms v1.36.0/go.mod h1:OHmlX4+o0XIlJAQGAHPIy0N9yZcYS/vNG+T7geSNcFw= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.2 h1:g/xAj4F7Zt9wXJ6QjfbfocVi/ZYlAFpNddHCFyfzRDg= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.2/go.mod h1:l2Tm4N2DKuq3ljONC2vOATeM9PUpXbIc8SgXdwwqEto= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/Examples/runtimes/go/keyring/awskmskeyring.go b/Examples/runtimes/go/keyring/awskmskeyring.go new file mode 100644 index 000000000..f7d1a11cb --- /dev/null +++ b/Examples/runtimes/go/keyring/awskmskeyring.go @@ -0,0 +1,179 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package keyring + +import ( + "context" + "fmt" + "reflect" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes" + dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes" + "github.com/aws/aws-database-encryption-sdk-dynamodb/dbesdkmiddleware" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +/* +This example sets up DynamoDb Encryption for the AWS SDK client +and uses the low level PutItem and GetItem DDB APIs to demonstrate +putting a client-side encrypted item into DynamoDb +and then retrieving and decrypting that item from DynamoDb. + +Running this example requires access to the DDB Table whose name +is provided in CLI arguments. +This table must be configured with the following +primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (N) +*/ +func AwsKmsKeyringExample(kmsKeyID, ddbTableName string) { + // 1. Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data. + // For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use. + // We will use the `CreateMrkMultiKeyring` method to create this keyring, + // as it will correctly handle both single region and Multi-Region KMS Keys. + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + // Create KMS client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Create the Aws Kms Keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: kmsKeyID, + } + keyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + + // 2. Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature + attributeActions := map[string]dbesdkstructuredencryptiontypes.CryptoAction{ + "partition_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Partition key must be SIGN_ONLY + "sort_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Sort key must be SIGN_ONLY + "attribute1": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign, + "attribute2": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, + ":attribute3": dbesdkstructuredencryptiontypes.CryptoActionDoNothing, + } + + // 3. Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: + // + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. + // + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. + // + // For this example, we have designed our DynamoDb table such that any attribute name with + // the ":" prefix should be considered unauthenticated. + allowedUnsignedAttributePrefix := ":" + + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + partitionKey := "partition_key" + sortKeyName := "sort_key" + algorithmSuiteId := mpltypes.DBEAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384SymsigHmacSha384 + tableConfig := dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig{ + LogicalTableName: ddbTableName, + PartitionKeyName: partitionKey, + SortKeyName: &sortKeyName, + AttributeActionsOnEncrypt: attributeActions, + Keyring: keyring, + AllowedUnsignedAttributePrefix: &allowedUnsignedAttributePrefix, + AlgorithmSuiteId: &algorithmSuiteId, + } + tableConfigsMap := make(map[string]dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig) + tableConfigsMap[ddbTableName] = tableConfig + listOfTableConfigs := dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig{ + TableEncryptionConfigs: tableConfigsMap, + } + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs) + if err != nil { + panic(err) + } + ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware()) + + // 6. Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. + item := map[string]types.AttributeValue{ + "partition_key": &types.AttributeValueMemberS{Value: "BasicPutGetExample"}, + "sort_key": &types.AttributeValueMemberN{Value: "0"}, + "attribute1": &types.AttributeValueMemberS{Value: "encrypt and sign me!"}, + "attribute2": &types.AttributeValueMemberS{Value: "sign me!"}, + ":attribute3": &types.AttributeValueMemberS{Value: "ignore me!"}, + } + putInput := &dynamodb.PutItemInput{ + TableName: aws.String(ddbTableName), + Item: item, + } + _, err = ddb.PutItem(context.TODO(), putInput) + if err != nil { + panic(err) + } + + // 7. Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. + key := map[string]types.AttributeValue{ + "partition_key": &types.AttributeValueMemberS{Value: "BasicPutGetExample"}, + "sort_key": &types.AttributeValueMemberN{Value: "0"}, + } + getInput := &dynamodb.GetItemInput{ + TableName: aws.String(ddbTableName), + Key: key, + // In this example we configure a strongly consistent read + // because we perform a read immediately after a write (for demonstrative purposes). + // By default, reads are only eventually consistent. + // Read our docs to determine which read consistency to use for your application: + // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html + ConsistentRead: aws.Bool(true), + } + result, err := ddb.GetItem(context.TODO(), getInput) + if err != nil { + panic(err) + } + // Verify the decrypted item + if !reflect.DeepEqual(item, result.Item) { + panic("Decrypted item does not match original item") + } + fmt.Println("Aws Kms Keyring Example successful.") +} diff --git a/Examples/runtimes/go/keyring/rawaeskeyring.go b/Examples/runtimes/go/keyring/rawaeskeyring.go new file mode 100644 index 000000000..eac292994 --- /dev/null +++ b/Examples/runtimes/go/keyring/rawaeskeyring.go @@ -0,0 +1,197 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package keyring + +import ( + "context" + "crypto/rand" + "fmt" + "reflect" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + dbesdkdynamodbencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes" + dbesdkstructuredencryptiontypes "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes" + "github.com/aws/aws-database-encryption-sdk-dynamodb/dbesdkmiddleware" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" +) + +/* + This example sets up DynamoDb Encryption for the AWS SDK client + using the raw AES Keyring. This keyring takes in an AES key + and uses that key to protect the data keys that encrypt and + decrypt DynamoDb table items. + + This example takes in an `aesKeyBytes` parameter. This parameter + should be a ByteBuffer representing a 256-bit AES key. If this example + is run through the class' main method, it will create a new key. + In practice, users of this library should not randomly generate a key, + and should instead retrieve an existing key from a secure key + management system (e.g. an HSM). + + This example encrypts a test item using the provided AES key and puts the + encrypted item to the provided DynamoDb table. Then, it gets the + item from the table and decrypts it. + + Running this example requires access to the DDB Table whose name + is provided in CLI arguments. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) +*/ + +func RawAesExample(ddbTableName string) { + aesKeyBytes, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // 1. Create the keyring. + // The DynamoDb encryption client uses this to encrypt and decrypt items. + + // Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Create the Raw Aes Keyring + var keyNamespace = "my-key-namespace" + var keyName = "my-aes-key-name" + rawAesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: aesKeyBytes, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + rawAesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), rawAesKeyRingInput) + if err != nil { + panic(err) + } + // 2. Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature + attributeActionsOnEncrypt := map[string]dbesdkstructuredencryptiontypes.CryptoAction{ + "partition_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Partition key must be SIGN_ONLY + "sort_key": dbesdkstructuredencryptiontypes.CryptoActionSignOnly, // Sort key must be SIGN_ONLY + "sensitive_data": dbesdkstructuredencryptiontypes.CryptoActionEncryptAndSign, + } + // 3. Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: + // + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. + // + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. + // + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + unsignedAttrPrefix := ":" + + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + partitionKey := "partition_key" + sortKeyName := "sort_key" + tableConfig := dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig{ + LogicalTableName: ddbTableName, + PartitionKeyName: partitionKey, + SortKeyName: &sortKeyName, + AttributeActionsOnEncrypt: attributeActionsOnEncrypt, + Keyring: rawAesKeyring, + AllowedUnsignedAttributePrefix: &unsignedAttrPrefix, + } + tableConfigsMap := make(map[string]dbesdkdynamodbencryptiontypes.DynamoDbTableEncryptionConfig) + tableConfigsMap[ddbTableName] = tableConfig + listOfTableConfigs := dbesdkdynamodbencryptiontypes.DynamoDbTablesEncryptionConfig{ + TableEncryptionConfigs: tableConfigsMap, + } + // 5. Create a new AWS SDK DynamoDb client using the Config above + + // Create DBESDK middleware + dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs) + if err != nil { + panic(err) + } + // Create aws config + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware()) + + // 6. Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. + item := map[string]types.AttributeValue{ + "partition_key": &types.AttributeValueMemberS{Value: "rawAesKeyringItem"}, + "sort_key": &types.AttributeValueMemberN{Value: "0"}, + "sensitive_data": &types.AttributeValueMemberS{Value: "encrypt and sign me!"}, + } + putInput := &dynamodb.PutItemInput{ + TableName: aws.String(ddbTableName), + Item: item, + } + _, err = ddb.PutItem(context.TODO(), putInput) + if err != nil { + panic(err) + } + // 7. Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. + key := map[string]types.AttributeValue{ + "partition_key": &types.AttributeValueMemberS{Value: "rawAesKeyringItem"}, + "sort_key": &types.AttributeValueMemberN{Value: "0"}, + } + getInput := &dynamodb.GetItemInput{ + TableName: aws.String(ddbTableName), + Key: key, + // In this example we configure a strongly consistent read + // because we perform a read immediately after a write (for demonstrative purposes). + // By default, reads are only eventually consistent. + // Read our docs to determine which read consistency to use for your application: + // https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html + ConsistentRead: aws.Bool(true), + } + result, err := ddb.GetItem(context.TODO(), getInput) + if err != nil { + panic(err) + } + // Verify the decrypted item + if !reflect.DeepEqual(item, result.Item) { + panic("Decrypted item does not match original item") + } + fmt.Println("Raw Aes Example successful.") +} + +func generateAes256KeyBytes() ([]byte, error) { + key := make([]byte, 32) // 256 bits = 32 bytes + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} diff --git a/Examples/runtimes/go/main.go b/Examples/runtimes/go/main.go new file mode 100644 index 000000000..88aaefd33 --- /dev/null +++ b/Examples/runtimes/go/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/aws/aws-database-encryption-sdk-dynamodb/examples/keyring" + "github.com/aws/aws-database-encryption-sdk-dynamodb/examples/utils" +) + +func main() { + keyring.AwsKmsKeyringExample(utils.KmsKeyID(), utils.DdbTableName()) + keyring.RawAesExample(utils.DdbTableName()) +} diff --git a/Examples/runtimes/go/utils/exampleUtils.go b/Examples/runtimes/go/utils/exampleUtils.go new file mode 100644 index 000000000..f11dc14d0 --- /dev/null +++ b/Examples/runtimes/go/utils/exampleUtils.go @@ -0,0 +1,31 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +const ( + kmsKeyID = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + ddbTableName = "DynamoDbEncryptionInterceptorTestTableCS" +) + +func KmsKeyID() string { + return kmsKeyID +} + +func DdbTableName() string { + return ddbTableName +} + +func AreMapsEqual(map1, map2 map[string]string) bool { + if len(map1) != len(map2) { + return false + } + + for key, value1 := range map1 { + value2, exists := map2[key] + if !exists || value1 != value2 { + return false + } + } + return true +}