From f24cc45806813886be9a605923b9339c7b1e664e Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 11:01:49 -0800 Subject: [PATCH 01/11] Examples init --- AwsEncryptionSDK/Makefile | 18 + .../clientsupplier/clientSupplierExample.go | 163 +++++++++ .../regionalroleclientsupplier.go | 57 ++++ .../regionalroleclientsupplierconfig.go | 22 ++ .../go/examples/examples/commitmentpolicy.go | 149 ++++++++ .../requiredencryptioncontext.go | 161 +++++++++ .../signingonlyexample.go | 137 ++++++++ .../signingsuiteonlycmm.go | 77 +++++ .../runtimes/go/examples/examples/go.mod | 47 +++ .../runtimes/go/examples/examples/go.sum | 48 +++ .../awskmsdiscoverykeyring.go | 189 +++++++++++ .../awskmsdiscoverymultikeyring.go | 169 +++++++++ .../awskmshierarchicalkeyring.go | 296 ++++++++++++++++ .../branchkeysupplier.go | 45 +++ .../createbranchkeyid.go | 45 +++ .../sharedcacheacrosshierarchicalkeyring.go | 228 +++++++++++++ .../versionbranchkeyid.go | 93 +++++ .../keyring/awskmskeyring/awskmskeyring.go | 122 +++++++ .../awskmsmrkdiscoverykeyring.go | 172 ++++++++++ .../awskmsmrkdiscoverymultikeyring.go | 164 +++++++++ .../awskmsmrkkeyring/awskmsmrkkeyring.go | 152 +++++++++ .../awskmsmrkmultikeyring.go | 153 +++++++++ .../awskmsmultikeyring/awskmsmultikeyring.go | 172 ++++++++++ .../awskmsrsakeyring/awskmsrsakeyring.go | 127 +++++++ .../ecdh/awskmsecdhdiscoverykeyring.go | 219 ++++++++++++ .../keyring/ecdh/awskmsecdhkeyring.go | 193 +++++++++++ .../keyring/ecdh/ephemeralrawecdhkeyring.go | 137 ++++++++ .../ecdh/publickeyrawdiscoveryecdhkeyring.go | 218 ++++++++++++ .../examples/keyring/ecdh/rawecdhkeyring.go | 194 +++++++++++ .../keyring/multikeyring/multikeyring.go | 233 +++++++++++++ .../keyring/rawaeskeyring/rawaeskeyring.go | 138 ++++++++ .../keyring/rawrsakeyring/rawrasakeyring.go | 178 ++++++++++ .../examples/limitencrypteddatakeysexample.go | 176 ++++++++++ .../runtimes/go/examples/examples/main.go | 157 +++++++++ .../examples/setencryptionalgorithmsuite.go | 171 ++++++++++ .../examples/examples/utils/exampleUtils.go | 321 ++++++++++++++++++ 36 files changed, 5341 insertions(+) create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/go.mod create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/go.sum create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/main.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go diff --git a/AwsEncryptionSDK/Makefile b/AwsEncryptionSDK/Makefile index a7b4437cd..aba19acc4 100644 --- a/AwsEncryptionSDK/Makefile +++ b/AwsEncryptionSDK/Makefile @@ -92,3 +92,21 @@ TYPES_FILE_WITHOUT_EXTERN_STRING="module AwsCryptographyEncryptionSdkTypes" INDEX_FILE_PATH=dafny/AwsEncryptionSdk/src/Index.dfy INDEX_FILE_WITH_EXTERN_STRING="module {:extern \"software.amazon.cryptography.encryptionsdk.internaldafny\" } ESDK refines AbstractAwsCryptographyEncryptionSdkService {" INDEX_FILE_WITHOUT_EXTERN_STRING="module ESDK refines AbstractAwsCryptographyEncryptionSdkService {" + +# Target to restore all directories in a list +# TODO: remove this once we don't copy all of the directories into implementation and test. This is done by make file target _mv_polymorph_go in smithy-dafny. +RESTORE_DIRS := examples +_polymorph_go: restore_directories +restore_directories: + @for dir in $(RESTORE_DIRS); do \ + if [ -d "runtimes/go/ImplementationFromDafny-go/$$dir" ]; then \ + cp -Rf runtimes/go/ImplementationFromDafny-go/$$dir runtimes/go/; \ + rm -rf runtimes/go/ImplementationFromDafny-go/$$dir; \ + rm -rf runtimes/go/TestsFromDafny-go/$$dir; \ + else \ + echo "Directory $$dir not found"; \ + fi \ + done + +_polymorph_dependencies: + @echo "No polymorphing of dependency" \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go new file mode 100644 index 000000000..d06cc467e --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go @@ -0,0 +1,163 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up an MRK multi-keyring and an MRK discovery + multi-keyring using a custom client supplier. + A custom client supplier grants users access to more granular + configuration aspects of their authentication details and KMS + client. In this example, we create a simple custom client supplier + that authenticates with a different IAM role based on the + region of the KMS key. + + This example creates a MRK multi-keyring configured with a custom + client supplier using a single MRK and encrypts the example_data with it. + Then, it creates a MRK discovery multi-keyring to decrypt the ciphertext. +*/ + +package clientsupplier + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func ClientSupplierExample(exampleText, mrkKeyIdEncrypt, awsAccountId string, awsRegions []string) { + // Step 1: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 3: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create keyrings + // First Keyring: Create the multi-keyring using our custom client supplier + // defined in the RegionalRoleClientSupplier class in this directory. + // Note: RegionalRoleClientSupplier will internally use the key_arn's region + // to retrieve the correct IAM role. + awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Generator: &mrkKeyIdEncrypt, + } + awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a MRK discovery multi-keyring with a custom client supplier. + // A discovery MRK multi-keyring will be composed of + // multiple discovery MRK keyrings, one for each region. + // Each component keyring has its own KMS client in a particular region. + // When we provide a client supplier to the multi-keyring, all component + // keyrings will use that client supplier configuration. + // In our tests, we make `mrk_key_id_encrypt` an MRK with a replica, and + // provide only the replica region in our discovery filter. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{awsAccountId}, + Partition: "aws", + } + awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Regions: awsRegions, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + // Decrypt your encrypted data using the discovery multi keyring. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + // is successful. + // Since every member of the Multi Keyring is a Discovery Keyring: + // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. + // For each filtered EDK, the keyring will attempt decryption with the keyring's client. + // All of this is done serially, until a success occurs or all keyrings have failed + // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt + // Multi Region Keys (MRKs) and regular KMS Keys. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Test the Missing Region Exception + // (This is for demonstration; you do not need to do this in your code.) + + // Create a MRK discovery multi-keyring with a custom client supplier and a fake region. + awsKmsMrkDiscoveryMultiKeyringInputMissingRegion := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Regions: []string{"fake-region"}, + DiscoveryFilter: &discoveryFilter, + } + _, err = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInputMissingRegion) + // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("Decryption using discovery keyring with missing region MUST raise AwsCryptographicMaterialProvidersException") + } + fmt.Println("Client Supplier Example completed successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go new file mode 100644 index 000000000..762cdf745 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go @@ -0,0 +1,57 @@ +package clientsupplier + +import ( + "context" + + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/sts" +) + +/* + Example class demonstrating an implementation of a custom client supplier. + This particular implementation will create KMS clients with different IAM roles, + depending on the region passed. +*/ + +// RegionalRoleClientSupplier provides implementation for mpltypes.IClientSupplier +type RegionalRoleClientSupplier struct { +} + +func (this *RegionalRoleClientSupplier) GetClient(input mpltypes.GetClientInput) (kms.Client, error) { + region := input.Region + // Check if the region is supported + regionIamRoleMap := RegionIamRoleMap() + var defaultVal kms.Client + // Check if region is supported + if _, exists := regionIamRoleMap[region]; !exists { + return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{ + Message: "Region is not supported by this client supplier", + } + } + // Get the IAM role ARN associated with the region + arn := regionIamRoleMap[region] + ctx := context.TODO() + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(region), + ) + if err != nil { + return defaultVal, err + } + stsClient := sts.NewFromConfig(cfg) + // Create the AssumeRoleProvider + provider := stscreds.NewAssumeRoleProvider(stsClient, arn, func(o *stscreds.AssumeRoleOptions) { + o.RoleSessionName = "Go-ESDK-Client-Supplier-Example-Session" + }) + // Load AWS SDK configuration with the AssumeRoleProvider + sdkConfig, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region), config.WithCredentialsProvider(provider)) + if err != nil { + return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{Message: "failed to load AWS SDK config"} + } + // Create the KMS client + kmsClient := kms.NewFromConfig(sdkConfig) + // Return the KMS client wrapped in a custom type + return *kmsClient, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go new file mode 100644 index 000000000..2c82f40fb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go @@ -0,0 +1,22 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package clientsupplier + +/* + File containing config for the RegionalRoleClientSupplier. + In your own code, this might be hardcoded, or reference + an external source, e.g. environment variables or AWS AppConfig. +*/ + +const ( + usEast1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-us-east-1-KMS-keys" + euWest1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-eu-west-1-KMS-keys" +) + +func RegionIamRoleMap() map[string]string { + return map[string]string{ + "us-east-1": usEast1IamRole, + "eu-west-1": euWest1IamRole, + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go b/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go new file mode 100644 index 000000000..2d501be4b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go @@ -0,0 +1,149 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example configures a client with a specific commitment policy for the +AWS Encryption SDK client, then encrypts and decrypts data using an AWS KMS Keyring. + +The commitment policy in this example (ForbidEncryptAllowDecrypt) should only be +used as part of a migration from version 1.x to 2.x, or for advanced users with +specialized requirements. Most AWS Encryption SDK users should use the default +commitment policy (RequireEncryptRequireDecrypt). + +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context for the commitment policy ForbidEncryptAllowDecrypt. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on setting your commitment policy, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package examples + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func CommitmentPolicyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultKmsKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // Build the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + + // Create one with the commitment policy RequireEncryptAllowDecrypt and another with ForbidEncryptAllowDecrypt. + // Read more about commitment policies here: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy + commitPolicyRequireEncryptRequireDecrypt := mpltypes.ESDKCommitmentPolicyRequireEncryptRequireDecrypt + commitPolicyForbidEncryptAllowDecrypt := mpltypes.ESDKCommitmentPolicyForbidEncryptAllowDecrypt + forbidEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyForbidEncryptAllowDecrypt}) + if err != nil { + panic(err) + } + requireEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyRequireEncryptRequireDecrypt}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + // Make sure you use a non-committing algorithm with the commitment policy ForbidEncryptAllowDecrypt. + // Otherwise encrypt() will throw + // Error: AwsCryptographicMaterialProvidersError + // { + // error: InvalidAlgorithmSuiteInfoOnEncrypt + // { + // message: "Configuration conflict. Commitment policy requires only non-committing algorithm suites" + // } + // } + // By default for ForbidEncryptAllowDecrypt, the algorithm used is + // AlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 which is a non-committing algorithm. + res, err := forbidEncryptClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := forbidEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Demonstrate that an EncryptionSDK that enforces Key Commitment on Decryption + // will fail to decrypt the encrypted message (as it was encrypted without Key Commitment). + _, err = requireEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + // We expect this to fail + if err == nil { + panic("Expected error but error is nil") + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Set Commitment Policy Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go new file mode 100644 index 000000000..5c763d09f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go @@ -0,0 +1,161 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. +A required encryption context CMM asks for required keys in the encryption context field +on encrypt such that they will not be stored on the message, but WILL be included in the header signature. +On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. +*/ + +package requiredencryptioncontext + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func RequiredEncryptionContextExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = utils.GetDefaultKmsKeyRegion() + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: utils.GetDefaultKMSKeyId(), + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + // Step 6: Create your required encryption context keys. + // These keys MUST be in your encryption context. + // These keys and their corresponding values WILL NOT be stored on the message but will be used + // for authentication. + underlyingCMM, err := matProv.CreateDefaultCryptographicMaterialsManager(context.Background(), mpltypes.CreateDefaultCryptographicMaterialsManagerInput{Keyring: awsKmsKeyring}) + if err != nil { + panic(err) + } + requiredEncryptionContextKeys := []string{} + requiredEncryptionContextKeys = append(requiredEncryptionContextKeys, "requiredKey1", "requiredKey2") + requiredEncryptionContextInput := mpltypes.CreateRequiredEncryptionContextCMMInput{ + UnderlyingCMM: underlyingCMM, + // If you pass in a keyring but no underlying cmm, it will result in a failure because only cmm is supported. + RequiredEncryptionContextKeys: requiredEncryptionContextKeys, + } + requiredEC, err := matProv.CreateRequiredEncryptionContextCMM(context.Background(), requiredEncryptionContextInput) + if err != nil { + panic(err) + } + // Step 7a: Encrypt + // NOTE: the keys "requiredKey1", and "requiredKey2" + // WILL NOT be stored in the message header, but "encryption", "is not", + // "but adds", "that can help you", and "the data you are handling" WILL be stored. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + MaterialsManager: requiredEC, + EncryptionContext: encryptionContext, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 7b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // For demonstration attempt to decrypt your encrypted data using the same cryptographic material manager + // you used on encrypt, but we won't pass the encryption context we DID NOT store on the message. + // This will fail + _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + // We expect failure. + if err == nil { + panic("Decryption passed without any error when encryption context was not provided.") + } + // Decrypt your encrypted data using the same cryptographic material manager + // you used to encrypt, but supply encryption context that contains ONLY the encryption context that + // was NOT stored. This will pass. + reproducedEncryptionContext := map[string]string{ + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + decryptOutputreproducedEC, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: reproducedEncryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputreproducedEC.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // You can also decrypt with the underlyingCMM, but must still provide the reproducedEncryptionContext. + decryptOutputWithCMM, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: reproducedEncryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: underlyingCMM, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputWithCMM.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Required Encryption Context CMM Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go new file mode 100644 index 000000000..6ce7c5460 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go @@ -0,0 +1,137 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + Demonstrate an encrypt/decrypt cycle using a Custom Cryptographic Materials Manager (CMM). + `signingsuiteonlycmm.go` demonstrates creating a custom CMM to reject Non-Signing Algorithms. +*/ + +package restrictalgorithmsuite + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func SigningOnlyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the AWS KMS client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultKmsKeyRegion + }) + // Step 3: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the Aws KMS Keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Create an instance of the custom CMM + cmm, err := NewSigningSuiteOnlyCMM(awsKmsKeyring) + if err != nil { + panic(err) + } + // Step 5a: Encrypt + algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384 + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + AlgorithmSuiteId: &algorithmSuiteId, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a Non Signing Algorithm Suite will be rejected by the CMM. + nonSigningAlgorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + _, err = encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + AlgorithmSuiteId: &nonSigningAlgorithmSuiteId, + }) + if err == nil { + panic("Expected error but error is nil") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + fmt.Println("SigningSuiteOnlyCMM Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go new file mode 100644 index 000000000..7d02d7a1b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go @@ -0,0 +1,77 @@ +package restrictalgorithmsuite + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" +) + +type SigningSuiteOnlyCMM struct { + approvedAlgos map[mpltypes.ESDKAlgorithmSuiteId]bool + cmm mpltypes.ICryptographicMaterialsManager +} + +// NewSigningSuiteOnlyCMM creates a new SigningSuiteOnlyCMM +func NewSigningSuiteOnlyCMM(keyring mpltypes.IKeyring) (*SigningSuiteOnlyCMM, error) { + // Initialize the MPL client + materialProviders, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Create a DefaultCryptographicMaterialsManager + cmmInput := mpltypes.CreateDefaultCryptographicMaterialsManagerInput{ + Keyring: keyring, + } + cmm, err := materialProviders.CreateDefaultCryptographicMaterialsManager(context.Background(), cmmInput) + if err != nil { + return nil, err + } + // Create list of approved algorithm + var approvedAlgos = map[mpltypes.ESDKAlgorithmSuiteId]bool{ + mpltypes.ESDKAlgorithmSuiteIdAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384: true, + } + return &SigningSuiteOnlyCMM{ + approvedAlgos: approvedAlgos, + cmm: cmm, + }, nil +} + +func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) GetEncryptionMaterials(input mpltypes.GetEncryptionMaterialsInput) (*mpltypes.GetEncryptionMaterialsOutput, error) { + // Get the algorithm suite from the input + esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) + if err != nil { + return nil, err + } + // Check if the algorithm is approved + if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { + return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} + } + // Delegate to the underlying CMM + return signingSuiteOnlyCMM.cmm.GetEncryptionMaterials(input) +} + +func getESDKAlgorithmSuite(algSuite mpltypes.AlgorithmSuiteId) (*mpltypes.AlgorithmSuiteIdMemberESDK, error) { + if esdk, ok := algSuite.(*mpltypes.AlgorithmSuiteIdMemberESDK); ok { + return esdk, nil + } + return nil, fmt.Errorf("algorithm suite is not ESDK type") +} + +func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) DecryptMaterials(input mpltypes.DecryptMaterialsInput) (*mpltypes.DecryptMaterialsOutput, error) { + // Get the algorithm suite from the input + esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) + if err != nil { + return nil, err + } + // Check if the algorithm is approved + if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { + return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} + } + // Delegate to the underlying CMM + return signingSuiteOnlyCMM.cmm.DecryptMaterials(input) +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod b/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod new file mode 100644 index 000000000..5daa093c6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod @@ -0,0 +1,47 @@ +module github.com/aws/aws-encryption-sdk/aws-esdk-go-preview + +go 1.23.0 + +replace github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 => ../mpl + +replace ( + github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 => ../dynamodb + github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 => ../kms + github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 => ../primitives + +) + +replace github.com/dafny-lang/DafnyStandardLibGo => ../StandardLibrary/ + +replace github.com/aws/aws-encryption-sdk => ./../esdk + +require ( + github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 + github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 + github.com/aws/aws-encryption-sdk v0.0.0-00010101000000-000000000000 + github.com/aws/aws-sdk-go-v2/config v1.27.37 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1 + github.com/aws/aws-sdk-go-v2/credentials v1.17.35 + github.com/aws/aws-sdk-go-v2/service/kms v1.36.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 +) + +require ( + github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 // indirect + github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // 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.11.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.23.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 // indirect + github.com/aws/smithy-go v1.21.0 // indirect + github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 // indirect + github.com/dafny-lang/DafnyStandardLibGo v0.0.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect +) diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum b/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum new file mode 100644 index 000000000..55861093d --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum @@ -0,0 +1,48 @@ +github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= +github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= +github.com/aws/aws-sdk-go-v2/config v1.27.37 h1:xaoIwzHVuRWRHFI0jhgEdEGc8xE1l91KaeRDsWEIncU= +github.com/aws/aws-sdk-go-v2/config v1.27.37/go.mod h1:S2e3ax9/8KnMSyRVNd3sWTKs+1clJ2f1U6nE0lpvQRg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.35 h1:7QknrZhYySEB1lEXJxGAmuD5sWwys5ZXNr4m5oEz0IE= +github.com/aws/aws-sdk-go-v2/credentials v1.17.35/go.mod h1:8Vy4kk7at4aPSmibr7K+nLTzG6qUQAUO4tW49fzUV4E= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= +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.35.1 h1:DDN8yqYzFUDy2W5zk3tLQNKaO/1t0h3fNixPJacu264= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1/go.mod h1:k5XW8MoMxsNZ20RJmsokakvENUwQyjv69R9GqrI4xdQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 h1:dOxqOlOEa2e2heC/74+ZzcJOa27+F1aXFZpYgY/4QfA= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19/go.mod h1:aV6U1beLFvk3qAgognjS3wnGGoDId8hlPEiBsLHXVZE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg= +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.23.1 h1:2jrVsMHqdLD1+PA4BA6Nh1eZp0Gsy3mFSB5MxDvcJtU= +github.com/aws/aws-sdk-go-v2/service/sso v1.23.1/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 h1:0L7yGCg3Hb3YQqnSgBTZM5wepougtL1aEccdcdYhHME= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 h1:8K0UNOkZiK9Uh3HIF6Bx0rcNCftqGCeKmOaR7Gp5BSo= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.1/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= +github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= +github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 h1:dOgaw3i0I9nWKPjfXYzEfgWsVRJykL6FA18DErvQiJQ= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1/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/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go new file mode 100644 index 000000000..e3ccfbbd2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go @@ -0,0 +1,189 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Discovery Keyring +AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. +This example also includes some sanity checks for demonstration: + 1. Ciphertext and plaintext data are not the same + 2. Decrypted plaintext value matches exampleText + 3. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to + which the KMS key used for encryption belongs +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsdiscoverykeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsDiscoveryKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + // So, we create two keyrings, one for encrypt and another for decrypt + // First Keyring: Create a AwsKmsKeyring to use for encryption + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a Discovery keyring to use for decryption. + // We'll add a discovery filter so that we limit + // the set of ciphertexts we are willing to decrypt to only ones created by KMS keys in our account and + // partition. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsDiscoveryKeyringInput := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ + KmsClient: kmsClient, + DiscoveryFilter: &discoveryFilter, + } + awsKmsDiscoveryKeyring, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate that if a different discovery keyring doesn't have the correct + // AWS Account ID's, the decrypt will fail with an error message + // Note that this assumes Account ID used here ('888888888888') is different than the one used + // during encryption + discoveryFilterFailureCase := mpltypes.DiscoveryFilter{ + AccountIds: []string{"888888888888"}, + Partition: "aws", + } + awsKmsDiscoveryKeyringInputFailureCase := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ + KmsClient: kmsClient, + DiscoveryFilter: &discoveryFilterFailureCase, + } + awsKmsDiscoveryKeyringFailureCase, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInputFailureCase) + _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryKeyringFailureCase, + Ciphertext: res.Ciphertext, + }) + // We expected error in failure case + if err == nil { + panic("Expected failure case to fail") + } + fmt.Println("AWS KMS Discovery Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go new file mode 100644 index 000000000..d9682072c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go @@ -0,0 +1,169 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption +using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi +keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsdiscoverymultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsDiscoveryMultiKeyringExample( + exampleText string, + defaultKmsKeyId string, + defaultKMSKeyAccountID string, + regions []string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + // So, we create two keyrings, one for encrypt and another for decrypt + // First Keyring: Create a AwsKmsKeyring to use for encryption + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a Discovery keyring to use for decryption. + // We'll add a discovery filter so that we limit the set of ciphertexts we are willing to + // decrypt to only ones created by KMS keys in our account and partition. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsDiscoveryMultiKeyringInput{ + Regions: regions, + DiscoveryFilter: &discoveryFilter, + } + awsKmsDiscoveryMultiKeyring, err := matProv.CreateAwsKmsDiscoveryMultiKeyring(context.Background(), awsKmsDiscoveryMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err := validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same.") + } + fmt.Println("AWS KMS Discovery Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go new file mode 100644 index 000000000..024598d8c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go @@ -0,0 +1,296 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" + keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these + branch keys are themselves protected by a KMS Key. + + Establishing a key hierarchy like this has two benefits: + First, by caching the branch key material, and only calling KMS to re-establish authentication + regularly according to your configured TTL, you limit how often you need to call KMS to protect + your data. This is a performance security tradeoff, where your authentication, audit, and logging + from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail + cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has + Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, + the benefit is that you no longer have to make a network call to KMS for every encrypt or + decrypt. + + Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a + multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect + the tenant's data. You can either statically configure a single branch key to ensure you are + restricting access to a single tenant, or you can implement an interface that selects the Branch + Key based on the Encryption Context. + + This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to + encrypt and decrypt data for two separate tenants. + + This example requires access to the DDB Table where you are storing the Branch Keys. 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). + + This example also requires using a KMS Key. You need the following access on this key: + - GenerateDataKeyWithoutPlaintext + - Decrypt + + For more information on how to use Hierarchical Keyrings, see + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-hierarchical-keyring.html +*/ + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "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/kms" +) + +func AwsKmsHKeyExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { + // Step 1: Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + // Step 1a: Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Step 1b: Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the keystore to manage the tenant keys + // This SHOULD be the same configuration that you used + // to initially create and populate your KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: keyStoreKMSKeyID, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + panic(err) + } + // Step 3: Create two new branch keys + branchKeyA, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + branchKeyB, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + // Step 4: Create a branch key supplier that maps the branch key id to a more readable format + // See branchkeysupplier.go in this package for the branchKeySupplier structure + keySupplier := branchKeySupplier{branchKeyA: branchKeyA, branchKeyB: branchKeyB} + // Step 5: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 6: Create the Hierarchical Keyring. + hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyIdSupplier: &keySupplier, + TtlSeconds: 600, + } + hKeyRing, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) + if err != nil { + panic(err) + } + // Step 7: Create encryption context for both tenants. + // The Branch Key Id supplier uses the encryption context to determine which branch key id will + // be used to encrypt data. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + + // Create encryption context for TenantA + encryptionContextA := map[string]string{ + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Create encryption context for TenantB + encryptionContextB := map[string]string{ + "tenant": "TenantB", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 8a: Encrypt the data + // Encrypt data for Tenant A + encryptOutputA, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContextA, + Keyring: hKeyRing, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutputA.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Encrypt data for Tenant B + encryptOutputB, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContextB, + Keyring: hKeyRing, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutputB.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 8b: Decrypt the data with various scenerios for demonstration + + // For demonstration, let's attest that TenantKeyB cannot decrypt a message written by TenantKeyA, + // and vice versa and construct more restrictive hierarchical keyrings. + hkeyringInputA := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyId: &branchKeyA, + TtlSeconds: 600, + } + hKeyRingA, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputA) + if err != nil { + panic(err) + } + hkeyringInputB := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyId: &branchKeyB, + TtlSeconds: 600, + } + hKeyRingB, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputB) + if err != nil { + panic(err) + } + // Demonstrate that data encrypted by one tenant's key + // cannot be decrypted with by a keyring specific to another tenant. + + // Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key + // This will fail and raise a AwsCryptographicMaterialProvidersException, + // which we swallow ONLY for demonstration purposes. + _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputA.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingB, + }) + if err == nil { + panic("Expected error did not occur") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + // Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. + // This will fail and raise a AwsCryptographicMaterialProvidersException, + // which we swallow ONLY for demonstration purposes. + _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputB.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingA, + }) + if err == nil { + panic("Expected error did not occur") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + // Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, + // and that the decrypted data matches the input data. + + // For tenant A + decryptOutputA, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputA.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingA, + }) + if err != nil { + panic(err) + } + // If you are not specifying the encryption context on decrypt. Its recommended to check if the encryption context matches. + // Although, we are specifying the encryption context on decrypt, only for demonstration we are validating the encryption context. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContextA, decryptOutputA.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputA.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // For tenant B + decryptOutputB, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputB.Ciphertext, + EncryptionContext: encryptionContextB, + Keyring: hKeyRingB, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContextB, decryptOutputB.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputB.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputA.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Aws Kms Hierarchical Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go new file mode 100644 index 000000000..fa066fd70 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go @@ -0,0 +1,45 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "fmt" + + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" +) + +/* +Demonstrates how to create a BranchKeyIdSupplier. + +The BranchKeyIdSupplier determines which Branch Key is used +to protect or access data. +It is an important component in a Multi-tenant solution, +where each tenant is cryptographically isolated. +The Branch Key ID Supplier uses the Encryption Context +provided at Encrypt or Decrypt +to determine what "shared secret" (Branch Key) +is used. +*/ + +type branchKeySupplier struct { + branchKeyA string + branchKeyB string +} + +func (b *branchKeySupplier) GetBranchKeyId(input mpltypes.GetBranchKeyIdInput) (*mpltypes.GetBranchKeyIdOutput, error) { + // We MUST use the encryption context to determine + // the Branch Key ID. + ec := input.EncryptionContext + if value, exists := ec["tenant"]; !exists || value == "" { + return nil, fmt.Errorf("EncryptionContext invalid, does not contain expected tenant key value pair.") + } + branchKeyIdentifier := ec["tenant"] + if branchKeyIdentifier == "TenantA" { + return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyA}, nil + } else if branchKeyIdentifier == "TenantB" { + return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyB}, nil + } else { + return &mpltypes.GetBranchKeyIdOutput{}, fmt.Errorf("unknown branch key identifier") + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go new file mode 100644 index 000000000..f236e9ac4 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go @@ -0,0 +1,45 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "context" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +/* + The Hierarchical Keyring Example relies on the existence + of a DDB-backed key store with pre-existing + branch key material. + + This example demonstrates configuring a KeyStore and creating a branch key. +*/ + +func createbranchkeyid(keyStoreTableName, logicalKeyStoreName, kmsKeyArn string, ddbClient *dynamodb.Client, kmsClient *kms.Client) (string, error) { + // 1. Create the keystore + // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: kmsKeyArn, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreTableName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + return "", err + } + // 2. Create a branch key identifier with the AWS KMS Key configured in the KeyStore Configuration. + branchKey, err := keyStore.CreateKey(context.Background(), keystoretypes.CreateKeyInput{}) + if err != nil { + return "", err + } + return branchKey.BranchKeyIdentifier, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go new file mode 100644 index 000000000..2aead2ccb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go @@ -0,0 +1,228 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example demonstrates how to use a shared cache across multiple Hierarchical Keyrings. + With this functionality, users only need to maintain one common shared cache across multiple + Hierarchical Keyrings with different Key Stores instances/KMS Clients/KMS Keys. + + If you want to use a Shared Cache, you need to initialize it only once, and + pass the same cache `shared_cache` to different hierarchical keyrings. + + There are three important parameters that users need to carefully set while providing the shared cache: + + 1. Partition ID - Partition ID is an optional parameter provided to the Hierarchical Keyring input, + which distinguishes Cryptographic Material Providers (i.e: Keyrings) writing to a cache. + - If the Partition ID is set and is the same for two Hierarchical Keyrings (or another Material Provider), + they CAN share the same cache entries in the cache. + - If the Partition ID is set and is different for two Hierarchical Keyrings (or another Material Provider), + they CANNOT share the same cache entries in the cache. + - If the Partition ID is not set by the user, it is initialized as a random 16-byte UUID which makes + it unique for every Hierarchical Keyring, and two Hierarchical Keyrings (or another Material Provider) + CANNOT share the same cache entries in the cache. + + 2. Logical Key Store Name - This parameter is set by the user when configuring the Key Store for + the Hierarchical Keyring. This is a logical name for the branch key store. + Suppose you have a physical Key Store (K). You create two instances of K (K1 and K2). Now, you create + two Hierarchical Keyrings (HK1 and HK2) with these Key Store instances (K1 and K2 respectively). + - If you want to share cache entries across these two keyrings, you should set the Logical Key Store Names + for both the Key Store instances (K1 and K2) to be the same. + - If you set the Logical Key Store Names for K1 and K2 to be different, HK1 (which uses Key Store instance K1) + and HK2 (which uses Key Store instance K2) will NOT be able to share cache entries. + + 3. Branch Key ID - Choose an effective Branch Key ID Schema + + This is demonstrated in the example below. + Notice that both K1 and K2 are instances of the same physical Key Store (K). + You MUST NEVER have two different physical Key Stores with the same Logical Key Store Name. + + Important Note: If you have two or more Hierarchy Keyrings with: + - Same Partition ID + - Same Logical Key Store Name of the Key Store for the Hierarchical Keyring + - Same Branch Key ID + then they WILL share the cache entries in the Shared Cache. + Please make sure that you set all of Partition ID, Logical Key Store Name and Branch Key ID + to be the same for two Hierarchical Keyrings if and only if you want them to share cache entries. + + This example first creates a shared cache that you can use across multiple Hierarchical Keyrings. + The example then configures a Hierarchical Keyring (HK1 and HK2) with the shared cache, + a Branch Key ID and two instances (K1 and K2) of the same physical Key Store (K) respectively, + i.e. HK1 with K1 and HK2 with K2. The example demonstrates that if you set the same Partition ID + for HK1 and HK2, the two keyrings can share cache entries. + If you set different Partition ID of the Hierarchical Keyrings, or different + Logical Key Store Names of the Key Store instances, then the keyrings will NOT + be able to share cache entries. + + This example requires access to the DDB Table (K) where you are storing the Branch Keys. 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) + + This example also requires using a KMS Key. You need the following access on this key: + - GenerateDataKeyWithoutPlaintext + - Decrypt +*/ + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "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/kms" +) + +func SharedCacheExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { + // Step 1: Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + fmt.Println(err) + panic(err) + } + // Step 1a: Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Step 1b: Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the CryptographicMaterialsCache (CMC) to share across multiple Hierarchical Keyrings + // using the Material Providers Library + // This CMC takes in: + // - CacheType + cache := mpltypes.CacheTypeMemberDefault{ + Value: mpltypes.DefaultCache{ + EntryCapacity: 100, + }, + } + cmcCacheInput := mpltypes.CreateCryptographicMaterialsCacheInput{ + Cache: &cache, + } + sharedCryptographicMaterialsCache, err := matProv.CreateCryptographicMaterialsCache(context.Background(), cmcCacheInput) + if err != nil { + panic(err) + } + // Step 4: Create a CacheType object for the sharedCryptographicMaterialsCache + // Note that the `cache` parameter in the Hierarchical Keyring Input takes a `CacheType` as input + // Here, we pass a `Shared` CacheType that passes an already initialized shared cache. + + // If you want to use a Shared Cache, you need to initialize it only once, and + // pass the same cache `shared_cache` to different hierarchical keyrings. + + // CryptographicMaterialsCacheRef is an Rc (Reference Counted), so if you clone it to + // pass it to different Hierarchical Keyrings, it will still point to the same + // underlying cache, and increment the reference count accordingly. + shared_cache := mpltypes.CacheTypeMemberShared{sharedCryptographicMaterialsCache} + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Configure your Key Store resource keyStore1. + // This SHOULD be the same configuration that you used + // to initially create and populate your physical Key Store. + // Note that key_store_table_name is the physical Key Store, + // and key_store1 is instances of this physical Key Store. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: keyStoreKMSKeyID, + } + keyStore1, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + panic(err) + } + // Step 6: Call create_branch_key_id to create one new branch key + branchKeyId, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + // Step 7: Create the Hierarchical Keyring HK1 with Key Store instance K1, partition_id, + // the shared_cache and the branch_key_id. + // Note that we are now providing an already initialized shared cache instead of just mentioning + // the cache type and the Hierarchical Keyring initializing a cache at initialization. + // partition_id for this example is a random UUID + partitionId := "91c1b6a2-6fc3-4539-ad5e-938d597ed730" + // Please make sure that you read the guidance on how to set Partition ID, Logical Key Store Name and + // Branch Key ID at the top of this example before creating Hierarchical Keyrings with a Shared Cache + hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore1, + BranchKeyId: &branchKeyId, + TtlSeconds: 600, + Cache: &shared_cache, + PartitionId: &partitionId, + } + keyring1, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) + // Step 8: Create encryption context for both tenants. + // The Branch Key Id supplier uses the encryption context to determine which branch key id will + // be used to encrypt data. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + + // Create encryption context for TenantA + encryptionContext := map[string]string{ + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 9: Encrypt the data for encryptionContext using keyring1 + encryptOutput, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContext, + Keyring: keyring1, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutput.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 10: Decrypt your encrypted data using the same keyring HK1 you used on encrypt. + decryptOutput, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutput.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: keyring1, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Shared Cache Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go new file mode 100644 index 000000000..f12f48715 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go @@ -0,0 +1,93 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + "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/kms" +) + +/* +This example demonstrates configuring a KeyStore and then +uses a helper method to version a branch key. +*/ +func versionBranchKeyId(keyStoreTableName, logicalKeyStoreName, kmsKeyArn, branchKeyId string) error { + // Load the AWS SDK configuration + cfg, err := config.LoadDefaultConfig(context.Background()) + if err != nil { + return err + } + // Create DDB and KMS clients + ddbClient := dynamodb.NewFromConfig(cfg) + kmsClient := kms.NewFromConfig(cfg) + // Create the keystore + // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: kmsKeyArn, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreTableName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + return err + } + // To version a branch key you MUST have access to kms:ReEncrypt* and kms:GenerateDataKeyWithoutPlaintext + _, err = keyStore.VersionKey(context.Background(), keystoretypes.VersionKeyInput{ + BranchKeyIdentifier: branchKeyId, + }) + if err != nil { + return err + } + return nil +} + +// Function to test versionBranchKeyId in main.go in examples directory +func CreateAndVersionBranchKeyId(keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) error { + // Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + // Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // create branch key ID + branchKeyId, err := createbranchkeyid( + keyStoreName, + logicalKeyStoreName, + keyStoreKMSKeyID, + ddbClient, + kmsClient, + ) + if err != nil { + panic(err) + } + // Version Branch Key + err = versionBranchKeyId( + keyStoreName, + logicalKeyStoreName, + keyStoreKMSKeyID, + branchKeyId, + ) + if err != nil { + panic(err) + } + fmt.Println("Create and version branchKey Id Example Completed Successfully") + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go new file mode 100644 index 000000000..246b8d9ac --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go @@ -0,0 +1,122 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Keyring +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmskeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go new file mode 100644 index 000000000..6822bbcfc --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go @@ -0,0 +1,172 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Keyring +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkdiscoverykeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkDiscoveryKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Though this example highlights Discovery keyrings, Discovery keyrings + // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. + // So, we create two keyrings. One for encryption, second one for decryption + // First Keyring: Create KMS MRK Keyring used for encryption + awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientEncrypt, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) + if err != nil { + panic(err) + } + // Second Keyring: create a Discovery keyring to use for decryption. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + // In order to illustrate the MRK behavior of this keyring, we configure + // the keyring to use the second KMS region where the MRK is replicated to. + // This example assumes you have already replicated your key, but since we + // are using a discovery keyring, we don't need to provide the mrk replica key id + awsKmsMrkDiscoveryInput := mpltypes.CreateAwsKmsMrkDiscoveryKeyringInput{ + KmsClient: kmsClientDecrypt, + Region: alternateRegionMrkKeyRegion, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryKeyring, err := matProv.CreateAwsKmsMrkDiscoveryKeyring(context.Background(), awsKmsMrkDiscoveryInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption ARE the same") + } + // Step 6b: Decrypt + // Create a Discovery keyring to use for decryption. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkDiscoveryKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Discovery Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go new file mode 100644 index 000000000..54dbdf17a --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go @@ -0,0 +1,164 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Multi Keyring +AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings. +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkdiscoverymultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkDiscoveryMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, defaultKMSKeyAccountID string, regionsOfMRKKeys []string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Though this example highlights Discovery keyrings, Discovery keyrings + // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. + // So, we create two keyrings. One for encryption, second one for decryption + // First Keyring: Create KMS MRK Keyring used for encryption + awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a MRK Discovery Multi Keyring to use for decryption + // We'll add a discovery filter to limit the set of encrypted data keys + // we are willing to decrypt to only ones created by KMS keys in select + // accounts and the partition `aws`. + // MRK Discovery keyrings also filter encrypted data keys by the region + // the keyring is created with. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + Regions: regionsOfMRKKeys, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsMrkDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Discovery Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go new file mode 100644 index 000000000..814a51328 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go @@ -0,0 +1,152 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Keyring +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkkeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyrings + // Create one keyring for encrypt with KMS client on defaultMRKKeyRegion region + // Create second keyring for decrypt with KMS client on alternateRegionMrkKeyRegion region. + // In order to illustrate the MRK behavior, we are creating two keyrings with two different regions + awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientEncrypt, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyringInputDecrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientDecrypt, + KmsKeyId: alternateRegionMrkKeyArn, + } + awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) + if err != nil { + panic(err) + } + awsKmsMrkKeyringDecrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputDecrypt) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // 1. Decrypt with the same keyring (same region) as encrypt + decryptOutputSameRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputSameRegion.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // 2. Decrypt with different keyring on different region. + decryptOutputDifferentRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringDecrypt, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputDifferentRegion.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputDifferentRegion.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go new file mode 100644 index 000000000..76a09046f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go @@ -0,0 +1,153 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Multi Keyring +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with AWS KMS MRK keys. +The KMS MRK multi-keyring consists of one or more individual keyrings of the +same or different type. The keys can either be regular KMS keys or MRKs. +The effect is like using several keyrings in a series. +This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id +as a child key, and then encrypts a custom input exampleText with an encryption context. +Either KMS Key individually is capable of decrypting data encrypted under this keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +3. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the + MRK (from the multi-keyring used for encryption) copied from the first region into + the second region +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkmultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultKMSKeyId, alternateRegionMrkKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + defaultMrkKey := defaultRegionMrkKeyArn + // Step 3: Create the keyring + awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ + Generator: &defaultMrkKey, + KmsKeyIds: []string{defaultKMSKeyId}, + } + awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutputMulti, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputMulti.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a single AwsKmsMrkKeyring configured with a replica of a MRK from the + // multi-keyring used to encrypt the data is also capable of decrypting the data. + awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: alternateRegionMrkKeyArn, + } + awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) + if err != nil { + panic(err) + } + decryptOutputMrk, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputMrk.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputMrk.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go new file mode 100644 index 000000000..ea6710deb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go @@ -0,0 +1,172 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, + and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and +another KMS keyring as a child keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsmultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMultiKeyringExample(exampleText, defaultKMSKeyId, alternateRegionKMSKeyId, alternateRegionKMSKeyRegion string) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. + // Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. + generatorKeyId := defaultKMSKeyId + awsKmsMultiKeyringInput := mpltypes.CreateAwsKmsMultiKeyringInput{ + Generator: &generatorKeyId, + KmsKeyIds: []string{alternateRegionKMSKeyId}, + } + awsKmsMultiKeyring, err := matProv.CreateAwsKmsMultiKeyring(context.Background(), awsKmsMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a single AwsKmsKeyring configured with either KMS key + // is also capable of decrypting the data. + // Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionKMSKeyRegion + }) + // Create a single AwsKmsKeyring with the KMS key from our second region. + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: alternateRegionKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + decryptOutputKmsKeyring, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputKmsKeyring.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputKmsKeyring.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("KMS Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go new file mode 100644 index 000000000..4d6ba051b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go @@ -0,0 +1,127 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS RSA Keyring +This example creates a KMS RSA Keyring and then encrypts a custom input +exampleText with an encryption context. +This example also includes some sanity checks for demonstration: + 1. Ciphertext and plaintext data are not the same + 2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +# For more information on how to use KMS keyrings, see +# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsrsakeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" + kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types" +) + +func AwsKmsRsaExample(exampleText string, kmsRsaKeyID string, kmsRSAPublicKey []byte) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsRSAKeyringInput := mpltypes.CreateAwsKmsRsaKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: kmsRsaKeyID, + PublicKey: kmsRSAPublicKey, + EncryptionAlgorithm: kmstypes.EncryptionAlgorithmSpecRsaesOaepSha256, + } + awsKmsRSAKeyring, err := matProv.CreateAwsKmsRsaKeyring(context.Background(), awsKmsRSAKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsRSAKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsRSAKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS RSA Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go new file mode 100644 index 000000000..f580c895c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go @@ -0,0 +1,219 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the KMS ECDH Discovery Keyring. + +This example takes in the recipient's KMS ECC key ARN. +This example attempts to decrypt a ciphertext using the kmsEcdhKeyIdP256RecipientKeyId, +it does so by checking if the message header contains the recipient's public key. + +This example also requires access to a KMS ECC key. +Our tests provide a KMS ECC Key ARN that anyone can use, but you +can also provide your own KMS ECC key. +To use your own KMS ECC key, you must have: + - kms:GetPublicKey permissions on that key. +This example will call kms:GetPublicKey on keyring creation. +You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. + +This example creates a KMS ECDH Discovery Keyring and then decrypts a ciphertext. +For getting the ciphertext, we create a KMS ECDH keyring without discovery +because kms_ecdh_discovery_keyring cannot encrypt data. +This example also includes some sanity checks for demonstration: +1. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsEcdhDiscoveryKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the KMS ECDH keyring. + // This keyring uses the KmsPublicKeyDiscovery configuration. + // On encrypt, the keyring will fail as it is not allowed to encrypt data under this configuration. + // On decrypt, the keyring will check if its corresponding public key is stored in the message header. It + // will call AWS KMS to derive the shared from the recipient's KMS ECC Key ARN and the sender's public key; + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery + // This keyring takes in: + // - kmsClient + // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + kmsEcdhDiscoveryStaticConfigurationInput := mpltypes.KmsPublicKeyDiscoveryInput{ + RecipientKmsIdentifier: kmsEcdhKeyIdP256RecipientKeyId, + } + kmsEcdhDiscoveryStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPublicKeyDiscovery{ + Value: kmsEcdhDiscoveryStaticConfigurationInput, + } + awsKmsEcdhDiscoveryKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhDiscoveryStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhDiscoveryKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhDiscoveryKeyringInput) + if err != nil { + panic(err) + } + // Step 6: Get ciphertext by creating a KMS ECDH keyring WITHOUT discovery + // because the KMS ECDH keyring WITH discovery CANNOT encrypt data. + // We are generating a message intended for the kmsEcdhKeyIdP256RecipientKeyId recipient. + // Since a KMS ECDH keyring WITHOUT discovery cannot encrypt data, this example will ONLY decrypt + // messages where the configured key on the Discovery keyring is present on the message ciphertext. + // In this example we call `kms:GetPublicKey` to get the public key associated with the + // kmsEcdhKeyIdP256RecipientKeyId KMS key ID. + // If the message contains this public key, message decryption will be attempted. + cipherText := getCipherTextKmsEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, kmsClient, kmsEcdhKeyIdP256RecipientKeyId, kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsEccPublicKeyFileNameRecipient) + + // Step 7: Decrypt your encrypted data using the keyring with discovery behavior we created in step 5. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsEcdhDiscoveryKeyring, + EncryptionContext: encryptionContext, + Ciphertext: cipherText, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("AWS KMS ECDH Discovery Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function creates a AWS KMS ECDH keyring and encrypt the exampleText +func getCipherTextKmsEcdh( + matProv *mpl.Client, + encryptionClient *client.Client, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + exampleText string, + encryptionContext map[string]string, + kmsClient *kms.Client, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) []byte { + // 1. Create the public key files for sender and recipient + // You may provide your own ECC keys. + // If not, this class will call the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. + // Sender ECC key used in this example is retrieved with kmsEcdhKeyIdP256SenderKeyId + // Recipent ECC key used in this example is retrieved with kmsEcdhKeyIdP256RecipientKeyId + if !utils.FileExists(kmsEccPublicKeyFileNameSender) { + err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) + if err != nil { + panic(err) + } + } + if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { + err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) + if err != nil { + panic(err) + } + } + // 2. Load public key from UTF-8 encoded PEM files into a DER encoded public key. + publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // 3. Create the KmsPrivateKeyToStaticPublicKeyInput and kmsEcdhStaticConfiguration + kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, + SenderPublicKey: publicKeySender, + } + kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ + Value: kmsEcdhStaticConfigurationInput, + } + // 4. Create the KMS ECDH keyring. + awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) + if err != nil { + panic(err) + } + // 5. Encrypt the data with the encryption_context + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // 6. Return the ciphertext + return res.Ciphertext +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go new file mode 100644 index 000000000..d30cbe539 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go @@ -0,0 +1,193 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the KMS ECDH Keyring. + +This example takes in the sender's KMS ECC key ARN, the sender's public key, +the recipient's public key, and the algorithm definition where the ECC keys lie. + +Both public keys MUST be UTF8 PEM-encoded X.509 public key, +also known as SubjectPublicKeyInfo (SPKI), + +This keyring, depending on its KeyAgreement scheme, +takes in the sender's KMS ECC Key ARN, and the recipient's ECC Public Key +to derive a shared secret. +The keyring uses the shared secret to derive a data key to protect the +data keys that encrypt and decrypt exampletext. + +This example also requires access to a KMS ECC key. +Our tests provide a KMS ECC Key ARN that you need permissions to, but you +can also provide your own KMS ECC key. +To use your own KMS ECC key, you must have either: +- Its public key downloaded in a UTF-8 encoded PEM file +- kms:GetPublicKey permissions on that key. +If you do not have the public key downloaded, running this example +through its main method will download the public key for you +by calling kms:GetPublicKey. +You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. +This example also requires a recipient ECC Public Key that lies on the same +curve as the sender public key. This examples uses another distinct +KMS ECC Public Key, it does not have to be a KMS key; it can be a +valid SubjectPublicKeyInfo (SPKI) Public Key. + +This example creates a KMS ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsEcdhKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Load public key from UTF-8 encoded PEM files into a DER encoded public key. + // You may provide your own ECC keys. + // If not, this class will call the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. + if !utils.FileExists(kmsEccPublicKeyFileNameSender) { + err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) + if err != nil { + panic(err) + } + } + if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { + err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) + if err != nil { + panic(err) + } + } + publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 3: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6: Create the KMS ECDH keyring. + // This keyring uses the KmsPrivateKeyToStaticPublicKey configuration. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key. + // For this example, on decrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key; + // however, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create + // This keyring takes in: + // - kmsClient + // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, + SenderPublicKey: publicKeySender, + } + kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ + Value: kmsEcdhStaticConfigurationInput, + } + awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) + if err != nil { + panic(err) + } + // Step 7a: Encrypt the data + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // Step 7b: Decrypt the data + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("AWS KMS ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go new file mode 100644 index 000000000..781da18ac --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go @@ -0,0 +1,137 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Ephemeral Raw ECDH Keyring. + +This example takes in the recipient's public key located at +eccPublicKeyFileNameRecipient as a +UTF8 PEM-encoded X.509 public key, +and the Curve Specification where the key lies. + +This example loads ECC keys from PEM files with paths defined in + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme. +This configuration will always create a new key pair as the sender key pair for the key agreement operation. +The ephemeral configuration can only encrypt data and CANNOT decrypt messages. + +This example creates an Ephemeral Raw ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func EphemeralRawECDHKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string) { + // Step 1: Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + } + ephemeralRawECDHStaticConfiguration := + mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ + Value: ephemeralRawEcdhStaticConfigurationInput, + } + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, + } + ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Step 6: Encrypt + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: ecdhKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + // (This is an example for demonstration; you do not need to do this in your own code.) + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + fmt.Println("Ephemeral Raw ECDH Keyring Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go new file mode 100644 index 000000000..607738ad2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go @@ -0,0 +1,218 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Public Key Discovery Raw ECDH Keyring. + +A public key discovery Raw ECDH Keyring takes in the recipient's private key located +at eccPrivateKeyFileNameRecipient +as a UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures) private key, +and the Curve Specification where the key lies. + +If you provide the eccPrivateKeyFileNameRecipient, make sure to also +provide the recipient's public key located at eccPublicKeyFileNameRecipient +in the directory that you run this example. Even though the Public Key Discovery Raw ECDH keyring +uses the eccPrivateKeyFileNameRecipient to decrypt the data, +the eccPublicKeyFileNameRecipient is needed to generate the ciphertext to decrypt. + +This example loads ECC keys from PEM files and the ciphertext with paths defined in + - eccPrivateKeyFileNameRecipient + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFilenameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This example creates a RawECDH keyring with the PublicKeyDiscovery key agreement scheme. +This scheme is only available on decrypt. + +This example creates a Public Key Discovery Raw ECDH Keyring and takes in a ciphertext to decrypt it. +This example also includes some sanity checks for demonstration: +1. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-PublicKeyDiscovery +*/ + +package ecdh + +import ( + "context" + "fmt" + "os" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func PublicKeyRawEcdhDiscoveryKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string, + eccPrivateKeyFileNameRecipient string) { + // Step 1: Generate Raw ECDH ECC keys and load the recipient's private key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + privateKeyRecipient, err := os.ReadFile(eccPrivateKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the Public Key Discovery Raw ECDH keyring. + // Create the keyring. + // This keyring uses a discovery configuration. This configuration will check on decrypt + // if it is meant to decrypt the message by checking if the configured public key is stored on the message. + // The discovery configuration can only decrypt messages and CANNOT encrypt messages. + discoveryRawEcdhStaticConfigurationInput := mpltypes.PublicKeyDiscoveryInput{ + RecipientStaticPrivateKey: privateKeyRecipient, + } + discoveryRawEcdhStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberPublicKeyDiscovery{ + Value: discoveryRawEcdhStaticConfigurationInput, + } + discoveryRawEcdhKeyringInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: discoveryRawEcdhStaticConfiguration, + } + discoveryRawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), discoveryRawEcdhKeyringInput) + if err != nil { + panic(err) + } + // Step 6a: Get the ciphertext + // Although this example highlights Public Key Discovery Raw ECDH Keyring keyring, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a Ephemeral Raw ECDH keyring without discovery mode. + cipherText := getCipherTextRawEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, eccPublicKeyFileNameRecipient) + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: discoveryRawEcdhKeyring, + EncryptionContext: encryptionContext, + Ciphertext: cipherText, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("Public Key Discovery Raw ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function creates a Ephemeral Raw ECDH keyring and encrypt the exampleText +func getCipherTextRawEcdh( + matProv *mpl.Client, + encryptionClient *client.Client, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + exampleText string, + encryptionContext map[string]string, + eccPublicKeyFileNameRecipient string) []byte { + // 1. Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + // Load public key from UTF-8 encoded PEM files into a DER encoded public key. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Create the RawEcdhStaticConfigurations + ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + } + ephemeralRawECDHStaticConfiguration := mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ + Value: ephemeralRawEcdhStaticConfigurationInput, + } + // Create the Ephemeral Raw ECDH keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, + } + ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Encrypt the data + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: ecdhKeyring, + }) + if err != nil { + panic(err) + } + return res.Ciphertext +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go new file mode 100644 index 000000000..78d9aed99 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go @@ -0,0 +1,194 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Raw ECDH Keyring. + +This example takes in the sender's private key located at +eccPrivateKeyFileNameSender as a UTF8 PEM-encoded +(PKCS #8 PrivateKeyInfo structures) private key, +and the recipient's public key located at +eccPublicKeyFileNameRecipient as a +UTF8 PEM-encoded X.509 public key, +also known as SubjectPublicKeyInfo (SPKI), +and the Curve Specification where the keys lie. + +This example loads ECC keys from PEM files with paths defined in + - eccPrivateKeyFileNameSender + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +These files will be generated in the directory where the example is run. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This example creates a RawECDH keyring with the RawPrivateKeyToStaticPublicKey key agreement scheme. +On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. +On decrypt, the shared secret is derived from the sender's private key and the recipient's public key; +however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with +the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + +This example creates a Raw ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-RawPrivateKeyToStaticPublicKey +*/ + +package ecdh + +import ( + "context" + "fmt" + "os" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func RawECDHKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string, + eccPrivateKeyFileNameSender string) { + // Step 1: Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) || !utils.FileExists(eccPrivateKeyFileNameSender) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + privateKeySender, err := os.ReadFile(eccPrivateKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the Raw ECDH keyring. + // This keyring uses static sender and recipient keys. This configuration calls for both of + // the keys to be on the same curve (P256 / P384 / P521). + // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + // For this example, on decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + // However, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + RawEcdhStaticConfigurationInput := mpltypes.RawPrivateKeyToStaticPublicKeyInput{ + SenderStaticPrivateKey: privateKeySender, + RecipientPublicKey: publicKeyRecipient, + } + RawECDHStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberRawPrivateKeyToStaticPublicKey{ + Value: RawEcdhStaticConfigurationInput, + } + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: RawECDHStaticConfiguration, + } + rawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Step 6a: Encrypt + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: rawEcdhKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + // (This is an example for demonstration; you do not need to do this in your own code.) + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: rawEcdhKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("Raw ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go new file mode 100644 index 000000000..32d4cc5c7 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go @@ -0,0 +1,233 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Multi Keyring + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring +as a child keyring. You can use different combinations of keyrings in the multi_keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package multikeyring + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func MultiKeyringExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the MultiKeyring that consists of the KMS Keyring as generator and Raw AES Keyring as child keyring + // When using this MultiKeyring to encrypt data, either KMS Keyring or + // Raw AES Keyring (or a MultiKeyring containing either) may be used to decrypt the data + awsKmsKeyring := getKMSKeyring(defaultKMSKeyId, defaultKmsKeyRegion, matProv) + rawAESKeyring := getRawAESKeyring(matProv) + createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ + Generator: awsKmsKeyring, + ChildKeyrings: []mpltypes.IKeyring{rawAESKeyring}, + } + multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that you can also successfully decrypt data using the `rawAESKeyring` directly. + // Because you used a MultiKeyring on Encrypt, you can use either the `kmsKeyring` or + // `rawAESKeyring` individually to decrypt the data. + decryptOutputRawAES, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: rawAESKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputRawAES.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputRawAES.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that you can also successfully decrypt data using the `awsKmsKeyring` directly. + decryptOutputAwsKms, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputAwsKms.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputAwsKms.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Multi Keyring Example Completed Successfully") +} +func getKMSKeyring(kmsKeyId string, kmsRegion string, matProv *mpl.Client) mpltypes.IKeyring { + // 1. Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = kmsRegion + }) + // 2. Create Aws Kms keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: kmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + return awsKmsKeyring +} +func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { + // 1. Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // 2. Create the keyring + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + return aesKeyring +} +func generateAes256KeyBytes() ([]byte, error) { + const keySize = 32 // 256 bits = 32 bytes + key := make([]byte, keySize) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go new file mode 100644 index 000000000..d8ccaa21d --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go @@ -0,0 +1,138 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the Raw AES Keyring +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. +This example creates a Raw AES Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that +you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, +but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring. +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +*/ +package rawaeskeyring + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func RawAesExample(exampleText string) { + // Step 1: Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Raw AES Keyring Example Completed Successfully") +} + +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 +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go new file mode 100644 index 000000000..bf70d31c2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go @@ -0,0 +1,178 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the Raw RSA Keyring +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. +This keyring accepts PEM encodings of the key pair as UTF-8 interpreted bytes. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. +This example generate private and public key pairs. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. +This example creates a Raw RSA Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private +key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, +and you can decrypt data with a Raw RSA keyring that has only a private key. This example requires +the user to either provide both private and public keys, or not provide any keys and the example +generates both to test encryption and decryption. If you configure a Raw RSA keyring with a +public and private key, be sure that they are part of the same key pair. Some language +implementations of the AWS Encryption SDK will not construct a Raw RSA keyring with keys +from different pairs. Others rely on you to verify that your keys are from the same key pair. +You can include any Raw RSA keyring in a multi-keyring. +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +*/ + +package rawrsakeyring + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func RawRsaExample(exampleText string) { + // Step 1: Generate the key-pairs + publicKeyBlock, privateKeyBlock, err := generateKeyPair() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw RSA keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + keyNamespace := "Some managed raw keys" + keyName := "My 2048-bit RSA wrapping key" + rsaKeyRingInput := mpltypes.CreateRawRsaKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + PaddingScheme: mpltypes.PaddingSchemeOaepSha512Mgf1, + PublicKey: pem.EncodeToMemory(publicKeyBlock), + PrivateKey: pem.EncodeToMemory(privateKeyBlock), + } + rsaKeyring, err := matProv.CreateRawRsaKeyring(context.Background(), rsaKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + cryptoClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := cryptoClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: rsaKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // You do not need to specify the encryption context on decrypt + // because the header of the encrypted message includes the encryption context. + decryptOutput, err := cryptoClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + Keyring: rsaKeyring, + EncryptionContext: encryptionContext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Raw RSA Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} + +func generateKeyPair() (*pem.Block, *pem.Block, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + // Extract public key from the private key + publicKey := &privateKey.PublicKey + // Encode public key to PKCS1 DER format + publicKeyDER, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return nil, nil, err + } + privateKeyDer, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, nil, err + } + // Encode to PEM format + publicKeyBlock := &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: publicKeyDER, + } + privateKeyBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyDer, + } + return publicKeyBlock, privateKeyBlock, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go new file mode 100644 index 000000000..c1995f56e --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go @@ -0,0 +1,176 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +Demonstrate limiting the number of Encrypted Data Keys [EDKs] allowed +when encrypting or decrypting a message. +Limiting encrypted data keys is most valuable when you are decrypting +messages from an untrusted source. +By default, the ESDK will allow up to 65,535 encrypted data keys. +A malicious actor might construct an encrypted message with thousands of +encrypted data keys, none of which can be decrypted. +As a result, the AWS Encryption SDK would attempt to decrypt each +encrypted data key until it exhausted the encrypted data keys in the message. + +For more information on limiting EDKs, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-limit-keys +*/ + +package examples + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func LimitEncryptedDataKeyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string, maxEncryptedDataKeys int64) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + // Also, set the EncryptionSDK's MaxEncryptedDataKeys parameter here + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ + MaxEncryptedDataKeys: &maxEncryptedDataKeys, + }) + if err != nil { + panic(err) + } + // Step 3: Generate `maxEncryptedDataKeys` AES keyrings to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + rawAESKeyrings := make([]mpltypes.IKeyring, 0, maxEncryptedDataKeys) + var i int64 = 0 + for i < maxEncryptedDataKeys { + rawAESKeyrings = append(rawAESKeyrings, getRawAESKeyring(matProv)) + i++ + } + // Step 4: Create a Multi Keyring with `maxEncryptedDataKeys` AES Keyrings + createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ + Generator: rawAESKeyrings[0], + ChildKeyrings: rawAESKeyrings[1:], + } + multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that an EncryptionSDK with a lower MaxEncryptedDataKeys + // will fail to decrypt the encrypted message. + // (This is an example for demonstration; you do not need to do this in your own code.) + lowerMaxEncryptedDataKeys := maxEncryptedDataKeys - 1 + encryptionClientIncorrectMaxEncryptedKeys, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ + MaxEncryptedDataKeys: &lowerMaxEncryptedDataKeys, + }) + if err != nil { + panic(err) + } + _, err = encryptionClientIncorrectMaxEncryptedKeys.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err == nil { + panic("Expected error not found.") + } + // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception + switch err.(type) { + case esdktypes.AwsEncryptionSdkException: + // You may choose how to handle the exception in this switch case. + default: + panic("Decryption using lower then max encrypted data keys MUST raise AwsEncryptionSdkException") + } + fmt.Println("Limit Encrypted Data Key Example completed successfully") +} + +func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { + // 1. Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generate256KeyBytesAES() + if err != nil { + panic(err) + } + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // 2. Create the keyring + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + return aesKeyring +} + +func generate256KeyBytesAES() ([]byte, error) { + const keySize = 32 // 256 bits = 32 bytes + key := make([]byte, keySize) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/main.go b/AwsEncryptionSDK/runtimes/go/examples/examples/main.go new file mode 100644 index 000000000..88162aa03 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/main.go @@ -0,0 +1,157 @@ +package main + +import ( + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/clientsupplier" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/cryptographicmaterialsmanager/requiredencryptioncontext" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/cryptographicmaterialsmanager/restrictalgorithmsuite" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsdiscoverykeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsdiscoverymultikeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmshierarchicalkeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmskeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkdiscoverykeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkdiscoverymultikeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkkeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkmultikeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmultikeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsrsakeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/ecdh" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/multikeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/rawaeskeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/rawrsakeyring" + "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" +) + +func main() { + const stringToEncrypt = "Text To encrypt" + clientsupplier.ClientSupplierExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultKMSKeyAccountID(), + []string{"eu-west-1"}) + examples.CommitmentPolicyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + examples.SetEncryptionAlgorithmSuiteExample(stringToEncrypt) + var maxEncryptedDataKeys int64 = 3 + examples.LimitEncryptedDataKeyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion(), + maxEncryptedDataKeys) + requiredencryptioncontext.RequiredEncryptionContextExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + restrictalgorithmsuite.SigningOnlyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + // keyrings + ecdh.PublicKeyRawEcdhDiscoveryKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient(), + utils.GetEccPrivateKeyFileNameRecipient()) + ecdh.EphemeralRawECDHKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient()) + ecdh.RawECDHKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient(), + utils.GetEccPrivateKeyFileNameSender()) + ecdh.AwsKmsEcdhKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetKmsEcdhKeyIdP256RecipientKeyId(), + utils.GetKmsEcdhKeyIdP256SenderKeyId(), + utils.GetKmsEccPublicKeyFileNameSender(), + utils.GetKmsEccPublicKeyFileNameRecipient()) + ecdh.AwsKmsEcdhDiscoveryKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetKmsEcdhKeyIdP256RecipientKeyId(), + utils.GetKmsEcdhKeyIdP256SenderKeyId(), + utils.GetKmsEccPublicKeyFileNameSender(), + utils.GetKmsEccPublicKeyFileNameRecipient()) + awskmskeyring.AwsKmsKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID()) + awskmsrsakeyring.AwsKmsRsaExample( + stringToEncrypt, + utils.GetTestKmsRsaKeyID(), + utils.GetKmsRSAPublicKey()) + awskmsmultikeyring.AwsKmsMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetAlternateRegionKMSKeyId(), + utils.GetAlternateRegionKMSKeyRegion()) + awskmsdiscoverykeyring.AwsKmsDiscoveryKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID()) + awskmsdiscoverymultikeyring.AwsKmsDiscoveryMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID(), + utils.GetRegions()) + rawrsakeyring.RawRsaExample(stringToEncrypt) + awskmsmrkkeyring.AwsKmsMrkKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetAlternateRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetAlternateRegionMrkKeyRegion()) + awskmsmrkmultikeyring.AwsKmsMrkMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetAlternateRegionMrkKeyArn(), + utils.GetDefaultKMSKeyId(), + utils.GetAlternateRegionMrkKeyRegion()) + awskmsmrkdiscoverykeyring.AwsKmsMrkDiscoveryKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetAlternateRegionMrkKeyRegion(), + utils.GetDefaultKMSKeyAccountID()) + awskmsmrkdiscoverymultikeyring.AwsKmsMrkDiscoveryMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetDefaultKMSKeyAccountID(), + utils.GetRegionsOfMRKKeys(), + ) + awskmshierarchicalkeyring.AwsKmsHKeyExample( + stringToEncrypt, + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + awskmshierarchicalkeyring.CreateAndVersionBranchKeyId( + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + awskmshierarchicalkeyring.SharedCacheExample( + stringToEncrypt, + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + rawaeskeyring.RawAesExample(stringToEncrypt) + multikeyring.MultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion(), + ) +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go b/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go new file mode 100644 index 000000000..d7e0bc9cb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go @@ -0,0 +1,171 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example demonstrates how to set an algorithm suite while using the Raw AES Keyring +in the AWS Encryption SDK. + +The algorithm suite used in the encrypt() method is the algorithm used to protect your +data using the data key. By setting this algorithm, you can configure the algorithm used +to encrypt and decrypt your data. + +Algorithm suites can be set in a similar manner in other keyrings as well. However, +please make sure that you're using a logical algorithm suite that is compatible with your +keyring. For more information on algorithm suites supported by the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html + +The AES wrapping algorithm (AesWrappingAlg::AlgAes256GcmIv12Tag16) protects your data key using +the user-provided wrapping key. In contrast, the algorithm suite used in the encrypt() method +is the algorithm used to protect your data using the data key. This example demonstrates setting the +latter, which is the algorithm suite for protecting your data. When the commitment policy is +RequireEncryptRequireDecrypt, the default algorithm used in the encrypt method is +AlgAes256GcmHkdfSha512CommitKeyEcdsaP384, which is a committing and signing algorithm. +Signature verification ensures the integrity of a digital message as it goes across trust +boundaries. However, signature verification adds a significant performance cost to encryption +and decryption. If encryptors and decryptors are equally trusted, we can consider using an algorithm +suite that does not include signing. This example sets the algorithm suite as +AlgAes256GcmHkdfSha512CommitKey, which is a committing but non-signing algorithm. +For more information on digital signatures, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + +This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context and the algorithm suite AlgAes256GcmHkdfSha512CommitKey. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +*/ + +package examples + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func SetEncryptionAlgorithmSuiteExample(exampleText string) { + // Step 1: Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // Note: The wrapping algorithm here is NOT the algorithm suite we set in this example. + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + // Here, we customize the Algorithm Suite that is used to Encrypt the plaintext. + // In particular, we use an Algorithm Suite without Signing. + // Signature verification adds a significant performance cost on decryption. + // If the users encrypting data and the users decrypting data are equally trusted, + // consider using an algorithm suite that does not include signing. + // See more about Digital Signatures: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + AlgorithmSuiteId: &algorithmSuiteId, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Set Encryption Algorithm Suite Example Completed Successfully") +} + +func generateAes256KeyBytes() ([]byte, error) { + numOfBytes := 32 // 256 bits = 32 bytes + key := make([]byte, numOfBytes) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go b/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go new file mode 100644 index 000000000..63639b5e6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go @@ -0,0 +1,321 @@ +package utils + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "os" + + "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +const ( + testKmsRsaPublicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ +oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN +/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK +fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2 +r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9 +yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou +twIDAQAB +-----END PUBLIC KEY-----` + testKmsRsaKeyID = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" + testDefaultKMSKeyId = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + defaultKmsKeyRegion = "us-west-2" + testAlternateRegionKMSKeyId = "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" + testAlternateRegionKMSKeyRegion = "eu-central-1" + testDefaultMRKKeyId = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + defaultMRKKeyRegion = "us-east-1" + testAlternateRegionMrkKeyId = "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + alternateRegionMrkKeyRegion = "eu-west-1" + testKeyStoreKMSKeyRegion = "us-west-2" + testKeyStoreKMSKeyID = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + testLogicalKeyStoreName = "KeyStoreDdbTable" + testKeyStoreName = "KeyStoreDdbTable" + testKeyStoreRegion = "us-west-2" + defaultKMSKeyAccountID = "658956600833" + eccPrivateKeyFileNameSender = "sender_private.pem" + eccPrivateKeyFileNameRecipient = "recipient_private.pem" + eccPublicKeyFileNameRecipient = "recipient_public.pem" + kmsEccPublicKeyFileNameRecipient = "KmsEccKeyringExamplePublicKeyRecipient.pem" + kmsEccPublicKeyFileNameSender = "KmsEccKeyringExamplePublicKeySender.pem" + testKmsEcdhKeyIdP256SenderKeyId = "arn:aws:kms:us-west-2:370957321024:key/eabdf483-6be2-4d2d-8ee4-8c2583d416e9" + testKmsEcdhKeyIdP256RecipientKeyId = "arn:aws:kms:us-west-2:370957321024:key/0265c8e9-5b6a-4055-8f70-63719e09fda5" +) + +// Getter functions + +func GetKmsEcdhKeyIdP256SenderKeyId() string { + return testKmsEcdhKeyIdP256SenderKeyId +} + +func GetKmsEcdhKeyIdP256RecipientKeyId() string { + return testKmsEcdhKeyIdP256RecipientKeyId +} + +func GetKmsEccPublicKeyFileNameRecipient() string { + return kmsEccPublicKeyFileNameRecipient +} + +func GetKmsEccPublicKeyFileNameSender() string { + return kmsEccPublicKeyFileNameSender +} + +func GetEccPrivateKeyFileNameSender() string { + return eccPrivateKeyFileNameSender +} + +func GetEccPrivateKeyFileNameRecipient() string { + return eccPrivateKeyFileNameRecipient +} + +func GetEccPublicKeyFileNameRecipient() string { + return eccPublicKeyFileNameRecipient +} + +func GetRegionsOfMRKKeys() []string { + return []string{defaultMRKKeyRegion, alternateRegionMrkKeyRegion} +} + +func GetRegions() []string { + return []string{defaultKmsKeyRegion, testAlternateRegionKMSKeyRegion} +} + +func GetDefaultKmsKeyRegion() string { + return defaultKmsKeyRegion +} + +func GetDefaultMRKKeyRegion() string { + return defaultMRKKeyRegion +} + +func GetAlternateRegionMrkKeyRegion() string { + return alternateRegionMrkKeyRegion +} + +func GetAlternateRegionMrkKeyArn() string { + return testAlternateRegionMrkKeyId +} + +func GetDefaultRegionMrkKeyArn() string { + return testDefaultMRKKeyId +} + +func GetAlternateRegionKMSKeyRegion() string { + return testAlternateRegionKMSKeyRegion +} + +func GetAlternateRegionKMSKeyId() string { + return testAlternateRegionKMSKeyId +} + +func GetDefaultKMSKeyAccountID() string { + return defaultKMSKeyAccountID +} + +func GetDefaultKMSKeyId() string { + return testDefaultKMSKeyId +} + +func GetTestKmsRsaKeyID() string { + return testKmsRsaKeyID +} + +func GetKmsRSAPublicKey() []byte { + return []byte(testKmsRsaPublicKey) +} + +func GetKeyStoreRegion() string { + return testKeyStoreRegion +} + +func GetKeyStoreKMSKeyRegion() string { + return testKeyStoreKMSKeyRegion +} + +func GetKeyStoreKMSKeyID() string { + return testKeyStoreKMSKeyID +} + +func GetLogicalKeyStoreName() string { + return testLogicalKeyStoreName +} + +func GetKeyStoreName() string { + return testKeyStoreName +} + +// Utility functions + +func WriteRawEcdhEccKeys(ecdhCurveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) error { + // Safety check: Validate neither file is present + if FileExists(eccPrivateKeyFileNameSender) || + FileExists(eccPrivateKeyFileNameRecipient) || + FileExists(eccPublicKeyFileNameRecipient) { + return errors.New("WriteRawEcdhEccKeys will not overwrite existing PEM files") + } + + // Generate key pairs + _, privateKeySender, err := generateRawEccKeyPair(ecdhCurveSpec) + if err != nil { + return err + } + + publicKeyRecipient, privateKeyRecipient, err := generateRawEccKeyPair(ecdhCurveSpec) + if err != nil { + return err + } + + // Create PEM blocks + privateKeySenderPEM := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeySender, + } + + privateKeyRecipientPEM := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyRecipient, + } + + publicKeyRecipientPEM := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyRecipient, + } + + // Write private key for sender in PEM format + err = os.WriteFile( + eccPrivateKeyFileNameSender, + pem.EncodeToMemory(privateKeySenderPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write sender's private key: %w", err) + } + + // Write private key for recipient in PEM format + err = os.WriteFile( + eccPrivateKeyFileNameRecipient, + pem.EncodeToMemory(privateKeyRecipientPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write recipient's private key: %w", err) + } + + // Write public key for recipient in PEM format + err = os.WriteFile( + eccPublicKeyFileNameRecipient, + pem.EncodeToMemory(publicKeyRecipientPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write recipient's public key: %w", err) + } + + return nil +} + +func LoadPublicKeyFromPEM(filename string) ([]byte, error) { + // Read the PEM file content as string + pemContent, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read PEM file: %w", err) + } + // Parse PEM block + block, _ := pem.Decode(pemContent) + + if block == nil { + return nil, fmt.Errorf("failed to decode PEM block") + } + + // The block.Bytes contains the DER encoded key + return block.Bytes, nil +} + +func FileExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +func generateRawEccKeyPair(curveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) ([]byte, []byte, error) { + // Select the appropriate elliptic curve based on the specification + var curve elliptic.Curve + switch curveSpec { + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP256: + curve = elliptic.P256() + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP384: + curve = elliptic.P384() + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP521: + curve = elliptic.P521() + default: + return nil, nil, fmt.Errorf("unsupported curve specification: %s", curveSpec) + } + // Generate the private key + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate private key: %w", err) + } + // Extract the public key + publicKey := &privateKey.PublicKey + // Marshal the private key to bytes (X.509 PKCS#8 format) + privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, nil, fmt.Errorf("failed to marshal private key: %w", err) + } + // Marshal the public key to bytes (X.509 SPKI format) + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return nil, nil, fmt.Errorf("failed to marshal public key: %w", err) + } + return publicKeyBytes, privateKeyBytes, nil +} + +func WriteKmsEcdhEccPublicKey(eccKeyArn, publicKeyFileName string, kmsClient *kms.Client) error { + // Safety check: Validate neither file is present + if FileExists(publicKeyFileName) { + return errors.New("WriteKmsEcdhEccPublicKey will not overwrite existing PEM files") + } + // Generate public key + publicKey, err := GenerateKmsEccPublicKey(eccKeyArn, kmsClient) + if err != nil { + return fmt.Errorf("failed to generate public key: %w", err) + } + // Create PEM block + pemBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKey, + } + // Encode PEM + pemData := pem.EncodeToMemory(pemBlock) + if pemData == nil { + return errors.New("failed to encode PEM data") + } + // Write file with proper permissions + err = os.WriteFile(publicKeyFileName, pemData, 0600) + if err != nil { + return fmt.Errorf("failed to write public key file: %w", err) + } + return nil +} + +func GenerateKmsEccPublicKey(eccKeyArn string, kmsClient *kms.Client) ([]byte, error) { + ctx := context.Background() + // Get public key from KMS + response, err := kmsClient.GetPublicKey(ctx, &kms.GetPublicKeyInput{ + KeyId: &eccKeyArn, + }) + if err != nil { + return nil, fmt.Errorf("failed to get public key from KMS: %w", err) + } + // Check if public key is present + if response.PublicKey == nil { + return nil, errors.New("no public key in KMS response") + } + return response.PublicKey, nil +} From c2e03da1f190769fdcd09713c5a37458e0a82404 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 11:49:51 -0800 Subject: [PATCH 02/11] auto commit --- .../clientsupplier/clientSupplierExample.go | 163 +++++++++ .../regionalroleclientsupplier.go | 57 ++++ .../regionalroleclientsupplierconfig.go | 22 ++ .../requiredencryptioncontext.go | 161 +++++++++ .../signingonlyexample.go | 137 ++++++++ .../signingsuiteonlycmm.go | 77 +++++ AwsEncryptionSDK/runtimes/go/examples/go.mod | 43 +++ AwsEncryptionSDK/runtimes/go/examples/go.sum | 48 +++ .../awskmsdiscoverykeyring.go | 189 +++++++++++ .../awskmsdiscoverymultikeyring.go | 169 +++++++++ .../awskmshierarchicalkeyring.go | 296 ++++++++++++++++ .../branchkeysupplier.go | 45 +++ .../createbranchkeyid.go | 45 +++ .../sharedcacheacrosshierarchicalkeyring.go | 228 +++++++++++++ .../versionbranchkeyid.go | 93 +++++ .../keyring/awskmskeyring/awskmskeyring.go | 122 +++++++ .../awskmsmrkdiscoverykeyring.go | 172 ++++++++++ .../awskmsmrkdiscoverymultikeyring.go | 164 +++++++++ .../awskmsmrkkeyring/awskmsmrkkeyring.go | 152 +++++++++ .../awskmsmrkmultikeyring.go | 153 +++++++++ .../awskmsmultikeyring/awskmsmultikeyring.go | 172 ++++++++++ .../awskmsrsakeyring/awskmsrsakeyring.go | 127 +++++++ .../ecdh/awskmsecdhdiscoverykeyring.go | 219 ++++++++++++ .../keyring/ecdh/awskmsecdhkeyring.go | 193 +++++++++++ .../keyring/ecdh/ephemeralrawecdhkeyring.go | 137 ++++++++ .../ecdh/publickeyrawdiscoveryecdhkeyring.go | 218 ++++++++++++ .../examples/keyring/ecdh/rawecdhkeyring.go | 194 +++++++++++ .../keyring/multikeyring/multikeyring.go | 233 +++++++++++++ .../keyring/rawaeskeyring/rawaeskeyring.go | 138 ++++++++ .../keyring/rawrsakeyring/rawrasakeyring.go | 178 ++++++++++ AwsEncryptionSDK/runtimes/go/examples/main.go | 161 +++++++++ .../go/examples/misc/commitmentpolicy.go | 149 ++++++++ .../misc/limitencrypteddatakeysexample.go | 176 ++++++++++ .../misc/setencryptionalgorithmsuite.go | 171 ++++++++++ .../go/examples/utils/exampleUtils.go | 321 ++++++++++++++++++ 35 files changed, 5323 insertions(+) create mode 100644 AwsEncryptionSDK/runtimes/go/examples/clientsupplier/clientSupplierExample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplier.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplierconfig.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/go.mod create mode 100644 AwsEncryptionSDK/runtimes/go/examples/go.sum create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmskeyring/awskmskeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/ephemeralrawecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/rawecdhkeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/multikeyring/multikeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/rawaeskeyring/rawaeskeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/keyring/rawrsakeyring/rawrasakeyring.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/main.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/misc/limitencrypteddatakeysexample.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/misc/setencryptionalgorithmsuite.go create mode 100644 AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go diff --git a/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/clientSupplierExample.go b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/clientSupplierExample.go new file mode 100644 index 000000000..d06cc467e --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/clientSupplierExample.go @@ -0,0 +1,163 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up an MRK multi-keyring and an MRK discovery + multi-keyring using a custom client supplier. + A custom client supplier grants users access to more granular + configuration aspects of their authentication details and KMS + client. In this example, we create a simple custom client supplier + that authenticates with a different IAM role based on the + region of the KMS key. + + This example creates a MRK multi-keyring configured with a custom + client supplier using a single MRK and encrypts the example_data with it. + Then, it creates a MRK discovery multi-keyring to decrypt the ciphertext. +*/ + +package clientsupplier + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func ClientSupplierExample(exampleText, mrkKeyIdEncrypt, awsAccountId string, awsRegions []string) { + // Step 1: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 3: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create keyrings + // First Keyring: Create the multi-keyring using our custom client supplier + // defined in the RegionalRoleClientSupplier class in this directory. + // Note: RegionalRoleClientSupplier will internally use the key_arn's region + // to retrieve the correct IAM role. + awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Generator: &mrkKeyIdEncrypt, + } + awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a MRK discovery multi-keyring with a custom client supplier. + // A discovery MRK multi-keyring will be composed of + // multiple discovery MRK keyrings, one for each region. + // Each component keyring has its own KMS client in a particular region. + // When we provide a client supplier to the multi-keyring, all component + // keyrings will use that client supplier configuration. + // In our tests, we make `mrk_key_id_encrypt` an MRK with a replica, and + // provide only the replica region in our discovery filter. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{awsAccountId}, + Partition: "aws", + } + awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Regions: awsRegions, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + // Decrypt your encrypted data using the discovery multi keyring. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + // is successful. + // Since every member of the Multi Keyring is a Discovery Keyring: + // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. + // For each filtered EDK, the keyring will attempt decryption with the keyring's client. + // All of this is done serially, until a success occurs or all keyrings have failed + // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt + // Multi Region Keys (MRKs) and regular KMS Keys. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Test the Missing Region Exception + // (This is for demonstration; you do not need to do this in your code.) + + // Create a MRK discovery multi-keyring with a custom client supplier and a fake region. + awsKmsMrkDiscoveryMultiKeyringInputMissingRegion := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + ClientSupplier: &RegionalRoleClientSupplier{}, + Regions: []string{"fake-region"}, + DiscoveryFilter: &discoveryFilter, + } + _, err = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInputMissingRegion) + // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("Decryption using discovery keyring with missing region MUST raise AwsCryptographicMaterialProvidersException") + } + fmt.Println("Client Supplier Example completed successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplier.go b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplier.go new file mode 100644 index 000000000..762cdf745 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplier.go @@ -0,0 +1,57 @@ +package clientsupplier + +import ( + "context" + + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials/stscreds" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/sts" +) + +/* + Example class demonstrating an implementation of a custom client supplier. + This particular implementation will create KMS clients with different IAM roles, + depending on the region passed. +*/ + +// RegionalRoleClientSupplier provides implementation for mpltypes.IClientSupplier +type RegionalRoleClientSupplier struct { +} + +func (this *RegionalRoleClientSupplier) GetClient(input mpltypes.GetClientInput) (kms.Client, error) { + region := input.Region + // Check if the region is supported + regionIamRoleMap := RegionIamRoleMap() + var defaultVal kms.Client + // Check if region is supported + if _, exists := regionIamRoleMap[region]; !exists { + return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{ + Message: "Region is not supported by this client supplier", + } + } + // Get the IAM role ARN associated with the region + arn := regionIamRoleMap[region] + ctx := context.TODO() + cfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(region), + ) + if err != nil { + return defaultVal, err + } + stsClient := sts.NewFromConfig(cfg) + // Create the AssumeRoleProvider + provider := stscreds.NewAssumeRoleProvider(stsClient, arn, func(o *stscreds.AssumeRoleOptions) { + o.RoleSessionName = "Go-ESDK-Client-Supplier-Example-Session" + }) + // Load AWS SDK configuration with the AssumeRoleProvider + sdkConfig, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region), config.WithCredentialsProvider(provider)) + if err != nil { + return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{Message: "failed to load AWS SDK config"} + } + // Create the KMS client + kmsClient := kms.NewFromConfig(sdkConfig) + // Return the KMS client wrapped in a custom type + return *kmsClient, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplierconfig.go b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplierconfig.go new file mode 100644 index 000000000..2c82f40fb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/clientsupplier/regionalroleclientsupplierconfig.go @@ -0,0 +1,22 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package clientsupplier + +/* + File containing config for the RegionalRoleClientSupplier. + In your own code, this might be hardcoded, or reference + an external source, e.g. environment variables or AWS AppConfig. +*/ + +const ( + usEast1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-us-east-1-KMS-keys" + euWest1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-eu-west-1-KMS-keys" +) + +func RegionIamRoleMap() map[string]string { + return map[string]string{ + "us-east-1": usEast1IamRole, + "eu-west-1": euWest1IamRole, + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go new file mode 100644 index 000000000..f0f42d548 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go @@ -0,0 +1,161 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. +A required encryption context CMM asks for required keys in the encryption context field +on encrypt such that they will not be stored on the message, but WILL be included in the header signature. +On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. +*/ + +package requiredencryptioncontext + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func RequiredEncryptionContextExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = utils.GetDefaultKmsKeyRegion() + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: utils.GetDefaultKMSKeyId(), + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + // Step 6: Create your required encryption context keys. + // These keys MUST be in your encryption context. + // These keys and their corresponding values WILL NOT be stored on the message but will be used + // for authentication. + underlyingCMM, err := matProv.CreateDefaultCryptographicMaterialsManager(context.Background(), mpltypes.CreateDefaultCryptographicMaterialsManagerInput{Keyring: awsKmsKeyring}) + if err != nil { + panic(err) + } + requiredEncryptionContextKeys := []string{} + requiredEncryptionContextKeys = append(requiredEncryptionContextKeys, "requiredKey1", "requiredKey2") + requiredEncryptionContextInput := mpltypes.CreateRequiredEncryptionContextCMMInput{ + UnderlyingCMM: underlyingCMM, + // If you pass in a keyring but no underlying cmm, it will result in a failure because only cmm is supported. + RequiredEncryptionContextKeys: requiredEncryptionContextKeys, + } + requiredEC, err := matProv.CreateRequiredEncryptionContextCMM(context.Background(), requiredEncryptionContextInput) + if err != nil { + panic(err) + } + // Step 7a: Encrypt + // NOTE: the keys "requiredKey1", and "requiredKey2" + // WILL NOT be stored in the message header, but "encryption", "is not", + // "but adds", "that can help you", and "the data you are handling" WILL be stored. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + MaterialsManager: requiredEC, + EncryptionContext: encryptionContext, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 7b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // For demonstration attempt to decrypt your encrypted data using the same cryptographic material manager + // you used on encrypt, but we won't pass the encryption context we DID NOT store on the message. + // This will fail + _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + // We expect failure. + if err == nil { + panic("Decryption passed without any error when encryption context was not provided.") + } + // Decrypt your encrypted data using the same cryptographic material manager + // you used to encrypt, but supply encryption context that contains ONLY the encryption context that + // was NOT stored. This will pass. + reproducedEncryptionContext := map[string]string{ + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + decryptOutputreproducedEC, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: reproducedEncryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: requiredEC, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputreproducedEC.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // You can also decrypt with the underlyingCMM, but must still provide the reproducedEncryptionContext. + decryptOutputWithCMM, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: reproducedEncryptionContext, + Ciphertext: res.Ciphertext, + MaterialsManager: underlyingCMM, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputWithCMM.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Required Encryption Context CMM Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go new file mode 100644 index 000000000..6ce7c5460 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go @@ -0,0 +1,137 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + Demonstrate an encrypt/decrypt cycle using a Custom Cryptographic Materials Manager (CMM). + `signingsuiteonlycmm.go` demonstrates creating a custom CMM to reject Non-Signing Algorithms. +*/ + +package restrictalgorithmsuite + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func SigningOnlyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the AWS KMS client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultKmsKeyRegion + }) + // Step 3: Create your encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the Aws KMS Keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Create an instance of the custom CMM + cmm, err := NewSigningSuiteOnlyCMM(awsKmsKeyring) + if err != nil { + panic(err) + } + // Step 5a: Encrypt + algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384 + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + AlgorithmSuiteId: &algorithmSuiteId, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a Non Signing Algorithm Suite will be rejected by the CMM. + nonSigningAlgorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + _, err = encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + MaterialsManager: cmm, + AlgorithmSuiteId: &nonSigningAlgorithmSuiteId, + }) + if err == nil { + panic("Expected error but error is nil") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + fmt.Println("SigningSuiteOnlyCMM Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go new file mode 100644 index 000000000..7d02d7a1b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go @@ -0,0 +1,77 @@ +package restrictalgorithmsuite + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" +) + +type SigningSuiteOnlyCMM struct { + approvedAlgos map[mpltypes.ESDKAlgorithmSuiteId]bool + cmm mpltypes.ICryptographicMaterialsManager +} + +// NewSigningSuiteOnlyCMM creates a new SigningSuiteOnlyCMM +func NewSigningSuiteOnlyCMM(keyring mpltypes.IKeyring) (*SigningSuiteOnlyCMM, error) { + // Initialize the MPL client + materialProviders, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Create a DefaultCryptographicMaterialsManager + cmmInput := mpltypes.CreateDefaultCryptographicMaterialsManagerInput{ + Keyring: keyring, + } + cmm, err := materialProviders.CreateDefaultCryptographicMaterialsManager(context.Background(), cmmInput) + if err != nil { + return nil, err + } + // Create list of approved algorithm + var approvedAlgos = map[mpltypes.ESDKAlgorithmSuiteId]bool{ + mpltypes.ESDKAlgorithmSuiteIdAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: true, + mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384: true, + } + return &SigningSuiteOnlyCMM{ + approvedAlgos: approvedAlgos, + cmm: cmm, + }, nil +} + +func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) GetEncryptionMaterials(input mpltypes.GetEncryptionMaterialsInput) (*mpltypes.GetEncryptionMaterialsOutput, error) { + // Get the algorithm suite from the input + esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) + if err != nil { + return nil, err + } + // Check if the algorithm is approved + if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { + return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} + } + // Delegate to the underlying CMM + return signingSuiteOnlyCMM.cmm.GetEncryptionMaterials(input) +} + +func getESDKAlgorithmSuite(algSuite mpltypes.AlgorithmSuiteId) (*mpltypes.AlgorithmSuiteIdMemberESDK, error) { + if esdk, ok := algSuite.(*mpltypes.AlgorithmSuiteIdMemberESDK); ok { + return esdk, nil + } + return nil, fmt.Errorf("algorithm suite is not ESDK type") +} + +func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) DecryptMaterials(input mpltypes.DecryptMaterialsInput) (*mpltypes.DecryptMaterialsOutput, error) { + // Get the algorithm suite from the input + esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) + if err != nil { + return nil, err + } + // Check if the algorithm is approved + if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { + return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} + } + // Delegate to the underlying CMM + return signingSuiteOnlyCMM.cmm.DecryptMaterials(input) +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/go.mod b/AwsEncryptionSDK/runtimes/go/examples/go.mod new file mode 100644 index 000000000..10303df09 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/go.mod @@ -0,0 +1,43 @@ +module github.com/aws/aws-encryption-sdk/examples + +go 1.23.0 + +replace ( + github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 => ../../../../mpl/ComAmazonawsDynamodb/runtimes/go/ImplementationFromDafny-go/ + github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 => ../../../../mpl/ComAmazonawsKms/runtimes/go/ImplementationFromDafny-go/ + github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 => ../../../../mpl/AwsCryptographicMaterialProviders/runtimes/go/ImplementationFromDafny-go/ + github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 => ../../../../mpl/AwsCryptographyPrimitives/runtimes/go/ImplementationFromDafny-go/ + github.com/aws/aws-encryption-sdk => ../ImplementationFromDafny-go/ + github.com/dafny-lang/DafnyStandardLibGo => ../../../../mpl/StandardLibrary/runtimes/go/ImplementationFromDafny-go/ +) + +require ( + github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 + github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 + github.com/aws/aws-encryption-sdk v0.0.0-00010101000000-000000000000 + github.com/aws/aws-sdk-go-v2/config v1.27.37 + github.com/aws/aws-sdk-go-v2/credentials v1.17.35 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1 + github.com/aws/aws-sdk-go-v2/service/kms v1.36.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 +) + +require ( + github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 // indirect + github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // 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.11.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.23.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 // indirect + github.com/aws/smithy-go v1.21.0 // indirect + github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 // indirect + github.com/dafny-lang/DafnyStandardLibGo v0.0.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect +) diff --git a/AwsEncryptionSDK/runtimes/go/examples/go.sum b/AwsEncryptionSDK/runtimes/go/examples/go.sum new file mode 100644 index 000000000..55861093d --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/go.sum @@ -0,0 +1,48 @@ +github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= +github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= +github.com/aws/aws-sdk-go-v2/config v1.27.37 h1:xaoIwzHVuRWRHFI0jhgEdEGc8xE1l91KaeRDsWEIncU= +github.com/aws/aws-sdk-go-v2/config v1.27.37/go.mod h1:S2e3ax9/8KnMSyRVNd3sWTKs+1clJ2f1U6nE0lpvQRg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.35 h1:7QknrZhYySEB1lEXJxGAmuD5sWwys5ZXNr4m5oEz0IE= +github.com/aws/aws-sdk-go-v2/credentials v1.17.35/go.mod h1:8Vy4kk7at4aPSmibr7K+nLTzG6qUQAUO4tW49fzUV4E= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= +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.35.1 h1:DDN8yqYzFUDy2W5zk3tLQNKaO/1t0h3fNixPJacu264= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1/go.mod h1:k5XW8MoMxsNZ20RJmsokakvENUwQyjv69R9GqrI4xdQ= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 h1:dOxqOlOEa2e2heC/74+ZzcJOa27+F1aXFZpYgY/4QfA= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19/go.mod h1:aV6U1beLFvk3qAgognjS3wnGGoDId8hlPEiBsLHXVZE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg= +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.23.1 h1:2jrVsMHqdLD1+PA4BA6Nh1eZp0Gsy3mFSB5MxDvcJtU= +github.com/aws/aws-sdk-go-v2/service/sso v1.23.1/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 h1:0L7yGCg3Hb3YQqnSgBTZM5wepougtL1aEccdcdYhHME= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 h1:8K0UNOkZiK9Uh3HIF6Bx0rcNCftqGCeKmOaR7Gp5BSo= +github.com/aws/aws-sdk-go-v2/service/sts v1.31.1/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= +github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= +github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 h1:dOgaw3i0I9nWKPjfXYzEfgWsVRJykL6FA18DErvQiJQ= +github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1/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/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go new file mode 100644 index 000000000..e3ccfbbd2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go @@ -0,0 +1,189 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Discovery Keyring +AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. +This example also includes some sanity checks for demonstration: + 1. Ciphertext and plaintext data are not the same + 2. Decrypted plaintext value matches exampleText + 3. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to + which the KMS key used for encryption belongs +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsdiscoverykeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsDiscoveryKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + // So, we create two keyrings, one for encrypt and another for decrypt + // First Keyring: Create a AwsKmsKeyring to use for encryption + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a Discovery keyring to use for decryption. + // We'll add a discovery filter so that we limit + // the set of ciphertexts we are willing to decrypt to only ones created by KMS keys in our account and + // partition. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsDiscoveryKeyringInput := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ + KmsClient: kmsClient, + DiscoveryFilter: &discoveryFilter, + } + awsKmsDiscoveryKeyring, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate that if a different discovery keyring doesn't have the correct + // AWS Account ID's, the decrypt will fail with an error message + // Note that this assumes Account ID used here ('888888888888') is different than the one used + // during encryption + discoveryFilterFailureCase := mpltypes.DiscoveryFilter{ + AccountIds: []string{"888888888888"}, + Partition: "aws", + } + awsKmsDiscoveryKeyringInputFailureCase := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ + KmsClient: kmsClient, + DiscoveryFilter: &discoveryFilterFailureCase, + } + awsKmsDiscoveryKeyringFailureCase, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInputFailureCase) + _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryKeyringFailureCase, + Ciphertext: res.Ciphertext, + }) + // We expected error in failure case + if err == nil { + panic("Expected failure case to fail") + } + fmt.Println("AWS KMS Discovery Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go new file mode 100644 index 000000000..d9682072c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go @@ -0,0 +1,169 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption +using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi +keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsdiscoverymultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsDiscoveryMultiKeyringExample( + exampleText string, + defaultKmsKeyId string, + defaultKMSKeyAccountID string, + regions []string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + // So, we create two keyrings, one for encrypt and another for decrypt + // First Keyring: Create a AwsKmsKeyring to use for encryption + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a Discovery keyring to use for decryption. + // We'll add a discovery filter so that we limit the set of ciphertexts we are willing to + // decrypt to only ones created by KMS keys in our account and partition. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsDiscoveryMultiKeyringInput{ + Regions: regions, + DiscoveryFilter: &discoveryFilter, + } + awsKmsDiscoveryMultiKeyring, err := matProv.CreateAwsKmsDiscoveryMultiKeyring(context.Background(), awsKmsDiscoveryMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err := validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same.") + } + fmt.Println("AWS KMS Discovery Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go new file mode 100644 index 000000000..024598d8c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go @@ -0,0 +1,296 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" + keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these + branch keys are themselves protected by a KMS Key. + + Establishing a key hierarchy like this has two benefits: + First, by caching the branch key material, and only calling KMS to re-establish authentication + regularly according to your configured TTL, you limit how often you need to call KMS to protect + your data. This is a performance security tradeoff, where your authentication, audit, and logging + from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail + cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has + Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, + the benefit is that you no longer have to make a network call to KMS for every encrypt or + decrypt. + + Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a + multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect + the tenant's data. You can either statically configure a single branch key to ensure you are + restricting access to a single tenant, or you can implement an interface that selects the Branch + Key based on the Encryption Context. + + This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to + encrypt and decrypt data for two separate tenants. + + This example requires access to the DDB Table where you are storing the Branch Keys. 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). + + This example also requires using a KMS Key. You need the following access on this key: + - GenerateDataKeyWithoutPlaintext + - Decrypt + + For more information on how to use Hierarchical Keyrings, see + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-hierarchical-keyring.html +*/ + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "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/kms" +) + +func AwsKmsHKeyExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { + // Step 1: Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + // Step 1a: Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Step 1b: Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the keystore to manage the tenant keys + // This SHOULD be the same configuration that you used + // to initially create and populate your KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: keyStoreKMSKeyID, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + panic(err) + } + // Step 3: Create two new branch keys + branchKeyA, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + branchKeyB, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + // Step 4: Create a branch key supplier that maps the branch key id to a more readable format + // See branchkeysupplier.go in this package for the branchKeySupplier structure + keySupplier := branchKeySupplier{branchKeyA: branchKeyA, branchKeyB: branchKeyB} + // Step 5: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 6: Create the Hierarchical Keyring. + hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyIdSupplier: &keySupplier, + TtlSeconds: 600, + } + hKeyRing, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) + if err != nil { + panic(err) + } + // Step 7: Create encryption context for both tenants. + // The Branch Key Id supplier uses the encryption context to determine which branch key id will + // be used to encrypt data. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + + // Create encryption context for TenantA + encryptionContextA := map[string]string{ + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Create encryption context for TenantB + encryptionContextB := map[string]string{ + "tenant": "TenantB", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 8a: Encrypt the data + // Encrypt data for Tenant A + encryptOutputA, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContextA, + Keyring: hKeyRing, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutputA.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Encrypt data for Tenant B + encryptOutputB, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContextB, + Keyring: hKeyRing, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutputB.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 8b: Decrypt the data with various scenerios for demonstration + + // For demonstration, let's attest that TenantKeyB cannot decrypt a message written by TenantKeyA, + // and vice versa and construct more restrictive hierarchical keyrings. + hkeyringInputA := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyId: &branchKeyA, + TtlSeconds: 600, + } + hKeyRingA, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputA) + if err != nil { + panic(err) + } + hkeyringInputB := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore, + BranchKeyId: &branchKeyB, + TtlSeconds: 600, + } + hKeyRingB, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputB) + if err != nil { + panic(err) + } + // Demonstrate that data encrypted by one tenant's key + // cannot be decrypted with by a keyring specific to another tenant. + + // Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key + // This will fail and raise a AwsCryptographicMaterialProvidersException, + // which we swallow ONLY for demonstration purposes. + _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputA.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingB, + }) + if err == nil { + panic("Expected error did not occur") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + // Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. + // This will fail and raise a AwsCryptographicMaterialProvidersException, + // which we swallow ONLY for demonstration purposes. + _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputB.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingA, + }) + if err == nil { + panic("Expected error did not occur") + } + switch err.(type) { + case mpltypes.AwsCryptographicMaterialProvidersException: + // You may choose how to handle the exception in this switch case. + default: + panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") + } + // Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, + // and that the decrypted data matches the input data. + + // For tenant A + decryptOutputA, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputA.Ciphertext, + EncryptionContext: encryptionContextA, + Keyring: hKeyRingA, + }) + if err != nil { + panic(err) + } + // If you are not specifying the encryption context on decrypt. Its recommended to check if the encryption context matches. + // Although, we are specifying the encryption context on decrypt, only for demonstration we are validating the encryption context. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContextA, decryptOutputA.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputA.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // For tenant B + decryptOutputB, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutputB.Ciphertext, + EncryptionContext: encryptionContextB, + Keyring: hKeyRingB, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContextB, decryptOutputB.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputB.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputA.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Aws Kms Hierarchical Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go new file mode 100644 index 000000000..fa066fd70 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go @@ -0,0 +1,45 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "fmt" + + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" +) + +/* +Demonstrates how to create a BranchKeyIdSupplier. + +The BranchKeyIdSupplier determines which Branch Key is used +to protect or access data. +It is an important component in a Multi-tenant solution, +where each tenant is cryptographically isolated. +The Branch Key ID Supplier uses the Encryption Context +provided at Encrypt or Decrypt +to determine what "shared secret" (Branch Key) +is used. +*/ + +type branchKeySupplier struct { + branchKeyA string + branchKeyB string +} + +func (b *branchKeySupplier) GetBranchKeyId(input mpltypes.GetBranchKeyIdInput) (*mpltypes.GetBranchKeyIdOutput, error) { + // We MUST use the encryption context to determine + // the Branch Key ID. + ec := input.EncryptionContext + if value, exists := ec["tenant"]; !exists || value == "" { + return nil, fmt.Errorf("EncryptionContext invalid, does not contain expected tenant key value pair.") + } + branchKeyIdentifier := ec["tenant"] + if branchKeyIdentifier == "TenantA" { + return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyA}, nil + } else if branchKeyIdentifier == "TenantB" { + return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyB}, nil + } else { + return &mpltypes.GetBranchKeyIdOutput{}, fmt.Errorf("unknown branch key identifier") + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go new file mode 100644 index 000000000..f236e9ac4 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go @@ -0,0 +1,45 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "context" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +/* + The Hierarchical Keyring Example relies on the existence + of a DDB-backed key store with pre-existing + branch key material. + + This example demonstrates configuring a KeyStore and creating a branch key. +*/ + +func createbranchkeyid(keyStoreTableName, logicalKeyStoreName, kmsKeyArn string, ddbClient *dynamodb.Client, kmsClient *kms.Client) (string, error) { + // 1. Create the keystore + // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: kmsKeyArn, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreTableName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + return "", err + } + // 2. Create a branch key identifier with the AWS KMS Key configured in the KeyStore Configuration. + branchKey, err := keyStore.CreateKey(context.Background(), keystoretypes.CreateKeyInput{}) + if err != nil { + return "", err + } + return branchKey.BranchKeyIdentifier, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go new file mode 100644 index 000000000..2aead2ccb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go @@ -0,0 +1,228 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example demonstrates how to use a shared cache across multiple Hierarchical Keyrings. + With this functionality, users only need to maintain one common shared cache across multiple + Hierarchical Keyrings with different Key Stores instances/KMS Clients/KMS Keys. + + If you want to use a Shared Cache, you need to initialize it only once, and + pass the same cache `shared_cache` to different hierarchical keyrings. + + There are three important parameters that users need to carefully set while providing the shared cache: + + 1. Partition ID - Partition ID is an optional parameter provided to the Hierarchical Keyring input, + which distinguishes Cryptographic Material Providers (i.e: Keyrings) writing to a cache. + - If the Partition ID is set and is the same for two Hierarchical Keyrings (or another Material Provider), + they CAN share the same cache entries in the cache. + - If the Partition ID is set and is different for two Hierarchical Keyrings (or another Material Provider), + they CANNOT share the same cache entries in the cache. + - If the Partition ID is not set by the user, it is initialized as a random 16-byte UUID which makes + it unique for every Hierarchical Keyring, and two Hierarchical Keyrings (or another Material Provider) + CANNOT share the same cache entries in the cache. + + 2. Logical Key Store Name - This parameter is set by the user when configuring the Key Store for + the Hierarchical Keyring. This is a logical name for the branch key store. + Suppose you have a physical Key Store (K). You create two instances of K (K1 and K2). Now, you create + two Hierarchical Keyrings (HK1 and HK2) with these Key Store instances (K1 and K2 respectively). + - If you want to share cache entries across these two keyrings, you should set the Logical Key Store Names + for both the Key Store instances (K1 and K2) to be the same. + - If you set the Logical Key Store Names for K1 and K2 to be different, HK1 (which uses Key Store instance K1) + and HK2 (which uses Key Store instance K2) will NOT be able to share cache entries. + + 3. Branch Key ID - Choose an effective Branch Key ID Schema + + This is demonstrated in the example below. + Notice that both K1 and K2 are instances of the same physical Key Store (K). + You MUST NEVER have two different physical Key Stores with the same Logical Key Store Name. + + Important Note: If you have two or more Hierarchy Keyrings with: + - Same Partition ID + - Same Logical Key Store Name of the Key Store for the Hierarchical Keyring + - Same Branch Key ID + then they WILL share the cache entries in the Shared Cache. + Please make sure that you set all of Partition ID, Logical Key Store Name and Branch Key ID + to be the same for two Hierarchical Keyrings if and only if you want them to share cache entries. + + This example first creates a shared cache that you can use across multiple Hierarchical Keyrings. + The example then configures a Hierarchical Keyring (HK1 and HK2) with the shared cache, + a Branch Key ID and two instances (K1 and K2) of the same physical Key Store (K) respectively, + i.e. HK1 with K1 and HK2 with K2. The example demonstrates that if you set the same Partition ID + for HK1 and HK2, the two keyrings can share cache entries. + If you set different Partition ID of the Hierarchical Keyrings, or different + Logical Key Store Names of the Key Store instances, then the keyrings will NOT + be able to share cache entries. + + This example requires access to the DDB Table (K) where you are storing the Branch Keys. 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) + + This example also requires using a KMS Key. You need the following access on this key: + - GenerateDataKeyWithoutPlaintext + - Decrypt +*/ + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "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/kms" +) + +func SharedCacheExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { + // Step 1: Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + fmt.Println(err) + panic(err) + } + // Step 1a: Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Step 1b: Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the CryptographicMaterialsCache (CMC) to share across multiple Hierarchical Keyrings + // using the Material Providers Library + // This CMC takes in: + // - CacheType + cache := mpltypes.CacheTypeMemberDefault{ + Value: mpltypes.DefaultCache{ + EntryCapacity: 100, + }, + } + cmcCacheInput := mpltypes.CreateCryptographicMaterialsCacheInput{ + Cache: &cache, + } + sharedCryptographicMaterialsCache, err := matProv.CreateCryptographicMaterialsCache(context.Background(), cmcCacheInput) + if err != nil { + panic(err) + } + // Step 4: Create a CacheType object for the sharedCryptographicMaterialsCache + // Note that the `cache` parameter in the Hierarchical Keyring Input takes a `CacheType` as input + // Here, we pass a `Shared` CacheType that passes an already initialized shared cache. + + // If you want to use a Shared Cache, you need to initialize it only once, and + // pass the same cache `shared_cache` to different hierarchical keyrings. + + // CryptographicMaterialsCacheRef is an Rc (Reference Counted), so if you clone it to + // pass it to different Hierarchical Keyrings, it will still point to the same + // underlying cache, and increment the reference count accordingly. + shared_cache := mpltypes.CacheTypeMemberShared{sharedCryptographicMaterialsCache} + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Configure your Key Store resource keyStore1. + // This SHOULD be the same configuration that you used + // to initially create and populate your physical Key Store. + // Note that key_store_table_name is the physical Key Store, + // and key_store1 is instances of this physical Key Store. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: keyStoreKMSKeyID, + } + keyStore1, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + panic(err) + } + // Step 6: Call create_branch_key_id to create one new branch key + branchKeyId, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) + if err != nil { + panic(err) + } + // Step 7: Create the Hierarchical Keyring HK1 with Key Store instance K1, partition_id, + // the shared_cache and the branch_key_id. + // Note that we are now providing an already initialized shared cache instead of just mentioning + // the cache type and the Hierarchical Keyring initializing a cache at initialization. + // partition_id for this example is a random UUID + partitionId := "91c1b6a2-6fc3-4539-ad5e-938d597ed730" + // Please make sure that you read the guidance on how to set Partition ID, Logical Key Store Name and + // Branch Key ID at the top of this example before creating Hierarchical Keyrings with a Shared Cache + hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ + KeyStore: keyStore1, + BranchKeyId: &branchKeyId, + TtlSeconds: 600, + Cache: &shared_cache, + PartitionId: &partitionId, + } + keyring1, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) + // Step 8: Create encryption context for both tenants. + // The Branch Key Id supplier uses the encryption context to determine which branch key id will + // be used to encrypt data. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + + // Create encryption context for TenantA + encryptionContext := map[string]string{ + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 9: Encrypt the data for encryptionContext using keyring1 + encryptOutput, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampletext), + EncryptionContext: encryptionContext, + Keyring: keyring1, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(encryptOutput.Ciphertext) == exampletext { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 10: Decrypt your encrypted data using the same keyring HK1 you used on encrypt. + decryptOutput, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: encryptOutput.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: keyring1, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampletext { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Shared Cache Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go new file mode 100644 index 000000000..f12f48715 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go @@ -0,0 +1,93 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package awskmshierarchicalkeyring + +import ( + "context" + "fmt" + + keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" + keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" + "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/kms" +) + +/* +This example demonstrates configuring a KeyStore and then +uses a helper method to version a branch key. +*/ +func versionBranchKeyId(keyStoreTableName, logicalKeyStoreName, kmsKeyArn, branchKeyId string) error { + // Load the AWS SDK configuration + cfg, err := config.LoadDefaultConfig(context.Background()) + if err != nil { + return err + } + // Create DDB and KMS clients + ddbClient := dynamodb.NewFromConfig(cfg) + kmsClient := kms.NewFromConfig(cfg) + // Create the keystore + // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. + kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ + Value: kmsKeyArn, + } + keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ + DdbTableName: keyStoreTableName, + KmsConfiguration: &kmsConfig, + LogicalKeyStoreName: logicalKeyStoreName, + DdbClient: ddbClient, + KmsClient: kmsClient, + }) + if err != nil { + return err + } + // To version a branch key you MUST have access to kms:ReEncrypt* and kms:GenerateDataKeyWithoutPlaintext + _, err = keyStore.VersionKey(context.Background(), keystoretypes.VersionKeyInput{ + BranchKeyIdentifier: branchKeyId, + }) + if err != nil { + return err + } + return nil +} + +// Function to test versionBranchKeyId in main.go in examples directory +func CreateAndVersionBranchKeyId(keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) error { + // Create the aws sdk clients + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + // Create the aws kms client + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = keyStoreKMSKeyRegion + }) + // Create the ddb client + ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { + options.Region = keyStoreRegion + }) + // create branch key ID + branchKeyId, err := createbranchkeyid( + keyStoreName, + logicalKeyStoreName, + keyStoreKMSKeyID, + ddbClient, + kmsClient, + ) + if err != nil { + panic(err) + } + // Version Branch Key + err = versionBranchKeyId( + keyStoreName, + logicalKeyStoreName, + keyStoreKMSKeyID, + branchKeyId, + ) + if err != nil { + panic(err) + } + fmt.Println("Create and version branchKey Id Example Completed Successfully") + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmskeyring/awskmskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmskeyring/awskmskeyring.go new file mode 100644 index 000000000..246b8d9ac --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmskeyring/awskmskeyring.go @@ -0,0 +1,122 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS Keyring +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmskeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go new file mode 100644 index 000000000..6822bbcfc --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go @@ -0,0 +1,172 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Keyring +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkdiscoverykeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkDiscoveryKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion, defaultKMSKeyAccountID string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Though this example highlights Discovery keyrings, Discovery keyrings + // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. + // So, we create two keyrings. One for encryption, second one for decryption + // First Keyring: Create KMS MRK Keyring used for encryption + awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientEncrypt, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) + if err != nil { + panic(err) + } + // Second Keyring: create a Discovery keyring to use for decryption. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + // In order to illustrate the MRK behavior of this keyring, we configure + // the keyring to use the second KMS region where the MRK is replicated to. + // This example assumes you have already replicated your key, but since we + // are using a discovery keyring, we don't need to provide the mrk replica key id + awsKmsMrkDiscoveryInput := mpltypes.CreateAwsKmsMrkDiscoveryKeyringInput{ + KmsClient: kmsClientDecrypt, + Region: alternateRegionMrkKeyRegion, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryKeyring, err := matProv.CreateAwsKmsMrkDiscoveryKeyring(context.Background(), awsKmsMrkDiscoveryInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption ARE the same") + } + // Step 6b: Decrypt + // Create a Discovery keyring to use for decryption. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkDiscoveryKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Discovery Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go new file mode 100644 index 000000000..54dbdf17a --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go @@ -0,0 +1,164 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Multi Keyring +AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings. +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkdiscoverymultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkDiscoveryMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, defaultKMSKeyAccountID string, regionsOfMRKKeys []string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // Though this example highlights Discovery keyrings, Discovery keyrings + // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. + // So, we create two keyrings. One for encryption, second one for decryption + // First Keyring: Create KMS MRK Keyring used for encryption + awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) + if err != nil { + panic(err) + } + // Second Keyring: Create a MRK Discovery Multi Keyring to use for decryption + // We'll add a discovery filter to limit the set of encrypted data keys + // we are willing to decrypt to only ones created by KMS keys in select + // accounts and the partition `aws`. + // MRK Discovery keyrings also filter encrypted data keys by the region + // the keyring is created with. + discoveryFilter := mpltypes.DiscoveryFilter{ + AccountIds: []string{defaultKMSKeyAccountID}, + Partition: "aws", + } + awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ + Regions: regionsOfMRKKeys, + DiscoveryFilter: &discoveryFilter, + } + awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsMrkDiscoveryMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Discovery Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go new file mode 100644 index 000000000..814a51328 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go @@ -0,0 +1,152 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Keyring +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkkeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultMRKKeyRegion + }) + kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyrings + // Create one keyring for encrypt with KMS client on defaultMRKKeyRegion region + // Create second keyring for decrypt with KMS client on alternateRegionMrkKeyRegion region. + // In order to illustrate the MRK behavior, we are creating two keyrings with two different regions + awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientEncrypt, + KmsKeyId: defaultRegionMrkKeyArn, + } + awsKmsMrkKeyringInputDecrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClientDecrypt, + KmsKeyId: alternateRegionMrkKeyArn, + } + awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) + if err != nil { + panic(err) + } + awsKmsMrkKeyringDecrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputDecrypt) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // 1. Decrypt with the same keyring (same region) as encrypt + decryptOutputSameRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringEncrypt, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputSameRegion.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // 2. Decrypt with different keyring on different region. + decryptOutputDifferentRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyringDecrypt, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputDifferentRegion.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputDifferentRegion.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go new file mode 100644 index 000000000..76a09046f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go @@ -0,0 +1,153 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS MRK (multi-region key) Multi Keyring +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with AWS KMS MRK keys. +The KMS MRK multi-keyring consists of one or more individual keyrings of the +same or different type. The keys can either be regular KMS keys or MRKs. +The effect is like using several keyrings in a series. +This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id +as a child key, and then encrypts a custom input exampleText with an encryption context. +Either KMS Key individually is capable of decrypting data encrypted under this keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +3. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the + MRK (from the multi-keyring used for encryption) copied from the first region into + the second region +These sanity checks are for demonstration in the example only. You do not need these in your code. +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ +package awskmsmrkmultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMrkMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultKMSKeyId, alternateRegionMrkKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionMrkKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + defaultMrkKey := defaultRegionMrkKeyArn + // Step 3: Create the keyring + awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ + Generator: &defaultMrkKey, + KmsKeyIds: []string{defaultKMSKeyId}, + } + awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutputMulti, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputMulti.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a single AwsKmsMrkKeyring configured with a replica of a MRK from the + // multi-keyring used to encrypt the data is also capable of decrypting the data. + awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: alternateRegionMrkKeyArn, + } + awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) + if err != nil { + panic(err) + } + decryptOutputMrk, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMrkKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputMrk.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputMrk.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS MRK Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go new file mode 100644 index 000000000..ea6710deb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go @@ -0,0 +1,172 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, + and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and +another KMS keyring as a child keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsmultikeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsMultiKeyringExample(exampleText, defaultKMSKeyId, alternateRegionKMSKeyId, alternateRegionKMSKeyRegion string) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. + // Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. + generatorKeyId := defaultKMSKeyId + awsKmsMultiKeyringInput := mpltypes.CreateAwsKmsMultiKeyringInput{ + Generator: &generatorKeyId, + KmsKeyIds: []string{alternateRegionKMSKeyId}, + } + awsKmsMultiKeyring, err := matProv.CreateAwsKmsMultiKeyring(context.Background(), awsKmsMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsMultiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsMultiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that a single AwsKmsKeyring configured with either KMS key + // is also capable of decrypting the data. + // Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = alternateRegionKMSKeyRegion + }) + // Create a single AwsKmsKeyring with the KMS key from our second region. + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: alternateRegionKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + decryptOutputKmsKeyring, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputKmsKeyring.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputKmsKeyring.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("KMS Multi Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go new file mode 100644 index 000000000..4d6ba051b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go @@ -0,0 +1,127 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the AWS KMS RSA Keyring +This example creates a KMS RSA Keyring and then encrypts a custom input +exampleText with an encryption context. +This example also includes some sanity checks for demonstration: + 1. Ciphertext and plaintext data are not the same + 2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +# For more information on how to use KMS keyrings, see +# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package awskmsrsakeyring + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" + kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types" +) + +func AwsKmsRsaExample(exampleText string, kmsRsaKeyID string, kmsRSAPublicKey []byte) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient( + mpltypes.MaterialProvidersConfig{}, + ) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsRSAKeyringInput := mpltypes.CreateAwsKmsRsaKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: kmsRsaKeyID, + PublicKey: kmsRSAPublicKey, + EncryptionAlgorithm: kmstypes.EncryptionAlgorithmSpecRsaesOaepSha256, + } + awsKmsRSAKeyring, err := matProv.CreateAwsKmsRsaKeyring(context.Background(), awsKmsRSAKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + AlgorithmSuiteId: &algorithmSuiteID, + EncryptionContext: encryptionContext, + Keyring: awsKmsRSAKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsRSAKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("AWS KMS RSA Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go new file mode 100644 index 000000000..ba6c960ba --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go @@ -0,0 +1,219 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the KMS ECDH Discovery Keyring. + +This example takes in the recipient's KMS ECC key ARN. +This example attempts to decrypt a ciphertext using the kmsEcdhKeyIdP256RecipientKeyId, +it does so by checking if the message header contains the recipient's public key. + +This example also requires access to a KMS ECC key. +Our tests provide a KMS ECC Key ARN that anyone can use, but you +can also provide your own KMS ECC key. +To use your own KMS ECC key, you must have: + - kms:GetPublicKey permissions on that key. +This example will call kms:GetPublicKey on keyring creation. +You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. + +This example creates a KMS ECDH Discovery Keyring and then decrypts a ciphertext. +For getting the ciphertext, we create a KMS ECDH keyring without discovery +because kms_ecdh_discovery_keyring cannot encrypt data. +This example also includes some sanity checks for demonstration: +1. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsEcdhDiscoveryKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the KMS ECDH keyring. + // This keyring uses the KmsPublicKeyDiscovery configuration. + // On encrypt, the keyring will fail as it is not allowed to encrypt data under this configuration. + // On decrypt, the keyring will check if its corresponding public key is stored in the message header. It + // will call AWS KMS to derive the shared from the recipient's KMS ECC Key ARN and the sender's public key; + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery + // This keyring takes in: + // - kmsClient + // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + kmsEcdhDiscoveryStaticConfigurationInput := mpltypes.KmsPublicKeyDiscoveryInput{ + RecipientKmsIdentifier: kmsEcdhKeyIdP256RecipientKeyId, + } + kmsEcdhDiscoveryStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPublicKeyDiscovery{ + Value: kmsEcdhDiscoveryStaticConfigurationInput, + } + awsKmsEcdhDiscoveryKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhDiscoveryStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhDiscoveryKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhDiscoveryKeyringInput) + if err != nil { + panic(err) + } + // Step 6: Get ciphertext by creating a KMS ECDH keyring WITHOUT discovery + // because the KMS ECDH keyring WITH discovery CANNOT encrypt data. + // We are generating a message intended for the kmsEcdhKeyIdP256RecipientKeyId recipient. + // Since a KMS ECDH keyring WITHOUT discovery cannot encrypt data, this example will ONLY decrypt + // messages where the configured key on the Discovery keyring is present on the message ciphertext. + // In this example we call `kms:GetPublicKey` to get the public key associated with the + // kmsEcdhKeyIdP256RecipientKeyId KMS key ID. + // If the message contains this public key, message decryption will be attempted. + cipherText := getCipherTextKmsEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, kmsClient, kmsEcdhKeyIdP256RecipientKeyId, kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsEccPublicKeyFileNameRecipient) + + // Step 7: Decrypt your encrypted data using the keyring with discovery behavior we created in step 5. + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: awsKmsEcdhDiscoveryKeyring, + EncryptionContext: encryptionContext, + Ciphertext: cipherText, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("AWS KMS ECDH Discovery Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function creates a AWS KMS ECDH keyring and encrypt the exampleText +func getCipherTextKmsEcdh( + matProv *mpl.Client, + encryptionClient *client.Client, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + exampleText string, + encryptionContext map[string]string, + kmsClient *kms.Client, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) []byte { + // 1. Create the public key files for sender and recipient + // You may provide your own ECC keys. + // If not, this class will call the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. + // Sender ECC key used in this example is retrieved with kmsEcdhKeyIdP256SenderKeyId + // Recipent ECC key used in this example is retrieved with kmsEcdhKeyIdP256RecipientKeyId + if !utils.FileExists(kmsEccPublicKeyFileNameSender) { + err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) + if err != nil { + panic(err) + } + } + if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { + err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) + if err != nil { + panic(err) + } + } + // 2. Load public key from UTF-8 encoded PEM files into a DER encoded public key. + publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // 3. Create the KmsPrivateKeyToStaticPublicKeyInput and kmsEcdhStaticConfiguration + kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, + SenderPublicKey: publicKeySender, + } + kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ + Value: kmsEcdhStaticConfigurationInput, + } + // 4. Create the KMS ECDH keyring. + awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) + if err != nil { + panic(err) + } + // 5. Encrypt the data with the encryption_context + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // 6. Return the ciphertext + return res.Ciphertext +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhkeyring.go new file mode 100644 index 000000000..fd51138cb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/awskmsecdhkeyring.go @@ -0,0 +1,193 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the KMS ECDH Keyring. + +This example takes in the sender's KMS ECC key ARN, the sender's public key, +the recipient's public key, and the algorithm definition where the ECC keys lie. + +Both public keys MUST be UTF8 PEM-encoded X.509 public key, +also known as SubjectPublicKeyInfo (SPKI), + +This keyring, depending on its KeyAgreement scheme, +takes in the sender's KMS ECC Key ARN, and the recipient's ECC Public Key +to derive a shared secret. +The keyring uses the shared secret to derive a data key to protect the +data keys that encrypt and decrypt exampletext. + +This example also requires access to a KMS ECC key. +Our tests provide a KMS ECC Key ARN that you need permissions to, but you +can also provide your own KMS ECC key. +To use your own KMS ECC key, you must have either: +- Its public key downloaded in a UTF-8 encoded PEM file +- kms:GetPublicKey permissions on that key. +If you do not have the public key downloaded, running this example +through its main method will download the public key for you +by calling kms:GetPublicKey. +You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. +This example also requires a recipient ECC Public Key that lies on the same +curve as the sender public key. This examples uses another distinct +KMS ECC Public Key, it does not have to be a KMS key; it can be a +valid SubjectPublicKeyInfo (SPKI) Public Key. + +This example creates a KMS ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func AwsKmsEcdhKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + kmsEcdhKeyIdP256RecipientKeyId string, + kmsEcdhKeyIdP256SenderKeyId string, + kmsEccPublicKeyFileNameSender string, + kmsEccPublicKeyFileNameRecipient string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = "us-west-2" + }) + // Step 2: Load public key from UTF-8 encoded PEM files into a DER encoded public key. + // You may provide your own ECC keys. + // If not, this class will call the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. + if !utils.FileExists(kmsEccPublicKeyFileNameSender) { + err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) + if err != nil { + panic(err) + } + } + if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { + err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) + if err != nil { + panic(err) + } + } + publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 3: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6: Create the KMS ECDH keyring. + // This keyring uses the KmsPrivateKeyToStaticPublicKey configuration. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key. + // For this example, on decrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key; + // however, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create + // This keyring takes in: + // - kmsClient + // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, + SenderPublicKey: publicKeySender, + } + kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ + Value: kmsEcdhStaticConfigurationInput, + } + awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: kmsEcdhStaticConfiguration, + KmsClient: kmsClient, + } + awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) + if err != nil { + panic(err) + } + // Step 7a: Encrypt the data + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // Step 7b: Decrypt the data + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsEcdhKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("AWS KMS ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/ephemeralrawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/ephemeralrawecdhkeyring.go new file mode 100644 index 000000000..2368826d3 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/ephemeralrawecdhkeyring.go @@ -0,0 +1,137 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Ephemeral Raw ECDH Keyring. + +This example takes in the recipient's public key located at +eccPublicKeyFileNameRecipient as a +UTF8 PEM-encoded X.509 public key, +and the Curve Specification where the key lies. + +This example loads ECC keys from PEM files with paths defined in + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme. +This configuration will always create a new key pair as the sender key pair for the key agreement operation. +The ephemeral configuration can only encrypt data and CANNOT decrypt messages. + +This example creates an Ephemeral Raw ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey +*/ + +package ecdh + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" +) + +func EphemeralRawECDHKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string) { + // Step 1: Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + } + ephemeralRawECDHStaticConfiguration := + mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ + Value: ephemeralRawEcdhStaticConfigurationInput, + } + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, + } + ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Step 6: Encrypt + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: ecdhKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + // (This is an example for demonstration; you do not need to do this in your own code.) + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + fmt.Println("Ephemeral Raw ECDH Keyring Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go new file mode 100644 index 000000000..71d30bed3 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go @@ -0,0 +1,218 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Public Key Discovery Raw ECDH Keyring. + +A public key discovery Raw ECDH Keyring takes in the recipient's private key located +at eccPrivateKeyFileNameRecipient +as a UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures) private key, +and the Curve Specification where the key lies. + +If you provide the eccPrivateKeyFileNameRecipient, make sure to also +provide the recipient's public key located at eccPublicKeyFileNameRecipient +in the directory that you run this example. Even though the Public Key Discovery Raw ECDH keyring +uses the eccPrivateKeyFileNameRecipient to decrypt the data, +the eccPublicKeyFileNameRecipient is needed to generate the ciphertext to decrypt. + +This example loads ECC keys from PEM files and the ciphertext with paths defined in + - eccPrivateKeyFileNameRecipient + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFilenameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This example creates a RawECDH keyring with the PublicKeyDiscovery key agreement scheme. +This scheme is only available on decrypt. + +This example creates a Public Key Discovery Raw ECDH Keyring and takes in a ciphertext to decrypt it. +This example also includes some sanity checks for demonstration: +1. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-PublicKeyDiscovery +*/ + +package ecdh + +import ( + "context" + "fmt" + "os" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" +) + +func PublicKeyRawEcdhDiscoveryKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string, + eccPrivateKeyFileNameRecipient string) { + // Step 1: Generate Raw ECDH ECC keys and load the recipient's private key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + privateKeyRecipient, err := os.ReadFile(eccPrivateKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the Public Key Discovery Raw ECDH keyring. + // Create the keyring. + // This keyring uses a discovery configuration. This configuration will check on decrypt + // if it is meant to decrypt the message by checking if the configured public key is stored on the message. + // The discovery configuration can only decrypt messages and CANNOT encrypt messages. + discoveryRawEcdhStaticConfigurationInput := mpltypes.PublicKeyDiscoveryInput{ + RecipientStaticPrivateKey: privateKeyRecipient, + } + discoveryRawEcdhStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberPublicKeyDiscovery{ + Value: discoveryRawEcdhStaticConfigurationInput, + } + discoveryRawEcdhKeyringInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: discoveryRawEcdhStaticConfiguration, + } + discoveryRawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), discoveryRawEcdhKeyringInput) + if err != nil { + panic(err) + } + // Step 6a: Get the ciphertext + // Although this example highlights Public Key Discovery Raw ECDH Keyring keyring, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a Ephemeral Raw ECDH keyring without discovery mode. + cipherText := getCipherTextRawEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, eccPublicKeyFileNameRecipient) + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Keyring: discoveryRawEcdhKeyring, + EncryptionContext: encryptionContext, + Ciphertext: cipherText, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("Public Key Discovery Raw ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function creates a Ephemeral Raw ECDH keyring and encrypt the exampleText +func getCipherTextRawEcdh( + matProv *mpl.Client, + encryptionClient *client.Client, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + exampleText string, + encryptionContext map[string]string, + eccPublicKeyFileNameRecipient string) []byte { + // 1. Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + // Load public key from UTF-8 encoded PEM files into a DER encoded public key. + if !utils.FileExists(eccPublicKeyFileNameRecipient) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + // Create the RawEcdhStaticConfigurations + ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ + RecipientPublicKey: publicKeyRecipient, + } + ephemeralRawECDHStaticConfiguration := mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ + Value: ephemeralRawEcdhStaticConfigurationInput, + } + // Create the Ephemeral Raw ECDH keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, + } + ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Encrypt the data + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: ecdhKeyring, + }) + if err != nil { + panic(err) + } + return res.Ciphertext +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/rawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/rawecdhkeyring.go new file mode 100644 index 000000000..461b1be66 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/ecdh/rawecdhkeyring.go @@ -0,0 +1,194 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Raw ECDH Keyring. + +This example takes in the sender's private key located at +eccPrivateKeyFileNameSender as a UTF8 PEM-encoded +(PKCS #8 PrivateKeyInfo structures) private key, +and the recipient's public key located at +eccPublicKeyFileNameRecipient as a +UTF8 PEM-encoded X.509 public key, +also known as SubjectPublicKeyInfo (SPKI), +and the Curve Specification where the keys lie. + +This example loads ECC keys from PEM files with paths defined in + - eccPrivateKeyFileNameSender + - eccPublicKeyFileNameRecipient + +If you do not provide these files, running this example through this +class' main method will generate three files required for all raw ECDH examples +eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient +and eccPublicKeyFileNameRecipient for you. +These files will be generated in the directory where the example is run. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This example creates a RawECDH keyring with the RawPrivateKeyToStaticPublicKey key agreement scheme. +On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. +On decrypt, the shared secret is derived from the sender's private key and the recipient's public key; +however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with +the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + +This example creates a Raw ECDH Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on this configuration see: +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-RawPrivateKeyToStaticPublicKey +*/ + +package ecdh + +import ( + "context" + "fmt" + "os" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/utils" +) + +func RawECDHKeyringExample( + exampleText string, + ecdhCurveSpec primitivestypes.ECDHCurveSpec, + eccPublicKeyFileNameRecipient string, + eccPrivateKeyFileNameSender string) { + // Step 1: Generate Raw ECDH ECC keys and load public key. + // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient + + // If you do not provide these files, running this example through this + // class' main method will generate three files required for all raw ECDH examples + // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient + // and eccPublicKeyFileNameRecipient for you. + if !utils.FileExists(eccPublicKeyFileNameRecipient) || !utils.FileExists(eccPrivateKeyFileNameSender) { + err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) + if err != nil { + panic(err) + } + } + privateKeySender, err := os.ReadFile(eccPrivateKeyFileNameSender) + if err != nil { + panic(err) + } + publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) + if err != nil { + panic(err) + } + + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5: Create the Raw ECDH keyring. + // This keyring uses static sender and recipient keys. This configuration calls for both of + // the keys to be on the same curve (P256 / P384 / P521). + // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + // For this example, on decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + // However, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + RawEcdhStaticConfigurationInput := mpltypes.RawPrivateKeyToStaticPublicKeyInput{ + SenderStaticPrivateKey: privateKeySender, + RecipientPublicKey: publicKeyRecipient, + } + RawECDHStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberRawPrivateKeyToStaticPublicKey{ + Value: RawEcdhStaticConfigurationInput, + } + rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ + CurveSpec: ecdhCurveSpec, + KeyAgreementScheme: RawECDHStaticConfiguration, + } + rawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) + if err != nil { + panic(err) + } + // Step 6a: Encrypt + // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair + // used as the sender is ephemeral. This means that at decrypt time it does not have + // the private key that corresponds to the public key that is stored on the message. + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: rawEcdhKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + // (This is an example for demonstration; you do not need to do this in your own code.) + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: rawEcdhKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + if string(decryptOutput.Plaintext) == exampleText { + fmt.Println("Raw ECDH Keyring Example Completed Successfully") + } else { + panic("FAILED!") + } +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/multikeyring/multikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/multikeyring/multikeyring.go new file mode 100644 index 000000000..32d4cc5c7 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/multikeyring/multikeyring.go @@ -0,0 +1,233 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Multi Keyring + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring +as a child keyring. You can use different combinations of keyrings in the multi_keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package multikeyring + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func MultiKeyringExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Create the MultiKeyring that consists of the KMS Keyring as generator and Raw AES Keyring as child keyring + // When using this MultiKeyring to encrypt data, either KMS Keyring or + // Raw AES Keyring (or a MultiKeyring containing either) may be used to decrypt the data + awsKmsKeyring := getKMSKeyring(defaultKMSKeyId, defaultKmsKeyRegion, matProv) + rawAESKeyring := getRawAESKeyring(matProv) + createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ + Generator: awsKmsKeyring, + ChildKeyrings: []mpltypes.IKeyring{rawAESKeyring}, + } + multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 3: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that you can also successfully decrypt data using the `rawAESKeyring` directly. + // Because you used a MultiKeyring on Encrypt, you can use either the `kmsKeyring` or + // `rawAESKeyring` individually to decrypt the data. + decryptOutputRawAES, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: rawAESKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputRawAES.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputRawAES.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that you can also successfully decrypt data using the `awsKmsKeyring` directly. + decryptOutputAwsKms, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutputAwsKms.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutputAwsKms.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Multi Keyring Example Completed Successfully") +} +func getKMSKeyring(kmsKeyId string, kmsRegion string, matProv *mpl.Client) mpltypes.IKeyring { + // 1. Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = kmsRegion + }) + // 2. Create Aws Kms keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: kmsKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + return awsKmsKeyring +} +func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { + // 1. Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // 2. Create the keyring + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + return aesKeyring +} +func generateAes256KeyBytes() ([]byte, error) { + const keySize = 32 // 256 bits = 32 bytes + key := make([]byte, keySize) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/rawaeskeyring/rawaeskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/rawaeskeyring/rawaeskeyring.go new file mode 100644 index 000000000..d8ccaa21d --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/rawaeskeyring/rawaeskeyring.go @@ -0,0 +1,138 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the Raw AES Keyring +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. +This example creates a Raw AES Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that +you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, +but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring. +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +*/ +package rawaeskeyring + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func RawAesExample(exampleText string) { + // Step 1: Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Raw AES Keyring Example Completed Successfully") +} + +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 +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/keyring/rawrsakeyring/rawrasakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/keyring/rawrsakeyring/rawrasakeyring.go new file mode 100644 index 000000000..bf70d31c2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/keyring/rawrsakeyring/rawrasakeyring.go @@ -0,0 +1,178 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* +This example sets up the Raw RSA Keyring +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. +This keyring accepts PEM encodings of the key pair as UTF-8 interpreted bytes. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. +This example generate private and public key pairs. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. +This example creates a Raw RSA Keyring and then encrypts a custom input exampleText +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. +A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private +key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, +and you can decrypt data with a Raw RSA keyring that has only a private key. This example requires +the user to either provide both private and public keys, or not provide any keys and the example +generates both to test encryption and decryption. If you configure a Raw RSA keyring with a +public and private key, be sure that they are part of the same key pair. Some language +implementations of the AWS Encryption SDK will not construct a Raw RSA keyring with keys +from different pairs. Others rely on you to verify that your keys are from the same key pair. +You can include any Raw RSA keyring in a multi-keyring. +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +*/ + +package rawrsakeyring + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func RawRsaExample(exampleText string) { + // Step 1: Generate the key-pairs + publicKeyBlock, privateKeyBlock, err := generateKeyPair() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw RSA keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring + keyNamespace := "Some managed raw keys" + keyName := "My 2048-bit RSA wrapping key" + rsaKeyRingInput := mpltypes.CreateRawRsaKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + PaddingScheme: mpltypes.PaddingSchemeOaepSha512Mgf1, + PublicKey: pem.EncodeToMemory(publicKeyBlock), + PrivateKey: pem.EncodeToMemory(privateKeyBlock), + } + rsaKeyring, err := matProv.CreateRawRsaKeyring(context.Background(), rsaKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + cryptoClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + res, err := cryptoClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: rsaKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + // You do not need to specify the encryption context on decrypt + // because the header of the encrypted message includes the encryption context. + decryptOutput, err := cryptoClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + Keyring: rsaKeyring, + EncryptionContext: encryptionContext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Raw RSA Keyring Example Completed Successfully") +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} + +func generateKeyPair() (*pem.Block, *pem.Block, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + // Extract public key from the private key + publicKey := &privateKey.PublicKey + // Encode public key to PKCS1 DER format + publicKeyDER, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return nil, nil, err + } + privateKeyDer, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, nil, err + } + // Encode to PEM format + publicKeyBlock := &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: publicKeyDER, + } + privateKeyBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyDer, + } + return publicKeyBlock, privateKeyBlock, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/main.go b/AwsEncryptionSDK/runtimes/go/examples/main.go new file mode 100644 index 000000000..3662ac551 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/main.go @@ -0,0 +1,161 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-encryption-sdk/examples/clientsupplier" + "github.com/aws/aws-encryption-sdk/examples/cryptographicmaterialsmanager/requiredencryptioncontext" + "github.com/aws/aws-encryption-sdk/examples/cryptographicmaterialsmanager/restrictalgorithmsuite" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsdiscoverykeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsdiscoverymultikeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmshierarchicalkeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmskeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsmrkdiscoverykeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsmrkdiscoverymultikeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsmrkkeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsmrkmultikeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsmultikeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/awskmsrsakeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/ecdh" + "github.com/aws/aws-encryption-sdk/examples/keyring/multikeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/rawaeskeyring" + "github.com/aws/aws-encryption-sdk/examples/keyring/rawrsakeyring" + "github.com/aws/aws-encryption-sdk/examples/misc" + "github.com/aws/aws-encryption-sdk/examples/utils" +) + +func main() { + const stringToEncrypt = "Text To encrypt" + clientsupplier.ClientSupplierExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultKMSKeyAccountID(), + []string{"eu-west-1"}) + misc.CommitmentPolicyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + misc.SetEncryptionAlgorithmSuiteExample(stringToEncrypt) + var maxEncryptedDataKeys int64 = 3 + misc.LimitEncryptedDataKeyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion(), + maxEncryptedDataKeys) + requiredencryptioncontext.RequiredEncryptionContextExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + restrictalgorithmsuite.SigningOnlyExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion()) + // keyrings + ecdh.PublicKeyRawEcdhDiscoveryKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient(), + utils.GetEccPrivateKeyFileNameRecipient()) + ecdh.EphemeralRawECDHKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient()) + ecdh.RawECDHKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetEccPublicKeyFileNameRecipient(), + utils.GetEccPrivateKeyFileNameSender()) + ecdh.AwsKmsEcdhKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetKmsEcdhKeyIdP256RecipientKeyId(), + utils.GetKmsEcdhKeyIdP256SenderKeyId(), + utils.GetKmsEccPublicKeyFileNameSender(), + utils.GetKmsEccPublicKeyFileNameRecipient()) + ecdh.AwsKmsEcdhDiscoveryKeyringExample( + stringToEncrypt, + primitivestypes.ECDHCurveSpecEccNistP256, + utils.GetKmsEcdhKeyIdP256RecipientKeyId(), + utils.GetKmsEcdhKeyIdP256SenderKeyId(), + utils.GetKmsEccPublicKeyFileNameSender(), + utils.GetKmsEccPublicKeyFileNameRecipient()) + awskmskeyring.AwsKmsKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID()) + awskmsrsakeyring.AwsKmsRsaExample( + stringToEncrypt, + utils.GetTestKmsRsaKeyID(), + utils.GetKmsRSAPublicKey()) + awskmsmultikeyring.AwsKmsMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetAlternateRegionKMSKeyId(), + utils.GetAlternateRegionKMSKeyRegion()) + awskmsdiscoverykeyring.AwsKmsDiscoveryKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID()) + awskmsdiscoverymultikeyring.AwsKmsDiscoveryMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKMSKeyAccountID(), + utils.GetRegions()) + rawrsakeyring.RawRsaExample(stringToEncrypt) + awskmsmrkkeyring.AwsKmsMrkKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetAlternateRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetAlternateRegionMrkKeyRegion()) + awskmsmrkmultikeyring.AwsKmsMrkMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetAlternateRegionMrkKeyArn(), + utils.GetDefaultKMSKeyId(), + utils.GetAlternateRegionMrkKeyRegion()) + awskmsmrkdiscoverykeyring.AwsKmsMrkDiscoveryKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetAlternateRegionMrkKeyRegion(), + utils.GetDefaultKMSKeyAccountID()) + awskmsmrkdiscoverymultikeyring.AwsKmsMrkDiscoveryMultiKeyringExample( + stringToEncrypt, + utils.GetDefaultRegionMrkKeyArn(), + utils.GetDefaultMRKKeyRegion(), + utils.GetDefaultKMSKeyAccountID(), + utils.GetRegionsOfMRKKeys(), + ) + awskmshierarchicalkeyring.AwsKmsHKeyExample( + stringToEncrypt, + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + awskmshierarchicalkeyring.CreateAndVersionBranchKeyId( + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + awskmshierarchicalkeyring.SharedCacheExample( + stringToEncrypt, + utils.GetKeyStoreKMSKeyRegion(), + utils.GetKeyStoreRegion(), + utils.GetKeyStoreKMSKeyID(), + utils.GetKeyStoreName(), + utils.GetLogicalKeyStoreName(), + ) + rawaeskeyring.RawAesExample(stringToEncrypt) + multikeyring.MultiKeyringExample( + stringToEncrypt, + utils.GetDefaultKMSKeyId(), + utils.GetDefaultKmsKeyRegion(), + ) +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go new file mode 100644 index 000000000..f7cdb2636 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go @@ -0,0 +1,149 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example configures a client with a specific commitment policy for the +AWS Encryption SDK client, then encrypts and decrypts data using an AWS KMS Keyring. + +The commitment policy in this example (ForbidEncryptAllowDecrypt) should only be +used as part of a migration from version 1.x to 2.x, or for advanced users with +specialized requirements. Most AWS Encryption SDK users should use the default +commitment policy (RequireEncryptRequireDecrypt). + +This example creates a KMS Keyring and then encrypts a custom input exampleText +with an encryption context for the commitment policy ForbidEncryptAllowDecrypt. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches exampleText +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on setting your commitment policy, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +package misc + +import ( + "context" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +func CommitmentPolicyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { + // Step 1: Create the aws kms client + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { + o.Region = defaultKmsKeyRegion + }) + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ + KmsClient: kmsClient, + KmsKeyId: defaultKMSKeyId, + } + awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // Build the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + + // Create one with the commitment policy RequireEncryptAllowDecrypt and another with ForbidEncryptAllowDecrypt. + // Read more about commitment policies here: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy + commitPolicyRequireEncryptRequireDecrypt := mpltypes.ESDKCommitmentPolicyRequireEncryptRequireDecrypt + commitPolicyForbidEncryptAllowDecrypt := mpltypes.ESDKCommitmentPolicyForbidEncryptAllowDecrypt + forbidEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyForbidEncryptAllowDecrypt}) + if err != nil { + panic(err) + } + requireEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyRequireEncryptRequireDecrypt}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + // Make sure you use a non-committing algorithm with the commitment policy ForbidEncryptAllowDecrypt. + // Otherwise encrypt() will throw + // Error: AwsCryptographicMaterialProvidersError + // { + // error: InvalidAlgorithmSuiteInfoOnEncrypt + // { + // message: "Configuration conflict. Commitment policy requires only non-committing algorithm suites" + // } + // } + // By default for ForbidEncryptAllowDecrypt, the algorithm used is + // AlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 which is a non-committing algorithm. + res, err := forbidEncryptClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := forbidEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Demonstrate that an EncryptionSDK that enforces Key Commitment on Decryption + // will fail to decrypt the encrypted message (as it was encrypted without Key Commitment). + _, err = requireEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: awsKmsKeyring, + Ciphertext: res.Ciphertext, + }) + // We expect this to fail + if err == nil { + panic("Expected error but error is nil") + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Set Commitment Policy Example Completed Successfully") +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/misc/limitencrypteddatakeysexample.go b/AwsEncryptionSDK/runtimes/go/examples/misc/limitencrypteddatakeysexample.go new file mode 100644 index 000000000..708b4dcc4 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/misc/limitencrypteddatakeysexample.go @@ -0,0 +1,176 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +Demonstrate limiting the number of Encrypted Data Keys [EDKs] allowed +when encrypting or decrypting a message. +Limiting encrypted data keys is most valuable when you are decrypting +messages from an untrusted source. +By default, the ESDK will allow up to 65,535 encrypted data keys. +A malicious actor might construct an encrypted message with thousands of +encrypted data keys, none of which can be decrypted. +As a result, the AWS Encryption SDK would attempt to decrypt each +encrypted data key until it exhausted the encrypted data keys in the message. + +For more information on limiting EDKs, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-limit-keys +*/ + +package misc + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func LimitEncryptedDataKeyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string, maxEncryptedDataKeys int64) { + // Step 1: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 2: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + // Also, set the EncryptionSDK's MaxEncryptedDataKeys parameter here + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ + MaxEncryptedDataKeys: &maxEncryptedDataKeys, + }) + if err != nil { + panic(err) + } + // Step 3: Generate `maxEncryptedDataKeys` AES keyrings to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + rawAESKeyrings := make([]mpltypes.IKeyring, 0, maxEncryptedDataKeys) + var i int64 = 0 + for i < maxEncryptedDataKeys { + rawAESKeyrings = append(rawAESKeyrings, getRawAESKeyring(matProv)) + i++ + } + // Step 4: Create a Multi Keyring with `maxEncryptedDataKeys` AES Keyrings + createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ + Generator: rawAESKeyrings[0], + ChildKeyrings: rawAESKeyrings[1:], + } + multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) + if err != nil { + panic(err) + } + // Step 4: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 5a: Encrypt + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 5b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + // Demonstrate that an EncryptionSDK with a lower MaxEncryptedDataKeys + // will fail to decrypt the encrypted message. + // (This is an example for demonstration; you do not need to do this in your own code.) + lowerMaxEncryptedDataKeys := maxEncryptedDataKeys - 1 + encryptionClientIncorrectMaxEncryptedKeys, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ + MaxEncryptedDataKeys: &lowerMaxEncryptedDataKeys, + }) + if err != nil { + panic(err) + } + _, err = encryptionClientIncorrectMaxEncryptedKeys.Decrypt(context.Background(), esdktypes.DecryptInput{ + EncryptionContext: encryptionContext, + Keyring: multiKeyring, + Ciphertext: res.Ciphertext, + }) + if err == nil { + panic("Expected error not found.") + } + // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception + switch err.(type) { + case esdktypes.AwsEncryptionSdkException: + // You may choose how to handle the exception in this switch case. + default: + panic("Decryption using lower then max encrypted data keys MUST raise AwsEncryptionSdkException") + } + fmt.Println("Limit Encrypted Data Key Example completed successfully") +} + +func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { + // 1. Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generate256KeyBytesAES() + if err != nil { + panic(err) + } + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // 2. Create the keyring + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + return aesKeyring +} + +func generate256KeyBytesAES() ([]byte, error) { + const keySize = 32 // 256 bits = 32 bytes + key := make([]byte, keySize) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/misc/setencryptionalgorithmsuite.go b/AwsEncryptionSDK/runtimes/go/examples/misc/setencryptionalgorithmsuite.go new file mode 100644 index 000000000..b14153de5 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/misc/setencryptionalgorithmsuite.go @@ -0,0 +1,171 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example demonstrates how to set an algorithm suite while using the Raw AES Keyring +in the AWS Encryption SDK. + +The algorithm suite used in the encrypt() method is the algorithm used to protect your +data using the data key. By setting this algorithm, you can configure the algorithm used +to encrypt and decrypt your data. + +Algorithm suites can be set in a similar manner in other keyrings as well. However, +please make sure that you're using a logical algorithm suite that is compatible with your +keyring. For more information on algorithm suites supported by the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html + +The AES wrapping algorithm (AesWrappingAlg::AlgAes256GcmIv12Tag16) protects your data key using +the user-provided wrapping key. In contrast, the algorithm suite used in the encrypt() method +is the algorithm used to protect your data using the data key. This example demonstrates setting the +latter, which is the algorithm suite for protecting your data. When the commitment policy is +RequireEncryptRequireDecrypt, the default algorithm used in the encrypt method is +AlgAes256GcmHkdfSha512CommitKeyEcdsaP384, which is a committing and signing algorithm. +Signature verification ensures the integrity of a digital message as it goes across trust +boundaries. However, signature verification adds a significant performance cost to encryption +and decryption. If encryptors and decryptors are equally trusted, we can consider using an algorithm +suite that does not include signing. This example sets the algorithm suite as +AlgAes256GcmHkdfSha512CommitKey, which is a committing but non-signing algorithm. +For more information on digital signatures, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + +This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context and the algorithm suite AlgAes256GcmHkdfSha512CommitKey. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +*/ + +package misc + +import ( + "context" + "crypto/rand" + "fmt" + + mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" + client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" + esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" +) + +func SetEncryptionAlgorithmSuiteExample(exampleText string) { + // Step 1: Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + key, err := generateAes256KeyBytes() + if err != nil { + panic(err) + } + // Step 2: Initialize the mpl client + matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) + if err != nil { + panic(err) + } + // Step 3: Create the keyring + // The key namespace and key name are defined by you + // and are used by the raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring + var keyNamespace = "A managed aes keys" + var keyName = "My 256-bit AES wrapping key" + // Note: The wrapping algorithm here is NOT the algorithm suite we set in this example. + aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ + KeyName: keyName, + KeyNamespace: keyNamespace, + WrappingKey: key, + WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, + } + aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) + if err != nil { + panic(err) + } + // Step 4: Instantiate the encryption SDK client. + // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) + if err != nil { + panic(err) + } + // Step 5: Create your encryption context (Optional). + // Remember that your encryption context is NOT SECRET. + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryptionContext := map[string]string{ + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + // Step 6a: Encrypt + // Here, we customize the Algorithm Suite that is used to Encrypt the plaintext. + // In particular, we use an Algorithm Suite without Signing. + // Signature verification adds a significant performance cost on decryption. + // If the users encrypting data and the users decrypting data are equally trusted, + // consider using an algorithm suite that does not include signing. + // See more about Digital Signatures: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey + res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ + Plaintext: []byte(exampleText), + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + AlgorithmSuiteId: &algorithmSuiteId, + }) + if err != nil { + panic(err) + } + // Validate Ciphertext and Plaintext before encryption are NOT the same + if string(res.Ciphertext) == exampleText { + panic("Ciphertext and Plaintext before encryption are the same") + } + // Step 6b: Decrypt + decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ + Ciphertext: res.Ciphertext, + EncryptionContext: encryptionContext, + Keyring: aesKeyring, + }) + if err != nil { + panic(err) + } + // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. + // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. + // Before your application uses plaintext data, verify that the encryption context that + // you used to encrypt the message is included in the encryption context that was used to + // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. + if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { + panic(err) + } + // Validate Plaintext after decryption and Plaintext before encryption ARE the same + if string(decryptOutput.Plaintext) != exampleText { + panic("Plaintext after decryption and Plaintext before encryption are NOT the same") + } + fmt.Println("Set Encryption Algorithm Suite Example Completed Successfully") +} + +func generateAes256KeyBytes() ([]byte, error) { + numOfBytes := 32 // 256 bits = 32 bytes + key := make([]byte, numOfBytes) + // Use crypto/rand for cryptographically secure random numbers + _, err := rand.Read(key) + if err != nil { + return nil, err + } + return key, nil +} + +// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. +func validateEncryptionContext(expected, actual map[string]string) error { + for expectedKey, expectedValue := range expected { + actualValue, exists := actual[expectedKey] + if !exists || actualValue != expectedValue { + return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", + expectedKey, expectedValue) + } + } + return nil +} diff --git a/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go b/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go new file mode 100644 index 000000000..63639b5e6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go @@ -0,0 +1,321 @@ +package utils + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "os" + + "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" + "github.com/aws/aws-sdk-go-v2/service/kms" +) + +const ( + testKmsRsaPublicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ +oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN +/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK +fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2 +r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9 +yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou +twIDAQAB +-----END PUBLIC KEY-----` + testKmsRsaKeyID = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" + testDefaultKMSKeyId = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + defaultKmsKeyRegion = "us-west-2" + testAlternateRegionKMSKeyId = "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" + testAlternateRegionKMSKeyRegion = "eu-central-1" + testDefaultMRKKeyId = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + defaultMRKKeyRegion = "us-east-1" + testAlternateRegionMrkKeyId = "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + alternateRegionMrkKeyRegion = "eu-west-1" + testKeyStoreKMSKeyRegion = "us-west-2" + testKeyStoreKMSKeyID = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + testLogicalKeyStoreName = "KeyStoreDdbTable" + testKeyStoreName = "KeyStoreDdbTable" + testKeyStoreRegion = "us-west-2" + defaultKMSKeyAccountID = "658956600833" + eccPrivateKeyFileNameSender = "sender_private.pem" + eccPrivateKeyFileNameRecipient = "recipient_private.pem" + eccPublicKeyFileNameRecipient = "recipient_public.pem" + kmsEccPublicKeyFileNameRecipient = "KmsEccKeyringExamplePublicKeyRecipient.pem" + kmsEccPublicKeyFileNameSender = "KmsEccKeyringExamplePublicKeySender.pem" + testKmsEcdhKeyIdP256SenderKeyId = "arn:aws:kms:us-west-2:370957321024:key/eabdf483-6be2-4d2d-8ee4-8c2583d416e9" + testKmsEcdhKeyIdP256RecipientKeyId = "arn:aws:kms:us-west-2:370957321024:key/0265c8e9-5b6a-4055-8f70-63719e09fda5" +) + +// Getter functions + +func GetKmsEcdhKeyIdP256SenderKeyId() string { + return testKmsEcdhKeyIdP256SenderKeyId +} + +func GetKmsEcdhKeyIdP256RecipientKeyId() string { + return testKmsEcdhKeyIdP256RecipientKeyId +} + +func GetKmsEccPublicKeyFileNameRecipient() string { + return kmsEccPublicKeyFileNameRecipient +} + +func GetKmsEccPublicKeyFileNameSender() string { + return kmsEccPublicKeyFileNameSender +} + +func GetEccPrivateKeyFileNameSender() string { + return eccPrivateKeyFileNameSender +} + +func GetEccPrivateKeyFileNameRecipient() string { + return eccPrivateKeyFileNameRecipient +} + +func GetEccPublicKeyFileNameRecipient() string { + return eccPublicKeyFileNameRecipient +} + +func GetRegionsOfMRKKeys() []string { + return []string{defaultMRKKeyRegion, alternateRegionMrkKeyRegion} +} + +func GetRegions() []string { + return []string{defaultKmsKeyRegion, testAlternateRegionKMSKeyRegion} +} + +func GetDefaultKmsKeyRegion() string { + return defaultKmsKeyRegion +} + +func GetDefaultMRKKeyRegion() string { + return defaultMRKKeyRegion +} + +func GetAlternateRegionMrkKeyRegion() string { + return alternateRegionMrkKeyRegion +} + +func GetAlternateRegionMrkKeyArn() string { + return testAlternateRegionMrkKeyId +} + +func GetDefaultRegionMrkKeyArn() string { + return testDefaultMRKKeyId +} + +func GetAlternateRegionKMSKeyRegion() string { + return testAlternateRegionKMSKeyRegion +} + +func GetAlternateRegionKMSKeyId() string { + return testAlternateRegionKMSKeyId +} + +func GetDefaultKMSKeyAccountID() string { + return defaultKMSKeyAccountID +} + +func GetDefaultKMSKeyId() string { + return testDefaultKMSKeyId +} + +func GetTestKmsRsaKeyID() string { + return testKmsRsaKeyID +} + +func GetKmsRSAPublicKey() []byte { + return []byte(testKmsRsaPublicKey) +} + +func GetKeyStoreRegion() string { + return testKeyStoreRegion +} + +func GetKeyStoreKMSKeyRegion() string { + return testKeyStoreKMSKeyRegion +} + +func GetKeyStoreKMSKeyID() string { + return testKeyStoreKMSKeyID +} + +func GetLogicalKeyStoreName() string { + return testLogicalKeyStoreName +} + +func GetKeyStoreName() string { + return testKeyStoreName +} + +// Utility functions + +func WriteRawEcdhEccKeys(ecdhCurveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) error { + // Safety check: Validate neither file is present + if FileExists(eccPrivateKeyFileNameSender) || + FileExists(eccPrivateKeyFileNameRecipient) || + FileExists(eccPublicKeyFileNameRecipient) { + return errors.New("WriteRawEcdhEccKeys will not overwrite existing PEM files") + } + + // Generate key pairs + _, privateKeySender, err := generateRawEccKeyPair(ecdhCurveSpec) + if err != nil { + return err + } + + publicKeyRecipient, privateKeyRecipient, err := generateRawEccKeyPair(ecdhCurveSpec) + if err != nil { + return err + } + + // Create PEM blocks + privateKeySenderPEM := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeySender, + } + + privateKeyRecipientPEM := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyRecipient, + } + + publicKeyRecipientPEM := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyRecipient, + } + + // Write private key for sender in PEM format + err = os.WriteFile( + eccPrivateKeyFileNameSender, + pem.EncodeToMemory(privateKeySenderPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write sender's private key: %w", err) + } + + // Write private key for recipient in PEM format + err = os.WriteFile( + eccPrivateKeyFileNameRecipient, + pem.EncodeToMemory(privateKeyRecipientPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write recipient's private key: %w", err) + } + + // Write public key for recipient in PEM format + err = os.WriteFile( + eccPublicKeyFileNameRecipient, + pem.EncodeToMemory(publicKeyRecipientPEM), + 0600, + ) + if err != nil { + return fmt.Errorf("failed to write recipient's public key: %w", err) + } + + return nil +} + +func LoadPublicKeyFromPEM(filename string) ([]byte, error) { + // Read the PEM file content as string + pemContent, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read PEM file: %w", err) + } + // Parse PEM block + block, _ := pem.Decode(pemContent) + + if block == nil { + return nil, fmt.Errorf("failed to decode PEM block") + } + + // The block.Bytes contains the DER encoded key + return block.Bytes, nil +} + +func FileExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +func generateRawEccKeyPair(curveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) ([]byte, []byte, error) { + // Select the appropriate elliptic curve based on the specification + var curve elliptic.Curve + switch curveSpec { + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP256: + curve = elliptic.P256() + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP384: + curve = elliptic.P384() + case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP521: + curve = elliptic.P521() + default: + return nil, nil, fmt.Errorf("unsupported curve specification: %s", curveSpec) + } + // Generate the private key + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate private key: %w", err) + } + // Extract the public key + publicKey := &privateKey.PublicKey + // Marshal the private key to bytes (X.509 PKCS#8 format) + privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, nil, fmt.Errorf("failed to marshal private key: %w", err) + } + // Marshal the public key to bytes (X.509 SPKI format) + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return nil, nil, fmt.Errorf("failed to marshal public key: %w", err) + } + return publicKeyBytes, privateKeyBytes, nil +} + +func WriteKmsEcdhEccPublicKey(eccKeyArn, publicKeyFileName string, kmsClient *kms.Client) error { + // Safety check: Validate neither file is present + if FileExists(publicKeyFileName) { + return errors.New("WriteKmsEcdhEccPublicKey will not overwrite existing PEM files") + } + // Generate public key + publicKey, err := GenerateKmsEccPublicKey(eccKeyArn, kmsClient) + if err != nil { + return fmt.Errorf("failed to generate public key: %w", err) + } + // Create PEM block + pemBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKey, + } + // Encode PEM + pemData := pem.EncodeToMemory(pemBlock) + if pemData == nil { + return errors.New("failed to encode PEM data") + } + // Write file with proper permissions + err = os.WriteFile(publicKeyFileName, pemData, 0600) + if err != nil { + return fmt.Errorf("failed to write public key file: %w", err) + } + return nil +} + +func GenerateKmsEccPublicKey(eccKeyArn string, kmsClient *kms.Client) ([]byte, error) { + ctx := context.Background() + // Get public key from KMS + response, err := kmsClient.GetPublicKey(ctx, &kms.GetPublicKeyInput{ + KeyId: &eccKeyArn, + }) + if err != nil { + return nil, fmt.Errorf("failed to get public key from KMS: %w", err) + } + // Check if public key is present + if response.PublicKey == nil { + return nil, errors.New("no public key in KMS response") + } + return response.PublicKey, nil +} From 2dd23287a6ab7036e2140700cb5cf4437fb7fad6 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 12:41:12 -0800 Subject: [PATCH 03/11] auto commit --- .github/workflows/library_go_tests.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/library_go_tests.yml b/.github/workflows/library_go_tests.yml index 8f31e011f..d52b6e467 100644 --- a/.github/workflows/library_go_tests.yml +++ b/.github/workflows/library_go_tests.yml @@ -95,4 +95,10 @@ jobs: working-directory: ${{ matrix.library }} shell: bash run: | - make test_go \ No newline at end of file + make test_go + + - name: Test Examples for Go + working-directory: ${{ matrix.library }}/runtimes/go/examples + shell: bash + run: | + go run main.go \ No newline at end of file From 9728534bbd0349fcd00f6f06fa6bed860815f442 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 12:44:22 -0800 Subject: [PATCH 04/11] remove duplicate example dir --- .../clientsupplier/clientSupplierExample.go | 163 --------- .../regionalroleclientsupplier.go | 57 ---- .../regionalroleclientsupplierconfig.go | 22 -- .../go/examples/examples/commitmentpolicy.go | 149 -------- .../requiredencryptioncontext.go | 161 --------- .../signingonlyexample.go | 137 -------- .../signingsuiteonlycmm.go | 77 ----- .../runtimes/go/examples/examples/go.mod | 47 --- .../runtimes/go/examples/examples/go.sum | 48 --- .../awskmsdiscoverykeyring.go | 189 ----------- .../awskmsdiscoverymultikeyring.go | 169 --------- .../awskmshierarchicalkeyring.go | 296 ---------------- .../branchkeysupplier.go | 45 --- .../createbranchkeyid.go | 45 --- .../sharedcacheacrosshierarchicalkeyring.go | 228 ------------- .../versionbranchkeyid.go | 93 ----- .../keyring/awskmskeyring/awskmskeyring.go | 122 ------- .../awskmsmrkdiscoverykeyring.go | 172 ---------- .../awskmsmrkdiscoverymultikeyring.go | 164 --------- .../awskmsmrkkeyring/awskmsmrkkeyring.go | 152 --------- .../awskmsmrkmultikeyring.go | 153 --------- .../awskmsmultikeyring/awskmsmultikeyring.go | 172 ---------- .../awskmsrsakeyring/awskmsrsakeyring.go | 127 ------- .../ecdh/awskmsecdhdiscoverykeyring.go | 219 ------------ .../keyring/ecdh/awskmsecdhkeyring.go | 193 ----------- .../keyring/ecdh/ephemeralrawecdhkeyring.go | 137 -------- .../ecdh/publickeyrawdiscoveryecdhkeyring.go | 218 ------------ .../examples/keyring/ecdh/rawecdhkeyring.go | 194 ----------- .../keyring/multikeyring/multikeyring.go | 233 ------------- .../keyring/rawaeskeyring/rawaeskeyring.go | 138 -------- .../keyring/rawrsakeyring/rawrasakeyring.go | 178 ---------- .../examples/limitencrypteddatakeysexample.go | 176 ---------- .../runtimes/go/examples/examples/main.go | 157 --------- .../examples/setencryptionalgorithmsuite.go | 171 ---------- .../examples/examples/utils/exampleUtils.go | 321 ------------------ 35 files changed, 5323 deletions(-) delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/go.mod delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/go.sum delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/main.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go delete mode 100644 AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go deleted file mode 100644 index d06cc467e..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/clientSupplierExample.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* - This example sets up an MRK multi-keyring and an MRK discovery - multi-keyring using a custom client supplier. - A custom client supplier grants users access to more granular - configuration aspects of their authentication details and KMS - client. In this example, we create a simple custom client supplier - that authenticates with a different IAM role based on the - region of the KMS key. - - This example creates a MRK multi-keyring configured with a custom - client supplier using a single MRK and encrypts the example_data with it. - Then, it creates a MRK discovery multi-keyring to decrypt the ciphertext. -*/ - -package clientsupplier - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func ClientSupplierExample(exampleText, mrkKeyIdEncrypt, awsAccountId string, awsRegions []string) { - // Step 1: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 2: Create your encryption context. - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 3: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create keyrings - // First Keyring: Create the multi-keyring using our custom client supplier - // defined in the RegionalRoleClientSupplier class in this directory. - // Note: RegionalRoleClientSupplier will internally use the key_arn's region - // to retrieve the correct IAM role. - awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ - ClientSupplier: &RegionalRoleClientSupplier{}, - Generator: &mrkKeyIdEncrypt, - } - awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) - if err != nil { - panic(err) - } - // Second Keyring: Create a MRK discovery multi-keyring with a custom client supplier. - // A discovery MRK multi-keyring will be composed of - // multiple discovery MRK keyrings, one for each region. - // Each component keyring has its own KMS client in a particular region. - // When we provide a client supplier to the multi-keyring, all component - // keyrings will use that client supplier configuration. - // In our tests, we make `mrk_key_id_encrypt` an MRK with a replica, and - // provide only the replica region in our discovery filter. - discoveryFilter := mpltypes.DiscoveryFilter{ - AccountIds: []string{awsAccountId}, - Partition: "aws", - } - awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ - ClientSupplier: &RegionalRoleClientSupplier{}, - Regions: awsRegions, - DiscoveryFilter: &discoveryFilter, - } - awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) - // Step 5a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkMultiKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 5b: Decrypt - // Decrypt your encrypted data using the discovery multi keyring. - // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. - // The header contains the Encrypted Data Keys (EDKs), which, if the EDK - // was encrypted by a KMS Keyring, includes the KMS Key ARN. - // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption - // is successful. - // Since every member of the Multi Keyring is a Discovery Keyring: - // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. - // For each filtered EDK, the keyring will attempt decryption with the keyring's client. - // All of this is done serially, until a success occurs or all keyrings have failed - // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt - // Multi Region Keys (MRKs) and regular KMS Keys. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkDiscoveryMultiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Test the Missing Region Exception - // (This is for demonstration; you do not need to do this in your code.) - - // Create a MRK discovery multi-keyring with a custom client supplier and a fake region. - awsKmsMrkDiscoveryMultiKeyringInputMissingRegion := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ - ClientSupplier: &RegionalRoleClientSupplier{}, - Regions: []string{"fake-region"}, - DiscoveryFilter: &discoveryFilter, - } - _, err = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInputMissingRegion) - // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception - switch err.(type) { - case mpltypes.AwsCryptographicMaterialProvidersException: - // You may choose how to handle the exception in this switch case. - default: - panic("Decryption using discovery keyring with missing region MUST raise AwsCryptographicMaterialProvidersException") - } - fmt.Println("Client Supplier Example completed successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go deleted file mode 100644 index 762cdf745..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplier.go +++ /dev/null @@ -1,57 +0,0 @@ -package clientsupplier - -import ( - "context" - - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials/stscreds" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/sts" -) - -/* - Example class demonstrating an implementation of a custom client supplier. - This particular implementation will create KMS clients with different IAM roles, - depending on the region passed. -*/ - -// RegionalRoleClientSupplier provides implementation for mpltypes.IClientSupplier -type RegionalRoleClientSupplier struct { -} - -func (this *RegionalRoleClientSupplier) GetClient(input mpltypes.GetClientInput) (kms.Client, error) { - region := input.Region - // Check if the region is supported - regionIamRoleMap := RegionIamRoleMap() - var defaultVal kms.Client - // Check if region is supported - if _, exists := regionIamRoleMap[region]; !exists { - return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{ - Message: "Region is not supported by this client supplier", - } - } - // Get the IAM role ARN associated with the region - arn := regionIamRoleMap[region] - ctx := context.TODO() - cfg, err := config.LoadDefaultConfig(ctx, - config.WithRegion(region), - ) - if err != nil { - return defaultVal, err - } - stsClient := sts.NewFromConfig(cfg) - // Create the AssumeRoleProvider - provider := stscreds.NewAssumeRoleProvider(stsClient, arn, func(o *stscreds.AssumeRoleOptions) { - o.RoleSessionName = "Go-ESDK-Client-Supplier-Example-Session" - }) - // Load AWS SDK configuration with the AssumeRoleProvider - sdkConfig, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region), config.WithCredentialsProvider(provider)) - if err != nil { - return defaultVal, mpltypes.AwsCryptographicMaterialProvidersException{Message: "failed to load AWS SDK config"} - } - // Create the KMS client - kmsClient := kms.NewFromConfig(sdkConfig) - // Return the KMS client wrapped in a custom type - return *kmsClient, nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go b/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go deleted file mode 100644 index 2c82f40fb..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/clientsupplier/regionalroleclientsupplierconfig.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package clientsupplier - -/* - File containing config for the RegionalRoleClientSupplier. - In your own code, this might be hardcoded, or reference - an external source, e.g. environment variables or AWS AppConfig. -*/ - -const ( - usEast1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-us-east-1-KMS-keys" - euWest1IamRole = "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-eu-west-1-KMS-keys" -) - -func RegionIamRoleMap() map[string]string { - return map[string]string{ - "us-east-1": usEast1IamRole, - "eu-west-1": euWest1IamRole, - } -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go b/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go deleted file mode 100644 index 2d501be4b..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/commitmentpolicy.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example configures a client with a specific commitment policy for the -AWS Encryption SDK client, then encrypts and decrypts data using an AWS KMS Keyring. - -The commitment policy in this example (ForbidEncryptAllowDecrypt) should only be -used as part of a migration from version 1.x to 2.x, or for advanced users with -specialized requirements. Most AWS Encryption SDK users should use the default -commitment policy (RequireEncryptRequireDecrypt). - -This example creates a KMS Keyring and then encrypts a custom input exampleText -with an encryption context for the commitment policy ForbidEncryptAllowDecrypt. -This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on setting your commitment policy, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy - -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package examples - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func CommitmentPolicyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = defaultKmsKeyRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultKMSKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // Build the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - - // Create one with the commitment policy RequireEncryptAllowDecrypt and another with ForbidEncryptAllowDecrypt. - // Read more about commitment policies here: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy - commitPolicyRequireEncryptRequireDecrypt := mpltypes.ESDKCommitmentPolicyRequireEncryptRequireDecrypt - commitPolicyForbidEncryptAllowDecrypt := mpltypes.ESDKCommitmentPolicyForbidEncryptAllowDecrypt - forbidEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyForbidEncryptAllowDecrypt}) - if err != nil { - panic(err) - } - requireEncryptClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{CommitmentPolicy: &commitPolicyRequireEncryptRequireDecrypt}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - // Make sure you use a non-committing algorithm with the commitment policy ForbidEncryptAllowDecrypt. - // Otherwise encrypt() will throw - // Error: AwsCryptographicMaterialProvidersError - // { - // error: InvalidAlgorithmSuiteInfoOnEncrypt - // { - // message: "Configuration conflict. Commitment policy requires only non-committing algorithm suites" - // } - // } - // By default for ForbidEncryptAllowDecrypt, the algorithm used is - // AlgAes256GcmIv12Tag16HkdfSha384EcdsaP384 which is a non-committing algorithm. - res, err := forbidEncryptClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := forbidEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Demonstrate that an EncryptionSDK that enforces Key Commitment on Decryption - // will fail to decrypt the encrypted message (as it was encrypted without Key Commitment). - _, err = requireEncryptClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - Ciphertext: res.Ciphertext, - }) - // We expect this to fail - if err == nil { - panic("Expected error but error is nil") - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Set Commitment Policy Example Completed Successfully") -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go deleted file mode 100644 index 5c763d09f..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. -A required encryption context CMM asks for required keys in the encryption context field -on encrypt such that they will not be stored on the message, but WILL be included in the header signature. -On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. -*/ - -package requiredencryptioncontext - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func RequiredEncryptionContextExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = utils.GetDefaultKmsKeyRegion() - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: utils.GetDefaultKMSKeyId(), - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context. - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - "requiredKey1": "requiredValue1", - "requiredKey2": "requiredValue2", - } - // Step 6: Create your required encryption context keys. - // These keys MUST be in your encryption context. - // These keys and their corresponding values WILL NOT be stored on the message but will be used - // for authentication. - underlyingCMM, err := matProv.CreateDefaultCryptographicMaterialsManager(context.Background(), mpltypes.CreateDefaultCryptographicMaterialsManagerInput{Keyring: awsKmsKeyring}) - if err != nil { - panic(err) - } - requiredEncryptionContextKeys := []string{} - requiredEncryptionContextKeys = append(requiredEncryptionContextKeys, "requiredKey1", "requiredKey2") - requiredEncryptionContextInput := mpltypes.CreateRequiredEncryptionContextCMMInput{ - UnderlyingCMM: underlyingCMM, - // If you pass in a keyring but no underlying cmm, it will result in a failure because only cmm is supported. - RequiredEncryptionContextKeys: requiredEncryptionContextKeys, - } - requiredEC, err := matProv.CreateRequiredEncryptionContextCMM(context.Background(), requiredEncryptionContextInput) - if err != nil { - panic(err) - } - // Step 7a: Encrypt - // NOTE: the keys "requiredKey1", and "requiredKey2" - // WILL NOT be stored in the message header, but "encryption", "is not", - // "but adds", "that can help you", and "the data you are handling" WILL be stored. - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - MaterialsManager: requiredEC, - EncryptionContext: encryptionContext, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 7b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Ciphertext: res.Ciphertext, - MaterialsManager: requiredEC, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // For demonstration attempt to decrypt your encrypted data using the same cryptographic material manager - // you used on encrypt, but we won't pass the encryption context we DID NOT store on the message. - // This will fail - _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - MaterialsManager: requiredEC, - }) - // We expect failure. - if err == nil { - panic("Decryption passed without any error when encryption context was not provided.") - } - // Decrypt your encrypted data using the same cryptographic material manager - // you used to encrypt, but supply encryption context that contains ONLY the encryption context that - // was NOT stored. This will pass. - reproducedEncryptionContext := map[string]string{ - "requiredKey1": "requiredValue1", - "requiredKey2": "requiredValue2", - } - decryptOutputreproducedEC, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: reproducedEncryptionContext, - Ciphertext: res.Ciphertext, - MaterialsManager: requiredEC, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputreproducedEC.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // You can also decrypt with the underlyingCMM, but must still provide the reproducedEncryptionContext. - decryptOutputWithCMM, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: reproducedEncryptionContext, - Ciphertext: res.Ciphertext, - MaterialsManager: underlyingCMM, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputWithCMM.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Required Encryption Context CMM Example Completed Successfully") -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go deleted file mode 100644 index 6ce7c5460..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingonlyexample.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* - Demonstrate an encrypt/decrypt cycle using a Custom Cryptographic Materials Manager (CMM). - `signingsuiteonlycmm.go` demonstrates creating a custom CMM to reject Non-Signing Algorithms. -*/ - -package restrictalgorithmsuite - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func SigningOnlyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { - // Step 1: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 2: Create the AWS KMS client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = defaultKmsKeyRegion - }) - // Step 3: Create your encryption context. - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the Aws KMS Keyring - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultKMSKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Create an instance of the custom CMM - cmm, err := NewSigningSuiteOnlyCMM(awsKmsKeyring) - if err != nil { - panic(err) - } - // Step 5a: Encrypt - algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384 - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - MaterialsManager: cmm, - AlgorithmSuiteId: &algorithmSuiteId, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 5b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - MaterialsManager: cmm, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that a Non Signing Algorithm Suite will be rejected by the CMM. - nonSigningAlgorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey - _, err = encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - MaterialsManager: cmm, - AlgorithmSuiteId: &nonSigningAlgorithmSuiteId, - }) - if err == nil { - panic("Expected error but error is nil") - } - switch err.(type) { - case mpltypes.AwsCryptographicMaterialProvidersException: - // You may choose how to handle the exception in this switch case. - default: - panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") - } - fmt.Println("SigningSuiteOnlyCMM Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go b/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go deleted file mode 100644 index 7d02d7a1b..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go +++ /dev/null @@ -1,77 +0,0 @@ -package restrictalgorithmsuite - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" -) - -type SigningSuiteOnlyCMM struct { - approvedAlgos map[mpltypes.ESDKAlgorithmSuiteId]bool - cmm mpltypes.ICryptographicMaterialsManager -} - -// NewSigningSuiteOnlyCMM creates a new SigningSuiteOnlyCMM -func NewSigningSuiteOnlyCMM(keyring mpltypes.IKeyring) (*SigningSuiteOnlyCMM, error) { - // Initialize the MPL client - materialProviders, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Create a DefaultCryptographicMaterialsManager - cmmInput := mpltypes.CreateDefaultCryptographicMaterialsManagerInput{ - Keyring: keyring, - } - cmm, err := materialProviders.CreateDefaultCryptographicMaterialsManager(context.Background(), cmmInput) - if err != nil { - return nil, err - } - // Create list of approved algorithm - var approvedAlgos = map[mpltypes.ESDKAlgorithmSuiteId]bool{ - mpltypes.ESDKAlgorithmSuiteIdAlgAes128GcmIv12Tag16HkdfSha256EcdsaP256: true, - mpltypes.ESDKAlgorithmSuiteIdAlgAes192GcmIv12Tag16HkdfSha384EcdsaP384: true, - mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384: true, - mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeyEcdsaP384: true, - } - return &SigningSuiteOnlyCMM{ - approvedAlgos: approvedAlgos, - cmm: cmm, - }, nil -} - -func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) GetEncryptionMaterials(input mpltypes.GetEncryptionMaterialsInput) (*mpltypes.GetEncryptionMaterialsOutput, error) { - // Get the algorithm suite from the input - esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) - if err != nil { - return nil, err - } - // Check if the algorithm is approved - if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { - return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} - } - // Delegate to the underlying CMM - return signingSuiteOnlyCMM.cmm.GetEncryptionMaterials(input) -} - -func getESDKAlgorithmSuite(algSuite mpltypes.AlgorithmSuiteId) (*mpltypes.AlgorithmSuiteIdMemberESDK, error) { - if esdk, ok := algSuite.(*mpltypes.AlgorithmSuiteIdMemberESDK); ok { - return esdk, nil - } - return nil, fmt.Errorf("algorithm suite is not ESDK type") -} - -func (signingSuiteOnlyCMM *SigningSuiteOnlyCMM) DecryptMaterials(input mpltypes.DecryptMaterialsInput) (*mpltypes.DecryptMaterialsOutput, error) { - // Get the algorithm suite from the input - esdkAlgorithmSuite, err := getESDKAlgorithmSuite(input.AlgorithmSuiteId) - if err != nil { - return nil, err - } - // Check if the algorithm is approved - if !signingSuiteOnlyCMM.approvedAlgos[esdkAlgorithmSuite.Value] { - return nil, mpltypes.AwsCryptographicMaterialProvidersException{Message: "Algorithm Suite must use Signing"} - } - // Delegate to the underlying CMM - return signingSuiteOnlyCMM.cmm.DecryptMaterials(input) -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod b/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod deleted file mode 100644 index 5daa093c6..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/go.mod +++ /dev/null @@ -1,47 +0,0 @@ -module github.com/aws/aws-encryption-sdk/aws-esdk-go-preview - -go 1.23.0 - -replace github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 => ../mpl - -replace ( - github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 => ../dynamodb - github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 => ../kms - github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 => ../primitives - -) - -replace github.com/dafny-lang/DafnyStandardLibGo => ../StandardLibrary/ - -replace github.com/aws/aws-encryption-sdk => ./../esdk - -require ( - github.com/aws/aws-cryptographic-material-providers-library/mpl v0.0.0 - github.com/aws/aws-cryptographic-material-providers-library/primitives v0.0.0 - github.com/aws/aws-encryption-sdk v0.0.0-00010101000000-000000000000 - github.com/aws/aws-sdk-go-v2/config v1.27.37 - github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1 - github.com/aws/aws-sdk-go-v2/credentials v1.17.35 - github.com/aws/aws-sdk-go-v2/service/kms v1.36.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 -) - -require ( - github.com/aws/aws-cryptographic-material-providers-library/dynamodb v0.0.0 // indirect - github.com/aws/aws-cryptographic-material-providers-library/kms v0.0.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // 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.11.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.23.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 // indirect - github.com/aws/smithy-go v1.21.0 // indirect - github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 // indirect - github.com/dafny-lang/DafnyStandardLibGo v0.0.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect -) diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum b/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum deleted file mode 100644 index 55861093d..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= -github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= -github.com/aws/aws-sdk-go-v2/config v1.27.37 h1:xaoIwzHVuRWRHFI0jhgEdEGc8xE1l91KaeRDsWEIncU= -github.com/aws/aws-sdk-go-v2/config v1.27.37/go.mod h1:S2e3ax9/8KnMSyRVNd3sWTKs+1clJ2f1U6nE0lpvQRg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.35 h1:7QknrZhYySEB1lEXJxGAmuD5sWwys5ZXNr4m5oEz0IE= -github.com/aws/aws-sdk-go-v2/credentials v1.17.35/go.mod h1:8Vy4kk7at4aPSmibr7K+nLTzG6qUQAUO4tW49fzUV4E= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= -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.35.1 h1:DDN8yqYzFUDy2W5zk3tLQNKaO/1t0h3fNixPJacu264= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.1/go.mod h1:k5XW8MoMxsNZ20RJmsokakvENUwQyjv69R9GqrI4xdQ= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 h1:dOxqOlOEa2e2heC/74+ZzcJOa27+F1aXFZpYgY/4QfA= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19/go.mod h1:aV6U1beLFvk3qAgognjS3wnGGoDId8hlPEiBsLHXVZE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg= -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.23.1 h1:2jrVsMHqdLD1+PA4BA6Nh1eZp0Gsy3mFSB5MxDvcJtU= -github.com/aws/aws-sdk-go-v2/service/sso v1.23.1/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 h1:0L7yGCg3Hb3YQqnSgBTZM5wepougtL1aEccdcdYhHME= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E= -github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 h1:8K0UNOkZiK9Uh3HIF6Bx0rcNCftqGCeKmOaR7Gp5BSo= -github.com/aws/aws-sdk-go-v2/service/sts v1.31.1/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= -github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= -github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1 h1:dOgaw3i0I9nWKPjfXYzEfgWsVRJykL6FA18DErvQiJQ= -github.com/dafny-lang/DafnyRuntimeGo/v4 v4.9.1/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/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go deleted file mode 100644 index e3ccfbbd2..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS Discovery Keyring -AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. -The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring -for AWS KMS multi-Region keys. For information about using multi-Region keys with the -AWS Encryption SDK, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks -Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. -If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt -operation fails. -When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt -any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or -has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt -permission on the AWS KMS key. -This example creates a KMS Keyring and then encrypts a custom input exampleText -with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. -This example also includes some sanity checks for demonstration: - 1. Ciphertext and plaintext data are not the same - 2. Decrypted plaintext value matches exampleText - 3. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to - which the KMS key used for encryption belongs -These sanity checks are for demonstration in the example only. You do not need these in your code. -For more information on how to use KMS Discovery keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package awskmsdiscoverykeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsDiscoveryKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient( - mpltypes.MaterialProvidersConfig{}, - ) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // Although this example highlights Discovery keyrings, Discovery keyrings cannot - // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. - // So, we create two keyrings, one for encrypt and another for decrypt - // First Keyring: Create a AwsKmsKeyring to use for encryption - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultKmsKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Second Keyring: Create a Discovery keyring to use for decryption. - // We'll add a discovery filter so that we limit - // the set of ciphertexts we are willing to decrypt to only ones created by KMS keys in our account and - // partition. - discoveryFilter := mpltypes.DiscoveryFilter{ - AccountIds: []string{defaultKMSKeyAccountID}, - Partition: "aws", - } - awsKmsDiscoveryKeyringInput := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ - KmsClient: kmsClient, - DiscoveryFilter: &discoveryFilter, - } - awsKmsDiscoveryKeyring, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - AlgorithmSuiteId: &algorithmSuiteID, - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. - // The header contains the Encrypted Data Keys (EDKs), which, if the EDK - // was encrypted by a KMS Keyring, includes the KMS Key ARN. - // The Discovery Keyring filters these EDKs for - // EDKs encrypted by Single Region OR Multi Region KMS Keys. - // If a Discovery Filter is present, these KMS Keys must belong - // to an AWS Account ID in the discovery filter's AccountIds and - // must be from the discovery filter's partition. - // Finally, KMS is called to decrypt each filtered EDK until an EDK is - // successfully decrypted. The resulting data key is used to decrypt the - // ciphertext's message. - // If all calls to KMS fail, the decryption fails. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: awsKmsDiscoveryKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate that if a different discovery keyring doesn't have the correct - // AWS Account ID's, the decrypt will fail with an error message - // Note that this assumes Account ID used here ('888888888888') is different than the one used - // during encryption - discoveryFilterFailureCase := mpltypes.DiscoveryFilter{ - AccountIds: []string{"888888888888"}, - Partition: "aws", - } - awsKmsDiscoveryKeyringInputFailureCase := mpltypes.CreateAwsKmsDiscoveryKeyringInput{ - KmsClient: kmsClient, - DiscoveryFilter: &discoveryFilterFailureCase, - } - awsKmsDiscoveryKeyringFailureCase, err := matProv.CreateAwsKmsDiscoveryKeyring(context.Background(), awsKmsDiscoveryKeyringInputFailureCase) - _, err = encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: awsKmsDiscoveryKeyringFailureCase, - Ciphertext: res.Ciphertext, - }) - // We expected error in failure case - if err == nil { - panic("Expected failure case to fail") - } - fmt.Println("AWS KMS Discovery Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go deleted file mode 100644 index d9682072c..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsdiscoverymultikeyring/awskmsdiscoverymultikeyring.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption -using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. -The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring -for AWS KMS multi-Region keys. For information about using multi-Region keys with the -AWS Encryption SDK, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks -Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. -If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt -operation fails. -When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt -any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or -has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt -permission on the AWS KMS key. -This example creates a KMS Keyring and then encrypts a custom input exampleText -with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi -keyring. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -For more information on how to use KMS Discovery keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package awskmsdiscoverymultikeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsDiscoveryMultiKeyringExample( - exampleText string, - defaultKmsKeyId string, - defaultKMSKeyAccountID string, - regions []string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient( - mpltypes.MaterialProvidersConfig{}, - ) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // Although this example highlights Discovery keyrings, Discovery keyrings cannot - // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. - // So, we create two keyrings, one for encrypt and another for decrypt - // First Keyring: Create a AwsKmsKeyring to use for encryption - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultKmsKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Second Keyring: Create a Discovery keyring to use for decryption. - // We'll add a discovery filter so that we limit the set of ciphertexts we are willing to - // decrypt to only ones created by KMS keys in our account and partition. - discoveryFilter := mpltypes.DiscoveryFilter{ - AccountIds: []string{defaultKMSKeyAccountID}, - Partition: "aws", - } - awsKmsDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsDiscoveryMultiKeyringInput{ - Regions: regions, - DiscoveryFilter: &discoveryFilter, - } - awsKmsDiscoveryMultiKeyring, err := matProv.CreateAwsKmsDiscoveryMultiKeyring(context.Background(), awsKmsDiscoveryMultiKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - AlgorithmSuiteId: &algorithmSuiteID, - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. - // The header contains the Encrypted Data Keys (EDKs), which, if the EDK - // was encrypted by a KMS Keyring, includes the KMS Key ARN. - // The Discovery Keyring filters these EDKs for - // EDKs encrypted by Single Region OR Multi Region KMS Keys. - // If a Discovery Filter is present, these KMS Keys must belong - // to an AWS Account ID in the discovery filter's AccountIds and - // must be from the discovery filter's partition. - // Finally, KMS is called to decrypt each filtered EDK until an EDK is - // successfully decrypted. The resulting data key is used to decrypt the - // ciphertext's message. - // If all calls to KMS fail, the decryption fails. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: awsKmsDiscoveryMultiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err := validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same.") - } - fmt.Println("AWS KMS Discovery Multi Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go deleted file mode 100644 index 024598d8c..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/awskmshierarchicalkeyring.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* - This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" - keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these - branch keys are themselves protected by a KMS Key. - - Establishing a key hierarchy like this has two benefits: - First, by caching the branch key material, and only calling KMS to re-establish authentication - regularly according to your configured TTL, you limit how often you need to call KMS to protect - your data. This is a performance security tradeoff, where your authentication, audit, and logging - from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail - cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has - Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, - the benefit is that you no longer have to make a network call to KMS for every encrypt or - decrypt. - - Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a - multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect - the tenant's data. You can either statically configure a single branch key to ensure you are - restricting access to a single tenant, or you can implement an interface that selects the Branch - Key based on the Encryption Context. - - This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to - encrypt and decrypt data for two separate tenants. - - This example requires access to the DDB Table where you are storing the Branch Keys. 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). - - This example also requires using a KMS Key. You need the following access on this key: - - GenerateDataKeyWithoutPlaintext - - Decrypt - - For more information on how to use Hierarchical Keyrings, see - https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-hierarchical-keyring.html -*/ - -package awskmshierarchicalkeyring - -import ( - "context" - "fmt" - - keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" - keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "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/kms" -) - -func AwsKmsHKeyExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { - // Step 1: Create the aws sdk clients - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - // Step 1a: Create the aws kms client - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = keyStoreKMSKeyRegion - }) - // Step 1b: Create the ddb client - ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { - options.Region = keyStoreRegion - }) - // Step 2: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 2: Create the keystore to manage the tenant keys - // This SHOULD be the same configuration that you used - // to initially create and populate your KeyStore. - kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ - Value: keyStoreKMSKeyID, - } - keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ - DdbTableName: keyStoreName, - KmsConfiguration: &kmsConfig, - LogicalKeyStoreName: logicalKeyStoreName, - DdbClient: ddbClient, - KmsClient: kmsClient, - }) - if err != nil { - panic(err) - } - // Step 3: Create two new branch keys - branchKeyA, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) - if err != nil { - panic(err) - } - branchKeyB, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) - if err != nil { - panic(err) - } - // Step 4: Create a branch key supplier that maps the branch key id to a more readable format - // See branchkeysupplier.go in this package for the branchKeySupplier structure - keySupplier := branchKeySupplier{branchKeyA: branchKeyA, branchKeyB: branchKeyB} - // Step 5: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 6: Create the Hierarchical Keyring. - hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ - KeyStore: keyStore, - BranchKeyIdSupplier: &keySupplier, - TtlSeconds: 600, - } - hKeyRing, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) - if err != nil { - panic(err) - } - // Step 7: Create encryption context for both tenants. - // The Branch Key Id supplier uses the encryption context to determine which branch key id will - // be used to encrypt data. - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - - // Create encryption context for TenantA - encryptionContextA := map[string]string{ - "tenant": "TenantA", - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Create encryption context for TenantB - encryptionContextB := map[string]string{ - "tenant": "TenantB", - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 8a: Encrypt the data - // Encrypt data for Tenant A - encryptOutputA, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampletext), - EncryptionContext: encryptionContextA, - Keyring: hKeyRing, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(encryptOutputA.Ciphertext) == exampletext { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Encrypt data for Tenant B - encryptOutputB, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampletext), - EncryptionContext: encryptionContextB, - Keyring: hKeyRing, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(encryptOutputB.Ciphertext) == exampletext { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 8b: Decrypt the data with various scenerios for demonstration - - // For demonstration, let's attest that TenantKeyB cannot decrypt a message written by TenantKeyA, - // and vice versa and construct more restrictive hierarchical keyrings. - hkeyringInputA := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ - KeyStore: keyStore, - BranchKeyId: &branchKeyA, - TtlSeconds: 600, - } - hKeyRingA, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputA) - if err != nil { - panic(err) - } - hkeyringInputB := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ - KeyStore: keyStore, - BranchKeyId: &branchKeyB, - TtlSeconds: 600, - } - hKeyRingB, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInputB) - if err != nil { - panic(err) - } - // Demonstrate that data encrypted by one tenant's key - // cannot be decrypted with by a keyring specific to another tenant. - - // Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key - // This will fail and raise a AwsCryptographicMaterialProvidersException, - // which we swallow ONLY for demonstration purposes. - _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: encryptOutputA.Ciphertext, - EncryptionContext: encryptionContextA, - Keyring: hKeyRingB, - }) - if err == nil { - panic("Expected error did not occur") - } - switch err.(type) { - case mpltypes.AwsCryptographicMaterialProvidersException: - // You may choose how to handle the exception in this switch case. - default: - panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") - } - // Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. - // This will fail and raise a AwsCryptographicMaterialProvidersException, - // which we swallow ONLY for demonstration purposes. - _, err = client.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: encryptOutputB.Ciphertext, - EncryptionContext: encryptionContextA, - Keyring: hKeyRingA, - }) - if err == nil { - panic("Expected error did not occur") - } - switch err.(type) { - case mpltypes.AwsCryptographicMaterialProvidersException: - // You may choose how to handle the exception in this switch case. - default: - panic("error is expected to be a type of AwsCryptographicMaterialProvidersException") - } - // Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, - // and that the decrypted data matches the input data. - - // For tenant A - decryptOutputA, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: encryptOutputA.Ciphertext, - EncryptionContext: encryptionContextA, - Keyring: hKeyRingA, - }) - if err != nil { - panic(err) - } - // If you are not specifying the encryption context on decrypt. Its recommended to check if the encryption context matches. - // Although, we are specifying the encryption context on decrypt, only for demonstration we are validating the encryption context. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContextA, decryptOutputA.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputA.Plaintext) != exampletext { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // For tenant B - decryptOutputB, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: encryptOutputB.Ciphertext, - EncryptionContext: encryptionContextB, - Keyring: hKeyRingB, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContextB, decryptOutputB.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputB.Plaintext) != exampletext { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputA.Plaintext) != exampletext { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Aws Kms Hierarchical Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go deleted file mode 100644 index fa066fd70..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/branchkeysupplier.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package awskmshierarchicalkeyring - -import ( - "fmt" - - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" -) - -/* -Demonstrates how to create a BranchKeyIdSupplier. - -The BranchKeyIdSupplier determines which Branch Key is used -to protect or access data. -It is an important component in a Multi-tenant solution, -where each tenant is cryptographically isolated. -The Branch Key ID Supplier uses the Encryption Context -provided at Encrypt or Decrypt -to determine what "shared secret" (Branch Key) -is used. -*/ - -type branchKeySupplier struct { - branchKeyA string - branchKeyB string -} - -func (b *branchKeySupplier) GetBranchKeyId(input mpltypes.GetBranchKeyIdInput) (*mpltypes.GetBranchKeyIdOutput, error) { - // We MUST use the encryption context to determine - // the Branch Key ID. - ec := input.EncryptionContext - if value, exists := ec["tenant"]; !exists || value == "" { - return nil, fmt.Errorf("EncryptionContext invalid, does not contain expected tenant key value pair.") - } - branchKeyIdentifier := ec["tenant"] - if branchKeyIdentifier == "TenantA" { - return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyA}, nil - } else if branchKeyIdentifier == "TenantB" { - return &mpltypes.GetBranchKeyIdOutput{BranchKeyId: b.branchKeyB}, nil - } else { - return &mpltypes.GetBranchKeyIdOutput{}, fmt.Errorf("unknown branch key identifier") - } -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go deleted file mode 100644 index f236e9ac4..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/createbranchkeyid.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package awskmshierarchicalkeyring - -import ( - "context" - - keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" - keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/service/dynamodb" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -/* - The Hierarchical Keyring Example relies on the existence - of a DDB-backed key store with pre-existing - branch key material. - - This example demonstrates configuring a KeyStore and creating a branch key. -*/ - -func createbranchkeyid(keyStoreTableName, logicalKeyStoreName, kmsKeyArn string, ddbClient *dynamodb.Client, kmsClient *kms.Client) (string, error) { - // 1. Create the keystore - // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. - kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ - Value: kmsKeyArn, - } - keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ - DdbTableName: keyStoreTableName, - KmsConfiguration: &kmsConfig, - LogicalKeyStoreName: logicalKeyStoreName, - DdbClient: ddbClient, - KmsClient: kmsClient, - }) - if err != nil { - return "", err - } - // 2. Create a branch key identifier with the AWS KMS Key configured in the KeyStore Configuration. - branchKey, err := keyStore.CreateKey(context.Background(), keystoretypes.CreateKeyInput{}) - if err != nil { - return "", err - } - return branchKey.BranchKeyIdentifier, nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go deleted file mode 100644 index 2aead2ccb..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/sharedcacheacrosshierarchicalkeyring.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* - This example demonstrates how to use a shared cache across multiple Hierarchical Keyrings. - With this functionality, users only need to maintain one common shared cache across multiple - Hierarchical Keyrings with different Key Stores instances/KMS Clients/KMS Keys. - - If you want to use a Shared Cache, you need to initialize it only once, and - pass the same cache `shared_cache` to different hierarchical keyrings. - - There are three important parameters that users need to carefully set while providing the shared cache: - - 1. Partition ID - Partition ID is an optional parameter provided to the Hierarchical Keyring input, - which distinguishes Cryptographic Material Providers (i.e: Keyrings) writing to a cache. - - If the Partition ID is set and is the same for two Hierarchical Keyrings (or another Material Provider), - they CAN share the same cache entries in the cache. - - If the Partition ID is set and is different for two Hierarchical Keyrings (or another Material Provider), - they CANNOT share the same cache entries in the cache. - - If the Partition ID is not set by the user, it is initialized as a random 16-byte UUID which makes - it unique for every Hierarchical Keyring, and two Hierarchical Keyrings (or another Material Provider) - CANNOT share the same cache entries in the cache. - - 2. Logical Key Store Name - This parameter is set by the user when configuring the Key Store for - the Hierarchical Keyring. This is a logical name for the branch key store. - Suppose you have a physical Key Store (K). You create two instances of K (K1 and K2). Now, you create - two Hierarchical Keyrings (HK1 and HK2) with these Key Store instances (K1 and K2 respectively). - - If you want to share cache entries across these two keyrings, you should set the Logical Key Store Names - for both the Key Store instances (K1 and K2) to be the same. - - If you set the Logical Key Store Names for K1 and K2 to be different, HK1 (which uses Key Store instance K1) - and HK2 (which uses Key Store instance K2) will NOT be able to share cache entries. - - 3. Branch Key ID - Choose an effective Branch Key ID Schema - - This is demonstrated in the example below. - Notice that both K1 and K2 are instances of the same physical Key Store (K). - You MUST NEVER have two different physical Key Stores with the same Logical Key Store Name. - - Important Note: If you have two or more Hierarchy Keyrings with: - - Same Partition ID - - Same Logical Key Store Name of the Key Store for the Hierarchical Keyring - - Same Branch Key ID - then they WILL share the cache entries in the Shared Cache. - Please make sure that you set all of Partition ID, Logical Key Store Name and Branch Key ID - to be the same for two Hierarchical Keyrings if and only if you want them to share cache entries. - - This example first creates a shared cache that you can use across multiple Hierarchical Keyrings. - The example then configures a Hierarchical Keyring (HK1 and HK2) with the shared cache, - a Branch Key ID and two instances (K1 and K2) of the same physical Key Store (K) respectively, - i.e. HK1 with K1 and HK2 with K2. The example demonstrates that if you set the same Partition ID - for HK1 and HK2, the two keyrings can share cache entries. - If you set different Partition ID of the Hierarchical Keyrings, or different - Logical Key Store Names of the Key Store instances, then the keyrings will NOT - be able to share cache entries. - - This example requires access to the DDB Table (K) where you are storing the Branch Keys. 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) - - This example also requires using a KMS Key. You need the following access on this key: - - GenerateDataKeyWithoutPlaintext - - Decrypt -*/ - -package awskmshierarchicalkeyring - -import ( - "context" - "fmt" - - keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" - keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "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/kms" -) - -func SharedCacheExample(exampletext, keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) { - // Step 1: Create the aws sdk clients - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - fmt.Println(err) - panic(err) - } - // Step 1a: Create the aws kms client - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = keyStoreKMSKeyRegion - }) - // Step 1b: Create the ddb client - ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { - options.Region = keyStoreRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the CryptographicMaterialsCache (CMC) to share across multiple Hierarchical Keyrings - // using the Material Providers Library - // This CMC takes in: - // - CacheType - cache := mpltypes.CacheTypeMemberDefault{ - Value: mpltypes.DefaultCache{ - EntryCapacity: 100, - }, - } - cmcCacheInput := mpltypes.CreateCryptographicMaterialsCacheInput{ - Cache: &cache, - } - sharedCryptographicMaterialsCache, err := matProv.CreateCryptographicMaterialsCache(context.Background(), cmcCacheInput) - if err != nil { - panic(err) - } - // Step 4: Create a CacheType object for the sharedCryptographicMaterialsCache - // Note that the `cache` parameter in the Hierarchical Keyring Input takes a `CacheType` as input - // Here, we pass a `Shared` CacheType that passes an already initialized shared cache. - - // If you want to use a Shared Cache, you need to initialize it only once, and - // pass the same cache `shared_cache` to different hierarchical keyrings. - - // CryptographicMaterialsCacheRef is an Rc (Reference Counted), so if you clone it to - // pass it to different Hierarchical Keyrings, it will still point to the same - // underlying cache, and increment the reference count accordingly. - shared_cache := mpltypes.CacheTypeMemberShared{sharedCryptographicMaterialsCache} - // Step 2: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - client, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Configure your Key Store resource keyStore1. - // This SHOULD be the same configuration that you used - // to initially create and populate your physical Key Store. - // Note that key_store_table_name is the physical Key Store, - // and key_store1 is instances of this physical Key Store. - kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ - Value: keyStoreKMSKeyID, - } - keyStore1, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ - DdbTableName: keyStoreName, - KmsConfiguration: &kmsConfig, - LogicalKeyStoreName: logicalKeyStoreName, - DdbClient: ddbClient, - KmsClient: kmsClient, - }) - if err != nil { - panic(err) - } - // Step 6: Call create_branch_key_id to create one new branch key - branchKeyId, err := createbranchkeyid(keyStoreName, logicalKeyStoreName, keyStoreKMSKeyID, ddbClient, kmsClient) - if err != nil { - panic(err) - } - // Step 7: Create the Hierarchical Keyring HK1 with Key Store instance K1, partition_id, - // the shared_cache and the branch_key_id. - // Note that we are now providing an already initialized shared cache instead of just mentioning - // the cache type and the Hierarchical Keyring initializing a cache at initialization. - // partition_id for this example is a random UUID - partitionId := "91c1b6a2-6fc3-4539-ad5e-938d597ed730" - // Please make sure that you read the guidance on how to set Partition ID, Logical Key Store Name and - // Branch Key ID at the top of this example before creating Hierarchical Keyrings with a Shared Cache - hkeyringInput := mpltypes.CreateAwsKmsHierarchicalKeyringInput{ - KeyStore: keyStore1, - BranchKeyId: &branchKeyId, - TtlSeconds: 600, - Cache: &shared_cache, - PartitionId: &partitionId, - } - keyring1, err := matProv.CreateAwsKmsHierarchicalKeyring(context.Background(), hkeyringInput) - // Step 8: Create encryption context for both tenants. - // The Branch Key Id supplier uses the encryption context to determine which branch key id will - // be used to encrypt data. - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - - // Create encryption context for TenantA - encryptionContext := map[string]string{ - "tenant": "TenantA", - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 9: Encrypt the data for encryptionContext using keyring1 - encryptOutput, err := client.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampletext), - EncryptionContext: encryptionContext, - Keyring: keyring1, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(encryptOutput.Ciphertext) == exampletext { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 10: Decrypt your encrypted data using the same keyring HK1 you used on encrypt. - decryptOutput, err := client.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: encryptOutput.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: keyring1, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampletext { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Shared Cache Example Completed Successfully") -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go deleted file mode 100644 index f12f48715..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmshierarchicalkeyring/versionbranchkeyid.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package awskmshierarchicalkeyring - -import ( - "context" - "fmt" - - keystore "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygenerated" - keystoretypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographykeystoresmithygeneratedtypes" - "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/kms" -) - -/* -This example demonstrates configuring a KeyStore and then -uses a helper method to version a branch key. -*/ -func versionBranchKeyId(keyStoreTableName, logicalKeyStoreName, kmsKeyArn, branchKeyId string) error { - // Load the AWS SDK configuration - cfg, err := config.LoadDefaultConfig(context.Background()) - if err != nil { - return err - } - // Create DDB and KMS clients - ddbClient := dynamodb.NewFromConfig(cfg) - kmsClient := kms.NewFromConfig(cfg) - // Create the keystore - // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. - kmsConfig := keystoretypes.KMSConfigurationMemberkmsKeyArn{ - Value: kmsKeyArn, - } - keyStore, err := keystore.NewClient(keystoretypes.KeyStoreConfig{ - DdbTableName: keyStoreTableName, - KmsConfiguration: &kmsConfig, - LogicalKeyStoreName: logicalKeyStoreName, - DdbClient: ddbClient, - KmsClient: kmsClient, - }) - if err != nil { - return err - } - // To version a branch key you MUST have access to kms:ReEncrypt* and kms:GenerateDataKeyWithoutPlaintext - _, err = keyStore.VersionKey(context.Background(), keystoretypes.VersionKeyInput{ - BranchKeyIdentifier: branchKeyId, - }) - if err != nil { - return err - } - return nil -} - -// Function to test versionBranchKeyId in main.go in examples directory -func CreateAndVersionBranchKeyId(keyStoreKMSKeyRegion, keyStoreRegion, keyStoreKMSKeyID, keyStoreName, logicalKeyStoreName string) error { - // Create the aws sdk clients - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - // Create the aws kms client - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = keyStoreKMSKeyRegion - }) - // Create the ddb client - ddbClient := dynamodb.NewFromConfig(cfg, func(options *dynamodb.Options) { - options.Region = keyStoreRegion - }) - // create branch key ID - branchKeyId, err := createbranchkeyid( - keyStoreName, - logicalKeyStoreName, - keyStoreKMSKeyID, - ddbClient, - kmsClient, - ) - if err != nil { - panic(err) - } - // Version Branch Key - err = versionBranchKeyId( - keyStoreName, - logicalKeyStoreName, - keyStoreKMSKeyID, - branchKeyId, - ) - if err != nil { - panic(err) - } - fmt.Println("Create and version branchKey Id Example Completed Successfully") - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go deleted file mode 100644 index 246b8d9ac..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmskeyring/awskmskeyring.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS Keyring -The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and -decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings -of the same or a different type. -For more information on how to use KMS keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package awskmskeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsKeyringExample(exampleText string, defaultKmsKeyId string, defaultKMSKeyAccountID string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultKmsKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go deleted file mode 100644 index 6822bbcfc..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS MRK (multi-region key) Discovery Keyring -The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. -When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt -any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or -has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt -permission on the AWS KMS MRK. -The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring -for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring -can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, -the encrypt operation fails. -The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to -create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). -This example creates a KMS MRK Keyring and then encrypts a custom input exampleText -with an encryption context. This encrypted ciphertext is then decrypted using an -MRK Discovery keyring. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -For information about using multi-Region keys with the AWS Encryption SDK, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks -For more info on KMS MRKs (multi-region keys), see the KMS documentation: -https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html -For more information on how to use KMS Discovery keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ -package awskmsmrkdiscoverykeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsMrkDiscoveryKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion, defaultKMSKeyAccountID string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = defaultMRKKeyRegion - }) - kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = alternateRegionMrkKeyRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // Though this example highlights Discovery keyrings, Discovery keyrings - // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. - // So, we create two keyrings. One for encryption, second one for decryption - // First Keyring: Create KMS MRK Keyring used for encryption - awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ - KmsClient: kmsClientEncrypt, - KmsKeyId: defaultRegionMrkKeyArn, - } - awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) - if err != nil { - panic(err) - } - // Second Keyring: create a Discovery keyring to use for decryption. - discoveryFilter := mpltypes.DiscoveryFilter{ - AccountIds: []string{defaultKMSKeyAccountID}, - Partition: "aws", - } - // In order to illustrate the MRK behavior of this keyring, we configure - // the keyring to use the second KMS region where the MRK is replicated to. - // This example assumes you have already replicated your key, but since we - // are using a discovery keyring, we don't need to provide the mrk replica key id - awsKmsMrkDiscoveryInput := mpltypes.CreateAwsKmsMrkDiscoveryKeyringInput{ - KmsClient: kmsClientDecrypt, - Region: alternateRegionMrkKeyRegion, - DiscoveryFilter: &discoveryFilter, - } - awsKmsMrkDiscoveryKeyring, err := matProv.CreateAwsKmsMrkDiscoveryKeyring(context.Background(), awsKmsMrkDiscoveryInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyringEncrypt, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption ARE the same") - } - // Step 6b: Decrypt - // Create a Discovery keyring to use for decryption. - // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. - // The header contains the Encrypted Data Keys (EDKs), which, if the EDK - // was encrypted by a KMS Keyring, includes the KMS Key ARN. - // The Discovery Keyring filters these EDKs for - // EDKs encrypted by Single Region OR Multi Region KMS Keys. - // If a Discovery Filter is present, these KMS Keys must belong - // to an AWS Account ID in the discovery filter's AccountIds and - // must be from the discovery filter's partition. - // Finally, KMS is called to decrypt each filtered EDK until an EDK is - // successfully decrypted. The resulting data key is used to decrypt the - // ciphertext's message. - // If all calls to KMS fail, the decryption fails. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkDiscoveryKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS MRK Discovery Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go deleted file mode 100644 index 54dbdf17a..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkdiscoverymultikeyring/awskmsmrkdiscoverymultikeyring.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS MRK (multi-region key) Discovery Multi Keyring -AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings. -The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. -When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt -any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or -has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt -permission on the AWS KMS MRK. -The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring -for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring -can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, -the encrypt operation fails. -The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to -create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). -This example creates a KMS MRK Keyring and then encrypts a custom input exampleText -with an encryption context. This encrypted ciphertext is then decrypted using an -MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -For information about using multi-Region keys with the AWS Encryption SDK, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks -For more info on KMS MRKs (multi-region keys), see the KMS documentation: -https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html -For more information on how to use KMS Discovery keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ -package awskmsmrkdiscoverymultikeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsMrkDiscoveryMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, defaultMRKKeyRegion, defaultKMSKeyAccountID string, regionsOfMRKKeys []string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = defaultMRKKeyRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // Though this example highlights Discovery keyrings, Discovery keyrings - // cannot be used to encrypt, so for encryption we create a KMS MRK keyring. - // So, we create two keyrings. One for encryption, second one for decryption - // First Keyring: Create KMS MRK Keyring used for encryption - awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: defaultRegionMrkKeyArn, - } - awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) - if err != nil { - panic(err) - } - // Second Keyring: Create a MRK Discovery Multi Keyring to use for decryption - // We'll add a discovery filter to limit the set of encrypted data keys - // we are willing to decrypt to only ones created by KMS keys in select - // accounts and the partition `aws`. - // MRK Discovery keyrings also filter encrypted data keys by the region - // the keyring is created with. - discoveryFilter := mpltypes.DiscoveryFilter{ - AccountIds: []string{defaultKMSKeyAccountID}, - Partition: "aws", - } - awsKmsMrkDiscoveryMultiKeyringInput := mpltypes.CreateAwsKmsMrkDiscoveryMultiKeyringInput{ - Regions: regionsOfMRKKeys, - DiscoveryFilter: &discoveryFilter, - } - awsKmsMrkDiscoveryMultiKeyring, err := matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(context.Background(), awsKmsMrkDiscoveryMultiKeyringInput) - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. - // The header contains the Encrypted Data Keys (EDKs), which, if the EDK - // was encrypted by a KMS Keyring, includes the KMS Key ARN. - // The Discovery Keyring filters these EDKs for - // EDKs encrypted by Single Region OR Multi Region KMS Keys. - // If a Discovery Filter is present, these KMS Keys must belong - // to an AWS Account ID in the discovery filter's AccountIds and - // must be from the discovery filter's partition. - // Finally, KMS is called to decrypt each filtered EDK until an EDK is - // successfully decrypted. The resulting data key is used to decrypt the - // ciphertext's message. - // If all calls to KMS fail, the decryption fails. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: awsKmsMrkDiscoveryMultiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS MRK Discovery Multi Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go deleted file mode 100644 index 814a51328..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkkeyring/awskmsmrkkeyring.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS MRK (multi-region key) Keyring -The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to -create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). -This example creates a KMS MRK Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings -of the same or a different type. -For more information on how to use KMS keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html -For more info on KMS MRK (multi-region keys), see the KMS documentation: -https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ -package awskmsmrkkeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsMrkKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultMRKKeyRegion, alternateRegionMrkKeyRegion string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClientEncrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = defaultMRKKeyRegion - }) - kmsClientDecrypt := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = alternateRegionMrkKeyRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyrings - // Create one keyring for encrypt with KMS client on defaultMRKKeyRegion region - // Create second keyring for decrypt with KMS client on alternateRegionMrkKeyRegion region. - // In order to illustrate the MRK behavior, we are creating two keyrings with two different regions - awsKmsMrkKeyringInputEncrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ - KmsClient: kmsClientEncrypt, - KmsKeyId: defaultRegionMrkKeyArn, - } - awsKmsMrkKeyringInputDecrypt := mpltypes.CreateAwsKmsMrkKeyringInput{ - KmsClient: kmsClientDecrypt, - KmsKeyId: alternateRegionMrkKeyArn, - } - awsKmsMrkKeyringEncrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputEncrypt) - if err != nil { - panic(err) - } - awsKmsMrkKeyringDecrypt, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInputDecrypt) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyringEncrypt, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - // 1. Decrypt with the same keyring (same region) as encrypt - decryptOutputSameRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyringEncrypt, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputSameRegion.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // 2. Decrypt with different keyring on different region. - decryptOutputDifferentRegion, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyringDecrypt, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutputDifferentRegion.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputDifferentRegion.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS MRK Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go deleted file mode 100644 index 76a09046f..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS MRK (multi-region key) Multi Keyring -The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to -create, encrypt, and decrypt data keys with AWS KMS MRK keys. -The KMS MRK multi-keyring consists of one or more individual keyrings of the -same or different type. The keys can either be regular KMS keys or MRKs. -The effect is like using several keyrings in a series. -This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id -as a child key, and then encrypts a custom input exampleText with an encryption context. -Either KMS Key individually is capable of decrypting data encrypted under this keyring. -This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -3. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the - MRK (from the multi-keyring used for encryption) copied from the first region into - the second region -These sanity checks are for demonstration in the example only. You do not need these in your code. -For more information on how to use KMS keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html -For more info on KMS MRK (multi-region keys), see the KMS documentation: -https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ -package awskmsmrkmultikeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsMrkMultiKeyringExample(exampleText, defaultRegionMrkKeyArn, alternateRegionMrkKeyArn, defaultKMSKeyId, alternateRegionMrkKeyRegion string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = alternateRegionMrkKeyRegion - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - defaultMrkKey := defaultRegionMrkKeyArn - // Step 3: Create the keyring - awsKmsMrkKeyringMultiInput := mpltypes.CreateAwsKmsMrkMultiKeyringInput{ - Generator: &defaultMrkKey, - KmsKeyIds: []string{defaultKMSKeyId}, - } - awsKmsMrkMultiKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), awsKmsMrkKeyringMultiInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkMultiKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutputMulti, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkMultiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputMulti.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that a single AwsKmsMrkKeyring configured with a replica of a MRK from the - // multi-keyring used to encrypt the data is also capable of decrypting the data. - awsKmsMrkKeyringInput := mpltypes.CreateAwsKmsMrkKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: alternateRegionMrkKeyArn, - } - awsKmsMrkKeyring, err := matProv.CreateAwsKmsMrkKeyring(context.Background(), awsKmsMrkKeyringInput) - if err != nil { - panic(err) - } - decryptOutputMrk, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMrkKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutputMrk.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputMrk.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS MRK Multi Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go deleted file mode 100644 index ea6710deb..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsmultikeyring/awskmsmultikeyring.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. - -A multi-keyring is a keyring that consists of one or more individual keyrings of the -same or a different type. The effect is like using several keyrings in a series. -When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its -keyrings can decrypt that data. - -When you create a multi-keyring to encrypt data, you designate one of the keyrings as -the generator keyring. All other keyrings are known as child keyrings. The generator keyring -generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the -child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext -key and one encrypted data key for each wrapping key in the multi-keyring. If you create a -multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. -If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates -and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, -and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. - -When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted -data keys. The keyrings are called in the order that they are specified in the multi-keyring. -Processing stops as soon as any key in any keyring can decrypt an encrypted data key. - -This example creates a Multi Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decryption of ciphertext is possible using the multi_keyring, - and every one of the keyrings from the multi_keyring separately -3. All decrypted plaintext value match exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -This example creates a multi_keyring using a KMS keyring as generator keyring and -another KMS keyring as a child keyring. - -For more information on how to use Multi keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html - -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package awskmsmultikeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsMultiKeyringExample(exampleText, defaultKMSKeyId, alternateRegionKMSKeyId, alternateRegionKMSKeyRegion string) { - // Step 1: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 2: Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. - // Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. - generatorKeyId := defaultKMSKeyId - awsKmsMultiKeyringInput := mpltypes.CreateAwsKmsMultiKeyringInput{ - Generator: &generatorKeyId, - KmsKeyIds: []string{alternateRegionKMSKeyId}, - } - awsKmsMultiKeyring, err := matProv.CreateAwsKmsMultiKeyring(context.Background(), awsKmsMultiKeyringInput) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsMultiKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 5b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsMultiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that a single AwsKmsKeyring configured with either KMS key - // is also capable of decrypting the data. - // Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = alternateRegionKMSKeyRegion - }) - // Create a single AwsKmsKeyring with the KMS key from our second region. - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: alternateRegionKMSKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - decryptOutputKmsKeyring, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutputKmsKeyring.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputKmsKeyring.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("KMS Multi Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go deleted file mode 100644 index 4d6ba051b..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/awskmsrsakeyring/awskmsrsakeyring.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the AWS KMS RSA Keyring -This example creates a KMS RSA Keyring and then encrypts a custom input -exampleText with an encryption context. -This example also includes some sanity checks for demonstration: - 1. Ciphertext and plaintext data are not the same - 2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -# For more information on how to use KMS keyrings, see -# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package awskmsrsakeyring - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" - kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types" -) - -func AwsKmsRsaExample(exampleText string, kmsRsaKeyID string, kmsRSAPublicKey []byte) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient( - mpltypes.MaterialProvidersConfig{}, - ) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - awsKmsRSAKeyringInput := mpltypes.CreateAwsKmsRsaKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: kmsRsaKeyID, - PublicKey: kmsRSAPublicKey, - EncryptionAlgorithm: kmstypes.EncryptionAlgorithmSpecRsaesOaepSha256, - } - awsKmsRSAKeyring, err := matProv.CreateAwsKmsRsaKeyring(context.Background(), awsKmsRSAKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - algorithmSuiteID := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - AlgorithmSuiteId: &algorithmSuiteID, - EncryptionContext: encryptionContext, - Keyring: awsKmsRSAKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: awsKmsRSAKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("AWS KMS RSA Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go deleted file mode 100644 index f580c895c..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhdiscoverykeyring.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the KMS ECDH Discovery Keyring. - -This example takes in the recipient's KMS ECC key ARN. -This example attempts to decrypt a ciphertext using the kmsEcdhKeyIdP256RecipientKeyId, -it does so by checking if the message header contains the recipient's public key. - -This example also requires access to a KMS ECC key. -Our tests provide a KMS ECC Key ARN that anyone can use, but you -can also provide your own KMS ECC key. -To use your own KMS ECC key, you must have: - - kms:GetPublicKey permissions on that key. -This example will call kms:GetPublicKey on keyring creation. -You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. - -This example creates a KMS ECDH Discovery Keyring and then decrypts a ciphertext. -For getting the ciphertext, we create a KMS ECDH keyring without discovery -because kms_ecdh_discovery_keyring cannot encrypt data. -This example also includes some sanity checks for demonstration: -1. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on this configuration see: -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery -*/ - -package ecdh - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsEcdhDiscoveryKeyringExample( - exampleText string, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - kmsEcdhKeyIdP256RecipientKeyId string, - kmsEcdhKeyIdP256SenderKeyId string, - kmsEccPublicKeyFileNameSender string, - kmsEccPublicKeyFileNameRecipient string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5: Create the KMS ECDH keyring. - // This keyring uses the KmsPublicKeyDiscovery configuration. - // On encrypt, the keyring will fail as it is not allowed to encrypt data under this configuration. - // On decrypt, the keyring will check if its corresponding public key is stored in the message header. It - // will call AWS KMS to derive the shared from the recipient's KMS ECC Key ARN and the sender's public key; - // For more information on this configuration see: - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery - // This keyring takes in: - // - kmsClient - // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie - kmsEcdhDiscoveryStaticConfigurationInput := mpltypes.KmsPublicKeyDiscoveryInput{ - RecipientKmsIdentifier: kmsEcdhKeyIdP256RecipientKeyId, - } - kmsEcdhDiscoveryStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPublicKeyDiscovery{ - Value: kmsEcdhDiscoveryStaticConfigurationInput, - } - awsKmsEcdhDiscoveryKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: kmsEcdhDiscoveryStaticConfiguration, - KmsClient: kmsClient, - } - awsKmsEcdhDiscoveryKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhDiscoveryKeyringInput) - if err != nil { - panic(err) - } - // Step 6: Get ciphertext by creating a KMS ECDH keyring WITHOUT discovery - // because the KMS ECDH keyring WITH discovery CANNOT encrypt data. - // We are generating a message intended for the kmsEcdhKeyIdP256RecipientKeyId recipient. - // Since a KMS ECDH keyring WITHOUT discovery cannot encrypt data, this example will ONLY decrypt - // messages where the configured key on the Discovery keyring is present on the message ciphertext. - // In this example we call `kms:GetPublicKey` to get the public key associated with the - // kmsEcdhKeyIdP256RecipientKeyId KMS key ID. - // If the message contains this public key, message decryption will be attempted. - cipherText := getCipherTextKmsEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, kmsClient, kmsEcdhKeyIdP256RecipientKeyId, kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsEccPublicKeyFileNameRecipient) - - // Step 7: Decrypt your encrypted data using the keyring with discovery behavior we created in step 5. - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: awsKmsEcdhDiscoveryKeyring, - EncryptionContext: encryptionContext, - Ciphertext: cipherText, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - if string(decryptOutput.Plaintext) == exampleText { - fmt.Println("AWS KMS ECDH Discovery Keyring Example Completed Successfully") - } else { - panic("FAILED!") - } -} - -// This function creates a AWS KMS ECDH keyring and encrypt the exampleText -func getCipherTextKmsEcdh( - matProv *mpl.Client, - encryptionClient *client.Client, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - exampleText string, - encryptionContext map[string]string, - kmsClient *kms.Client, - kmsEcdhKeyIdP256RecipientKeyId string, - kmsEcdhKeyIdP256SenderKeyId string, - kmsEccPublicKeyFileNameSender string, - kmsEccPublicKeyFileNameRecipient string) []byte { - // 1. Create the public key files for sender and recipient - // You may provide your own ECC keys. - // If not, this class will call the KMS ECC key, retrieve its public key, and store it - // in a PEM file for example use. - // Sender ECC key used in this example is retrieved with kmsEcdhKeyIdP256SenderKeyId - // Recipent ECC key used in this example is retrieved with kmsEcdhKeyIdP256RecipientKeyId - if !utils.FileExists(kmsEccPublicKeyFileNameSender) { - err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) - if err != nil { - panic(err) - } - } - if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { - err := utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) - if err != nil { - panic(err) - } - } - // 2. Load public key from UTF-8 encoded PEM files into a DER encoded public key. - publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) - if err != nil { - panic(err) - } - publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) - if err != nil { - panic(err) - } - // 3. Create the KmsPrivateKeyToStaticPublicKeyInput and kmsEcdhStaticConfiguration - kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ - RecipientPublicKey: publicKeyRecipient, - SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, - SenderPublicKey: publicKeySender, - } - kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ - Value: kmsEcdhStaticConfigurationInput, - } - // 4. Create the KMS ECDH keyring. - awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: kmsEcdhStaticConfiguration, - KmsClient: kmsClient, - } - awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) - if err != nil { - panic(err) - } - // 5. Encrypt the data with the encryption_context - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsEcdhKeyring, - }) - if err != nil { - panic(err) - } - // 6. Return the ciphertext - return res.Ciphertext -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go deleted file mode 100644 index d30cbe539..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/awskmsecdhkeyring.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the KMS ECDH Keyring. - -This example takes in the sender's KMS ECC key ARN, the sender's public key, -the recipient's public key, and the algorithm definition where the ECC keys lie. - -Both public keys MUST be UTF8 PEM-encoded X.509 public key, -also known as SubjectPublicKeyInfo (SPKI), - -This keyring, depending on its KeyAgreement scheme, -takes in the sender's KMS ECC Key ARN, and the recipient's ECC Public Key -to derive a shared secret. -The keyring uses the shared secret to derive a data key to protect the -data keys that encrypt and decrypt exampletext. - -This example also requires access to a KMS ECC key. -Our tests provide a KMS ECC Key ARN that you need permissions to, but you -can also provide your own KMS ECC key. -To use your own KMS ECC key, you must have either: -- Its public key downloaded in a UTF-8 encoded PEM file -- kms:GetPublicKey permissions on that key. -If you do not have the public key downloaded, running this example -through its main method will download the public key for you -by calling kms:GetPublicKey. -You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. -This example also requires a recipient ECC Public Key that lies on the same -curve as the sender public key. This examples uses another distinct -KMS ECC Public Key, it does not have to be a KMS key; it can be a -valid SubjectPublicKeyInfo (SPKI) Public Key. - -This example creates a KMS ECDH Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on this configuration see: -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create -*/ - -package ecdh - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func AwsKmsEcdhKeyringExample( - exampleText string, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - kmsEcdhKeyIdP256RecipientKeyId string, - kmsEcdhKeyIdP256SenderKeyId string, - kmsEccPublicKeyFileNameSender string, - kmsEccPublicKeyFileNameRecipient string) { - // Step 1: Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = "us-west-2" - }) - // Step 2: Load public key from UTF-8 encoded PEM files into a DER encoded public key. - // You may provide your own ECC keys. - // If not, this class will call the KMS ECC key, retrieve its public key, and store it - // in a PEM file for example use. - if !utils.FileExists(kmsEccPublicKeyFileNameSender) { - err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256SenderKeyId, kmsEccPublicKeyFileNameSender, kmsClient) - if err != nil { - panic(err) - } - } - if !utils.FileExists(kmsEccPublicKeyFileNameRecipient) { - err = utils.WriteKmsEcdhEccPublicKey(kmsEcdhKeyIdP256RecipientKeyId, kmsEccPublicKeyFileNameRecipient, kmsClient) - if err != nil { - panic(err) - } - } - publicKeySender, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameSender) - if err != nil { - panic(err) - } - publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(kmsEccPublicKeyFileNameRecipient) - if err != nil { - panic(err) - } - // Step 3: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6: Create the KMS ECDH keyring. - // This keyring uses the KmsPrivateKeyToStaticPublicKey configuration. This configuration calls for both of - // the keys to be on the same curve (P256, P384, P521). - // On encrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key. - // For this example, on decrypt, the keyring calls AWS KMS to derive the shared secret from the sender's KMS ECC Key ARN and the recipient's public key; - // however, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with - // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. - // For more information on this configuration see: - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create - // This keyring takes in: - // - kmsClient - // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie - // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public - // key for the key passed into kmsKeyId in DER format - // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public - // key for the key passed into kmsKeyId in DER format - kmsEcdhStaticConfigurationInput := mpltypes.KmsPrivateKeyToStaticPublicKeyInput{ - RecipientPublicKey: publicKeyRecipient, - SenderKmsIdentifier: kmsEcdhKeyIdP256SenderKeyId, - SenderPublicKey: publicKeySender, - } - kmsEcdhStaticConfiguration := &mpltypes.KmsEcdhStaticConfigurationsMemberKmsPrivateKeyToStaticPublicKey{ - Value: kmsEcdhStaticConfigurationInput, - } - awsKmsEcdhKeyringInput := mpltypes.CreateAwsKmsEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: kmsEcdhStaticConfiguration, - KmsClient: kmsClient, - } - awsKmsEcdhKeyring, err := matProv.CreateAwsKmsEcdhKeyring(context.Background(), awsKmsEcdhKeyringInput) - if err != nil { - panic(err) - } - // Step 7a: Encrypt the data - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: awsKmsEcdhKeyring, - }) - if err != nil { - panic(err) - } - // Step 7b: Decrypt the data - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: awsKmsEcdhKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - if string(decryptOutput.Plaintext) == exampleText { - fmt.Println("AWS KMS ECDH Keyring Example Completed Successfully") - } else { - panic("FAILED!") - } -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go deleted file mode 100644 index 781da18ac..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/ephemeralrawecdhkeyring.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the Ephemeral Raw ECDH Keyring. - -This example takes in the recipient's public key located at -eccPublicKeyFileNameRecipient as a -UTF8 PEM-encoded X.509 public key, -and the Curve Specification where the key lies. - -This example loads ECC keys from PEM files with paths defined in - - eccPublicKeyFileNameRecipient - -If you do not provide these files, running this example through this -class' main method will generate three files required for all raw ECDH examples -eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient -and eccPublicKeyFileNameRecipient for you. -In practice, users of this library should not generate new key pairs -like this, and should instead retrieve an existing key from a secure -key management system (e.g. an HSM). -You may also provide your own key pair by placing PEM files in the -directory where the example is run or modifying the paths in the code -below. These files must be valid PEM encodings of the key pair as UTF-8 -encoded bytes. If you do provide your own key pair, or if a key pair -already exists, this class' main method will not generate a new key pair. - -This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme. -This configuration will always create a new key pair as the sender key pair for the key agreement operation. -The ephemeral configuration can only encrypt data and CANNOT decrypt messages. - -This example creates an Ephemeral Raw ECDH Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on this configuration see: -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey -*/ - -package ecdh - -import ( - "context" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func EphemeralRawECDHKeyringExample( - exampleText string, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - eccPublicKeyFileNameRecipient string) { - // Step 1: Generate Raw ECDH ECC keys and load public key. - // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient - - // If you do not provide these files, running this example through this - // class' main method will generate three files required for all raw ECDH examples - // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient - // and eccPublicKeyFileNameRecipient for you. - if !utils.FileExists(eccPublicKeyFileNameRecipient) { - err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) - if err != nil { - panic(err) - } - } - publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) - if err != nil { - panic(err) - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5: Create the keyring. - // This keyring uses an ephemeral configuration. This configuration will always create a new - // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only - // encrypt data and CANNOT decrypt messages. - ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ - RecipientPublicKey: publicKeyRecipient, - } - ephemeralRawECDHStaticConfiguration := - mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ - Value: ephemeralRawEcdhStaticConfigurationInput, - } - rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, - } - ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) - if err != nil { - panic(err) - } - // Step 6: Encrypt - // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair - // used as the sender is ephemeral. This means that at decrypt time it does not have - // the private key that corresponds to the public key that is stored on the message. - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: ecdhKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - // (This is an example for demonstration; you do not need to do this in your own code.) - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - fmt.Println("Ephemeral Raw ECDH Keyring Example Completed Successfully") -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go deleted file mode 100644 index 607738ad2..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/publickeyrawdiscoveryecdhkeyring.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the Public Key Discovery Raw ECDH Keyring. - -A public key discovery Raw ECDH Keyring takes in the recipient's private key located -at eccPrivateKeyFileNameRecipient -as a UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures) private key, -and the Curve Specification where the key lies. - -If you provide the eccPrivateKeyFileNameRecipient, make sure to also -provide the recipient's public key located at eccPublicKeyFileNameRecipient -in the directory that you run this example. Even though the Public Key Discovery Raw ECDH keyring -uses the eccPrivateKeyFileNameRecipient to decrypt the data, -the eccPublicKeyFileNameRecipient is needed to generate the ciphertext to decrypt. - -This example loads ECC keys from PEM files and the ciphertext with paths defined in - - eccPrivateKeyFileNameRecipient - - eccPublicKeyFileNameRecipient - -If you do not provide these files, running this example through this -class' main method will generate three files required for all raw ECDH examples -eccPrivateKeyFilenameSender, eccPrivateKeyFileNameRecipient -and eccPublicKeyFileNameRecipient for you. -In practice, users of this library should not generate new key pairs -like this, and should instead retrieve an existing key from a secure -key management system (e.g. an HSM). -You may also provide your own key pair by placing PEM files in the -directory where the example is run or modifying the paths in the code -below. These files must be valid PEM encodings of the key pair as UTF-8 -encoded bytes. If you do provide your own key pair, or if a key pair -already exists, this class' main method will not generate a new key pair. - -This example creates a RawECDH keyring with the PublicKeyDiscovery key agreement scheme. -This scheme is only available on decrypt. - -This example creates a Public Key Discovery Raw ECDH Keyring and takes in a ciphertext to decrypt it. -This example also includes some sanity checks for demonstration: -1. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on this configuration see: -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-PublicKeyDiscovery -*/ - -package ecdh - -import ( - "context" - "fmt" - "os" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func PublicKeyRawEcdhDiscoveryKeyringExample( - exampleText string, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - eccPublicKeyFileNameRecipient string, - eccPrivateKeyFileNameRecipient string) { - // Step 1: Generate Raw ECDH ECC keys and load the recipient's private key. - // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient - - // If you do not provide these files, running this example through this - // class' main method will generate three files required for all raw ECDH examples - // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient - // and eccPublicKeyFileNameRecipient for you. - if !utils.FileExists(eccPublicKeyFileNameRecipient) { - err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) - if err != nil { - panic(err) - } - } - privateKeyRecipient, err := os.ReadFile(eccPrivateKeyFileNameRecipient) - if err != nil { - panic(err) - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5: Create the Public Key Discovery Raw ECDH keyring. - // Create the keyring. - // This keyring uses a discovery configuration. This configuration will check on decrypt - // if it is meant to decrypt the message by checking if the configured public key is stored on the message. - // The discovery configuration can only decrypt messages and CANNOT encrypt messages. - discoveryRawEcdhStaticConfigurationInput := mpltypes.PublicKeyDiscoveryInput{ - RecipientStaticPrivateKey: privateKeyRecipient, - } - discoveryRawEcdhStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberPublicKeyDiscovery{ - Value: discoveryRawEcdhStaticConfigurationInput, - } - discoveryRawEcdhKeyringInput := mpltypes.CreateRawEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: discoveryRawEcdhStaticConfiguration, - } - discoveryRawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), discoveryRawEcdhKeyringInput) - if err != nil { - panic(err) - } - // Step 6a: Get the ciphertext - // Although this example highlights Public Key Discovery Raw ECDH Keyring keyring, Discovery keyrings cannot - // be used to encrypt, so for encryption we create a Ephemeral Raw ECDH keyring without discovery mode. - cipherText := getCipherTextRawEcdh(matProv, encryptionClient, ecdhCurveSpec, exampleText, encryptionContext, eccPublicKeyFileNameRecipient) - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Keyring: discoveryRawEcdhKeyring, - EncryptionContext: encryptionContext, - Ciphertext: cipherText, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - if string(decryptOutput.Plaintext) == exampleText { - fmt.Println("Public Key Discovery Raw ECDH Keyring Example Completed Successfully") - } else { - panic("FAILED!") - } -} - -// This function creates a Ephemeral Raw ECDH keyring and encrypt the exampleText -func getCipherTextRawEcdh( - matProv *mpl.Client, - encryptionClient *client.Client, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - exampleText string, - encryptionContext map[string]string, - eccPublicKeyFileNameRecipient string) []byte { - // 1. Generate Raw ECDH ECC keys and load public key. - // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient - - // If you do not provide these files, running this example through this - // class' main method will generate three files required for all raw ECDH examples - // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient - // and eccPublicKeyFileNameRecipient for you. - // Load public key from UTF-8 encoded PEM files into a DER encoded public key. - if !utils.FileExists(eccPublicKeyFileNameRecipient) { - err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) - if err != nil { - panic(err) - } - } - publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) - if err != nil { - panic(err) - } - // Create the RawEcdhStaticConfigurations - ephemeralRawEcdhStaticConfigurationInput := mpltypes.EphemeralPrivateKeyToStaticPublicKeyInput{ - RecipientPublicKey: publicKeyRecipient, - } - ephemeralRawECDHStaticConfiguration := mpltypes.RawEcdhStaticConfigurationsMemberEphemeralPrivateKeyToStaticPublicKey{ - Value: ephemeralRawEcdhStaticConfigurationInput, - } - // Create the Ephemeral Raw ECDH keyring. - // This keyring uses an ephemeral configuration. This configuration will always create a new - // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only - // encrypt data and CANNOT decrypt messages. - rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: &ephemeralRawECDHStaticConfiguration, - } - ecdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) - if err != nil { - panic(err) - } - // Encrypt the data - // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair - // used as the sender is ephemeral. This means that at decrypt time it does not have - // the private key that corresponds to the public key that is stored on the message. - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: ecdhKeyring, - }) - if err != nil { - panic(err) - } - return res.Ciphertext -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go deleted file mode 100644 index 78d9aed99..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/ecdh/rawecdhkeyring.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the Raw ECDH Keyring. - -This example takes in the sender's private key located at -eccPrivateKeyFileNameSender as a UTF8 PEM-encoded -(PKCS #8 PrivateKeyInfo structures) private key, -and the recipient's public key located at -eccPublicKeyFileNameRecipient as a -UTF8 PEM-encoded X.509 public key, -also known as SubjectPublicKeyInfo (SPKI), -and the Curve Specification where the keys lie. - -This example loads ECC keys from PEM files with paths defined in - - eccPrivateKeyFileNameSender - - eccPublicKeyFileNameRecipient - -If you do not provide these files, running this example through this -class' main method will generate three files required for all raw ECDH examples -eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient -and eccPublicKeyFileNameRecipient for you. -These files will be generated in the directory where the example is run. -In practice, users of this library should not generate new key pairs -like this, and should instead retrieve an existing key from a secure -key management system (e.g. an HSM). -You may also provide your own key pair by placing PEM files in the -directory where the example is run or modifying the paths in the code -below. These files must be valid PEM encodings of the key pair as UTF-8 -encoded bytes. If you do provide your own key pair, or if a key pair -already exists, this class' main method will not generate a new key pair. - -This example creates a RawECDH keyring with the RawPrivateKeyToStaticPublicKey key agreement scheme. -On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. -On decrypt, the shared secret is derived from the sender's private key and the recipient's public key; -however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with -the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. - -This example creates a Raw ECDH Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on this configuration see: -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-RawPrivateKeyToStaticPublicKey -*/ - -package ecdh - -import ( - "context" - "fmt" - "os" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func RawECDHKeyringExample( - exampleText string, - ecdhCurveSpec primitivestypes.ECDHCurveSpec, - eccPublicKeyFileNameRecipient string, - eccPrivateKeyFileNameSender string) { - // Step 1: Generate Raw ECDH ECC keys and load public key. - // You may provide your own ECC keys in the files returned by eccPublicKeyFileNameRecipient - - // If you do not provide these files, running this example through this - // class' main method will generate three files required for all raw ECDH examples - // eccPrivateKeyFileNameSender, eccPrivateKeyFileNameRecipient - // and eccPublicKeyFileNameRecipient for you. - if !utils.FileExists(eccPublicKeyFileNameRecipient) || !utils.FileExists(eccPrivateKeyFileNameSender) { - err := utils.WriteRawEcdhEccKeys(ecdhCurveSpec) - if err != nil { - panic(err) - } - } - privateKeySender, err := os.ReadFile(eccPrivateKeyFileNameSender) - if err != nil { - panic(err) - } - publicKeyRecipient, err := utils.LoadPublicKeyFromPEM(eccPublicKeyFileNameRecipient) - if err != nil { - panic(err) - } - - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // For more information, see - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5: Create the Raw ECDH keyring. - // This keyring uses static sender and recipient keys. This configuration calls for both of - // the keys to be on the same curve (P256 / P384 / P521). - // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. - // For this example, on decrypt, the shared secret is derived from the sender's private key and the recipient's public key; - // However, on decrypt, the recipient can construct a keyring such that the shared secret is calculated with - // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. - RawEcdhStaticConfigurationInput := mpltypes.RawPrivateKeyToStaticPublicKeyInput{ - SenderStaticPrivateKey: privateKeySender, - RecipientPublicKey: publicKeyRecipient, - } - RawECDHStaticConfiguration := &mpltypes.RawEcdhStaticConfigurationsMemberRawPrivateKeyToStaticPublicKey{ - Value: RawEcdhStaticConfigurationInput, - } - rawEcdhKeyRingInput := mpltypes.CreateRawEcdhKeyringInput{ - CurveSpec: ecdhCurveSpec, - KeyAgreementScheme: RawECDHStaticConfiguration, - } - rawEcdhKeyring, err := matProv.CreateRawEcdhKeyring(context.Background(), rawEcdhKeyRingInput) - if err != nil { - panic(err) - } - // Step 6a: Encrypt - // A raw ecdh keyring with Ephemeral configuration cannot decrypt data since the key pair - // used as the sender is ephemeral. This means that at decrypt time it does not have - // the private key that corresponds to the public key that is stored on the message. - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: rawEcdhKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - // (This is an example for demonstration; you do not need to do this in your own code.) - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: rawEcdhKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - if string(decryptOutput.Plaintext) == exampleText { - fmt.Println("Raw ECDH Keyring Example Completed Successfully") - } else { - panic("FAILED!") - } -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go deleted file mode 100644 index 32d4cc5c7..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/multikeyring/multikeyring.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example sets up the Multi Keyring - -A multi-keyring is a keyring that consists of one or more individual keyrings of the -same or a different type. The effect is like using several keyrings in a series. -When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its -keyrings can decrypt that data. - -When you create a multi-keyring to encrypt data, you designate one of the keyrings as -the generator keyring. All other keyrings are known as child keyrings. The generator keyring -generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the -child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext -key and one encrypted data key for each wrapping key in the multi-keyring. If you create a -multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. -If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates -and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, -and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. - -When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted -data keys. The keyrings are called in the order that they are specified in the multi-keyring. -Processing stops as soon as any key in any keyring can decrypt an encrypted data key. - -This example creates a Multi Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decryption of ciphertext is possible using the multi_keyring, -and every one of the keyrings from the multi_keyring separately -3. All decrypted plaintext value match exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. - -This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring -as a child keyring. You can use different combinations of keyrings in the multi_keyring. - -For more information on how to use Multi keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html - -For more information on KMS Key identifiers, see -https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id -*/ - -package multikeyring - -import ( - "context" - "crypto/rand" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func MultiKeyringExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string) { - // Step 1: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 2: Create the MultiKeyring that consists of the KMS Keyring as generator and Raw AES Keyring as child keyring - // When using this MultiKeyring to encrypt data, either KMS Keyring or - // Raw AES Keyring (or a MultiKeyring containing either) may be used to decrypt the data - awsKmsKeyring := getKMSKeyring(defaultKMSKeyId, defaultKmsKeyRegion, matProv) - rawAESKeyring := getRawAESKeyring(matProv) - createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ - Generator: awsKmsKeyring, - ChildKeyrings: []mpltypes.IKeyring{rawAESKeyring}, - } - multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) - if err != nil { - panic(err) - } - // Step 3: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: multiKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 5b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: multiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that you can also successfully decrypt data using the `rawAESKeyring` directly. - // Because you used a MultiKeyring on Encrypt, you can use either the `kmsKeyring` or - // `rawAESKeyring` individually to decrypt the data. - decryptOutputRawAES, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: rawAESKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutputRawAES.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputRawAES.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that you can also successfully decrypt data using the `awsKmsKeyring` directly. - decryptOutputAwsKms, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: awsKmsKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutputAwsKms.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutputAwsKms.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Multi Keyring Example Completed Successfully") -} -func getKMSKeyring(kmsKeyId string, kmsRegion string, matProv *mpl.Client) mpltypes.IKeyring { - // 1. Create the aws kms client - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - panic(err) - } - kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = kmsRegion - }) - // 2. Create Aws Kms keyring - awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ - KmsClient: kmsClient, - KmsKeyId: kmsKeyId, - } - awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) - if err != nil { - panic(err) - } - return awsKmsKeyring -} -func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { - // 1. Generate a 256-bit AES key to use with your keyring. - // In practice, you should get this key from a secure key management system such as an HSM. - key, err := generateAes256KeyBytes() - if err != nil { - panic(err) - } - // The key namespace and key name are defined by you - // and are used by the raw AES keyring to determine - // whether it should attempt to decrypt an encrypted data key. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring - var keyNamespace = "A managed aes keys" - var keyName = "My 256-bit AES wrapping key" - // 2. Create the keyring - aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ - KeyName: keyName, - KeyNamespace: keyNamespace, - WrappingKey: key, - WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, - } - aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) - return aesKeyring -} -func generateAes256KeyBytes() ([]byte, error) { - const keySize = 32 // 256 bits = 32 bytes - key := make([]byte, keySize) - // Use crypto/rand for cryptographically secure random numbers - _, err := rand.Read(key) - if err != nil { - return nil, err - } - return key, nil -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go deleted file mode 100644 index d8ccaa21d..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawaeskeyring/rawaeskeyring.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the Raw AES Keyring -The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that -protects your data key. You need to generate, store, and protect the key material, -preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring -when you need to provide the wrapping key and encrypt the data keys locally or offline. -This example creates a Raw AES Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that -you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, -but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring. -For more information on how to use Raw AES keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html -*/ -package rawaeskeyring - -import ( - "context" - "crypto/rand" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func RawAesExample(exampleText string) { - // Step 1: Generate a 256-bit AES key to use with your keyring. - // In practice, you should get this key from a secure key management system such as an HSM. - key, err := generateAes256KeyBytes() - if err != nil { - panic(err) - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // The key namespace and key name are defined by you - // and are used by the raw AES keyring to determine - // whether it should attempt to decrypt an encrypted data key. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring - var keyNamespace = "A managed aes keys" - var keyName = "My 256-bit AES wrapping key" - aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ - KeyName: keyName, - KeyNamespace: keyNamespace, - WrappingKey: key, - WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, - } - aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: aesKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: aesKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Raw AES Keyring Example Completed Successfully") -} - -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 -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go b/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go deleted file mode 100644 index bf70d31c2..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/keyring/rawrsakeyring/rawrasakeyring.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -/* -This example sets up the Raw RSA Keyring -The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory -with RSA public and private keys that you provide. -This keyring accepts PEM encodings of the key pair as UTF-8 interpreted bytes. -The encryption function encrypts the data key under the RSA public key. The decryption function -decrypts the data key using the private key. -This example generate private and public key pairs. -In practice, users of this library should not generate new key pairs -like this, and should instead retrieve an existing key from a secure -key management system (e.g. an HSM). -You may also provide your own key pair by placing PEM files in the -directory where the example is run or modifying the paths in the code -below. These files must be valid PEM encodings of the key pair as UTF-8 -encoded bytes. If you do provide your own key pair, or if a key pair -already exists, this class' main method will not generate a new key pair. -This example creates a Raw RSA Keyring and then encrypts a custom input exampleText -with an encryption context. This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches exampleText -These sanity checks are for demonstration in the example only. You do not need these in your code. -A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private -key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, -and you can decrypt data with a Raw RSA keyring that has only a private key. This example requires -the user to either provide both private and public keys, or not provide any keys and the example -generates both to test encryption and decryption. If you configure a Raw RSA keyring with a -public and private key, be sure that they are part of the same key pair. Some language -implementations of the AWS Encryption SDK will not construct a Raw RSA keyring with keys -from different pairs. Others rely on you to verify that your keys are from the same key pair. -You can include any Raw RSA keyring in a multi-keyring. -For more information on how to use Raw RSA keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html -*/ - -package rawrsakeyring - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func RawRsaExample(exampleText string) { - // Step 1: Generate the key-pairs - publicKeyBlock, privateKeyBlock, err := generateKeyPair() - if err != nil { - panic(err) - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // The key namespace and key name are defined by you - // and are used by the raw RSA keyring to determine - // whether it should attempt to decrypt an encrypted data key. - // - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-rsa-keyring - keyNamespace := "Some managed raw keys" - keyName := "My 2048-bit RSA wrapping key" - rsaKeyRingInput := mpltypes.CreateRawRsaKeyringInput{ - KeyName: keyName, - KeyNamespace: keyNamespace, - PaddingScheme: mpltypes.PaddingSchemeOaepSha512Mgf1, - PublicKey: pem.EncodeToMemory(publicKeyBlock), - PrivateKey: pem.EncodeToMemory(privateKeyBlock), - } - rsaKeyring, err := matProv.CreateRawRsaKeyring(context.Background(), rsaKeyRingInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - cryptoClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - res, err := cryptoClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: rsaKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - // You do not need to specify the encryption context on decrypt - // because the header of the encrypted message includes the encryption context. - decryptOutput, err := cryptoClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - Keyring: rsaKeyring, - EncryptionContext: encryptionContext, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Raw RSA Keyring Example Completed Successfully") -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} - -func generateKeyPair() (*pem.Block, *pem.Block, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return nil, nil, err - } - // Extract public key from the private key - publicKey := &privateKey.PublicKey - // Encode public key to PKCS1 DER format - publicKeyDER, err := x509.MarshalPKIXPublicKey(publicKey) - if err != nil { - return nil, nil, err - } - privateKeyDer, err := x509.MarshalPKCS8PrivateKey(privateKey) - if err != nil { - return nil, nil, err - } - // Encode to PEM format - publicKeyBlock := &pem.Block{ - Type: "RSA PUBLIC KEY", - Bytes: publicKeyDER, - } - privateKeyBlock := &pem.Block{ - Type: "PRIVATE KEY", - Bytes: privateKeyDer, - } - return publicKeyBlock, privateKeyBlock, nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go b/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go deleted file mode 100644 index c1995f56e..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/limitencrypteddatakeysexample.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -Demonstrate limiting the number of Encrypted Data Keys [EDKs] allowed -when encrypting or decrypting a message. -Limiting encrypted data keys is most valuable when you are decrypting -messages from an untrusted source. -By default, the ESDK will allow up to 65,535 encrypted data keys. -A malicious actor might construct an encrypted message with thousands of -encrypted data keys, none of which can be decrypted. -As a result, the AWS Encryption SDK would attempt to decrypt each -encrypted data key until it exhausted the encrypted data keys in the message. - -For more information on limiting EDKs, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-limit-keys -*/ - -package examples - -import ( - "context" - "crypto/rand" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func LimitEncryptedDataKeyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion string, maxEncryptedDataKeys int64) { - // Step 1: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 2: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - // Also, set the EncryptionSDK's MaxEncryptedDataKeys parameter here - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ - MaxEncryptedDataKeys: &maxEncryptedDataKeys, - }) - if err != nil { - panic(err) - } - // Step 3: Generate `maxEncryptedDataKeys` AES keyrings to use with your keyring. - // In practice, you should get this key from a secure key management system such as an HSM. - rawAESKeyrings := make([]mpltypes.IKeyring, 0, maxEncryptedDataKeys) - var i int64 = 0 - for i < maxEncryptedDataKeys { - rawAESKeyrings = append(rawAESKeyrings, getRawAESKeyring(matProv)) - i++ - } - // Step 4: Create a Multi Keyring with `maxEncryptedDataKeys` AES Keyrings - createMultiKeyringInput := mpltypes.CreateMultiKeyringInput{ - Generator: rawAESKeyrings[0], - ChildKeyrings: rawAESKeyrings[1:], - } - multiKeyring, err := matProv.CreateMultiKeyring(context.Background(), createMultiKeyringInput) - if err != nil { - panic(err) - } - // Step 4: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 5a: Encrypt - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: multiKeyring, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 5b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: multiKeyring, - Ciphertext: res.Ciphertext, - }) - if err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - // Demonstrate that an EncryptionSDK with a lower MaxEncryptedDataKeys - // will fail to decrypt the encrypted message. - // (This is an example for demonstration; you do not need to do this in your own code.) - lowerMaxEncryptedDataKeys := maxEncryptedDataKeys - 1 - encryptionClientIncorrectMaxEncryptedKeys, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{ - MaxEncryptedDataKeys: &lowerMaxEncryptedDataKeys, - }) - if err != nil { - panic(err) - } - _, err = encryptionClientIncorrectMaxEncryptedKeys.Decrypt(context.Background(), esdktypes.DecryptInput{ - EncryptionContext: encryptionContext, - Keyring: multiKeyring, - Ciphertext: res.Ciphertext, - }) - if err == nil { - panic("Expected error not found.") - } - // Swallow the AwsCryptographicMaterialProvidersException but you may choose how to handle the exception - switch err.(type) { - case esdktypes.AwsEncryptionSdkException: - // You may choose how to handle the exception in this switch case. - default: - panic("Decryption using lower then max encrypted data keys MUST raise AwsEncryptionSdkException") - } - fmt.Println("Limit Encrypted Data Key Example completed successfully") -} - -func getRawAESKeyring(matProv *mpl.Client) mpltypes.IKeyring { - // 1. Generate a 256-bit AES key to use with your keyring. - // In practice, you should get this key from a secure key management system such as an HSM. - key, err := generate256KeyBytesAES() - if err != nil { - panic(err) - } - // The key namespace and key name are defined by you - // and are used by the raw AES keyring to determine - // whether it should attempt to decrypt an encrypted data key. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring - var keyNamespace = "A managed aes keys" - var keyName = "My 256-bit AES wrapping key" - // 2. Create the keyring - aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ - KeyName: keyName, - KeyNamespace: keyNamespace, - WrappingKey: key, - WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, - } - aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) - return aesKeyring -} - -func generate256KeyBytesAES() ([]byte, error) { - const keySize = 32 // 256 bits = 32 bytes - key := make([]byte, keySize) - // Use crypto/rand for cryptographically secure random numbers - _, err := rand.Read(key) - if err != nil { - return nil, err - } - return key, nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/main.go b/AwsEncryptionSDK/runtimes/go/examples/examples/main.go deleted file mode 100644 index 88162aa03..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/main.go +++ /dev/null @@ -1,157 +0,0 @@ -package main - -import ( - primitivestypes "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/clientsupplier" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/cryptographicmaterialsmanager/requiredencryptioncontext" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/cryptographicmaterialsmanager/restrictalgorithmsuite" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsdiscoverykeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsdiscoverymultikeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmshierarchicalkeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmskeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkdiscoverykeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkdiscoverymultikeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkkeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmrkmultikeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsmultikeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/awskmsrsakeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/ecdh" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/multikeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/rawaeskeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/keyring/rawrsakeyring" - "github.com/aws/aws-encryption-sdk/aws-esdk-go-preview/examples/utils" -) - -func main() { - const stringToEncrypt = "Text To encrypt" - clientsupplier.ClientSupplierExample( - stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultKMSKeyAccountID(), - []string{"eu-west-1"}) - examples.CommitmentPolicyExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) - examples.SetEncryptionAlgorithmSuiteExample(stringToEncrypt) - var maxEncryptedDataKeys int64 = 3 - examples.LimitEncryptedDataKeyExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion(), - maxEncryptedDataKeys) - requiredencryptioncontext.RequiredEncryptionContextExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) - restrictalgorithmsuite.SigningOnlyExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) - // keyrings - ecdh.PublicKeyRawEcdhDiscoveryKeyringExample( - stringToEncrypt, - primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient(), - utils.GetEccPrivateKeyFileNameRecipient()) - ecdh.EphemeralRawECDHKeyringExample( - stringToEncrypt, - primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient()) - ecdh.RawECDHKeyringExample( - stringToEncrypt, - primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient(), - utils.GetEccPrivateKeyFileNameSender()) - ecdh.AwsKmsEcdhKeyringExample( - stringToEncrypt, - primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetKmsEcdhKeyIdP256RecipientKeyId(), - utils.GetKmsEcdhKeyIdP256SenderKeyId(), - utils.GetKmsEccPublicKeyFileNameSender(), - utils.GetKmsEccPublicKeyFileNameRecipient()) - ecdh.AwsKmsEcdhDiscoveryKeyringExample( - stringToEncrypt, - primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetKmsEcdhKeyIdP256RecipientKeyId(), - utils.GetKmsEcdhKeyIdP256SenderKeyId(), - utils.GetKmsEccPublicKeyFileNameSender(), - utils.GetKmsEccPublicKeyFileNameRecipient()) - awskmskeyring.AwsKmsKeyringExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID()) - awskmsrsakeyring.AwsKmsRsaExample( - stringToEncrypt, - utils.GetTestKmsRsaKeyID(), - utils.GetKmsRSAPublicKey()) - awskmsmultikeyring.AwsKmsMultiKeyringExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetAlternateRegionKMSKeyId(), - utils.GetAlternateRegionKMSKeyRegion()) - awskmsdiscoverykeyring.AwsKmsDiscoveryKeyringExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID()) - awskmsdiscoverymultikeyring.AwsKmsDiscoveryMultiKeyringExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID(), - utils.GetRegions()) - rawrsakeyring.RawRsaExample(stringToEncrypt) - awskmsmrkkeyring.AwsKmsMrkKeyringExample( - stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetAlternateRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetAlternateRegionMrkKeyRegion()) - awskmsmrkmultikeyring.AwsKmsMrkMultiKeyringExample( - stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetAlternateRegionMrkKeyArn(), - utils.GetDefaultKMSKeyId(), - utils.GetAlternateRegionMrkKeyRegion()) - awskmsmrkdiscoverykeyring.AwsKmsMrkDiscoveryKeyringExample( - stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetAlternateRegionMrkKeyRegion(), - utils.GetDefaultKMSKeyAccountID()) - awskmsmrkdiscoverymultikeyring.AwsKmsMrkDiscoveryMultiKeyringExample( - stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetDefaultKMSKeyAccountID(), - utils.GetRegionsOfMRKKeys(), - ) - awskmshierarchicalkeyring.AwsKmsHKeyExample( - stringToEncrypt, - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), - ) - awskmshierarchicalkeyring.CreateAndVersionBranchKeyId( - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), - ) - awskmshierarchicalkeyring.SharedCacheExample( - stringToEncrypt, - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), - ) - rawaeskeyring.RawAesExample(stringToEncrypt) - multikeyring.MultiKeyringExample( - stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion(), - ) -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go b/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go deleted file mode 100644 index d7e0bc9cb..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/setencryptionalgorithmsuite.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/* -This example demonstrates how to set an algorithm suite while using the Raw AES Keyring -in the AWS Encryption SDK. - -The algorithm suite used in the encrypt() method is the algorithm used to protect your -data using the data key. By setting this algorithm, you can configure the algorithm used -to encrypt and decrypt your data. - -Algorithm suites can be set in a similar manner in other keyrings as well. However, -please make sure that you're using a logical algorithm suite that is compatible with your -keyring. For more information on algorithm suites supported by the AWS Encryption SDK, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html - -The AES wrapping algorithm (AesWrappingAlg::AlgAes256GcmIv12Tag16) protects your data key using -the user-provided wrapping key. In contrast, the algorithm suite used in the encrypt() method -is the algorithm used to protect your data using the data key. This example demonstrates setting the -latter, which is the algorithm suite for protecting your data. When the commitment policy is -RequireEncryptRequireDecrypt, the default algorithm used in the encrypt method is -AlgAes256GcmHkdfSha512CommitKeyEcdsaP384, which is a committing and signing algorithm. -Signature verification ensures the integrity of a digital message as it goes across trust -boundaries. However, signature verification adds a significant performance cost to encryption -and decryption. If encryptors and decryptors are equally trusted, we can consider using an algorithm -suite that does not include signing. This example sets the algorithm suite as -AlgAes256GcmHkdfSha512CommitKey, which is a committing but non-signing algorithm. -For more information on digital signatures, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs - -This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA -with an encryption context and the algorithm suite AlgAes256GcmHkdfSha512CommitKey. -This example also includes some sanity checks for demonstration: -1. Ciphertext and plaintext data are not the same -2. Decrypted plaintext value matches EXAMPLE_DATA -These sanity checks are for demonstration in the example only. You do not need these in your code. - -For more information on how to use Raw AES keyrings, see -https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html -*/ - -package examples - -import ( - "context" - "crypto/rand" - "fmt" - - mpl "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygenerated" - mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" - client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" - esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" -) - -func SetEncryptionAlgorithmSuiteExample(exampleText string) { - // Step 1: Generate a 256-bit AES key to use with your keyring. - // In practice, you should get this key from a secure key management system such as an HSM. - key, err := generateAes256KeyBytes() - if err != nil { - panic(err) - } - // Step 2: Initialize the mpl client - matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) - if err != nil { - panic(err) - } - // Step 3: Create the keyring - // The key namespace and key name are defined by you - // and are used by the raw AES keyring to determine - // whether it should attempt to decrypt an encrypted data key. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html#use-raw-aes-keyring - var keyNamespace = "A managed aes keys" - var keyName = "My 256-bit AES wrapping key" - // Note: The wrapping algorithm here is NOT the algorithm suite we set in this example. - aesKeyRingInput := mpltypes.CreateRawAesKeyringInput{ - KeyName: keyName, - KeyNamespace: keyNamespace, - WrappingKey: key, - WrappingAlg: mpltypes.AesWrappingAlgAlgAes256GcmIv12Tag16, - } - aesKeyring, err := matProv.CreateRawAesKeyring(context.Background(), aesKeyRingInput) - if err != nil { - panic(err) - } - // Step 4: Instantiate the encryption SDK client. - // This builds the default client with the RequireEncryptRequireDecrypt commitment policy, - // which enforces that this client only encrypts using committing algorithm suites and enforces - // that this client will only decrypt encrypted messages that were created with a committing - // algorithm suite. - encryptionClient, err := client.NewClient(esdktypes.AwsEncryptionSdkConfig{}) - if err != nil { - panic(err) - } - // Step 5: Create your encryption context (Optional). - // Remember that your encryption context is NOT SECRET. - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context - encryptionContext := map[string]string{ - "encryption": "context", - "is not": "secret", - "but adds": "useful metadata", - "that can help you": "be confident that", - "the data you are handling": "is what you think it is", - } - // Step 6a: Encrypt - // Here, we customize the Algorithm Suite that is used to Encrypt the plaintext. - // In particular, we use an Algorithm Suite without Signing. - // Signature verification adds a significant performance cost on decryption. - // If the users encrypting data and the users decrypting data are equally trusted, - // consider using an algorithm suite that does not include signing. - // See more about Digital Signatures: - // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs - algorithmSuiteId := mpltypes.ESDKAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKey - res, err := encryptionClient.Encrypt(context.Background(), esdktypes.EncryptInput{ - Plaintext: []byte(exampleText), - EncryptionContext: encryptionContext, - Keyring: aesKeyring, - AlgorithmSuiteId: &algorithmSuiteId, - }) - if err != nil { - panic(err) - } - // Validate Ciphertext and Plaintext before encryption are NOT the same - if string(res.Ciphertext) == exampleText { - panic("Ciphertext and Plaintext before encryption are the same") - } - // Step 6b: Decrypt - decryptOutput, err := encryptionClient.Decrypt(context.Background(), esdktypes.DecryptInput{ - Ciphertext: res.Ciphertext, - EncryptionContext: encryptionContext, - Keyring: aesKeyring, - }) - if err != nil { - panic(err) - } - // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. - // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. - // Before your application uses plaintext data, verify that the encryption context that - // you used to encrypt the message is included in the encryption context that was used to - // decrypt the message. The AWS Encryption SDK can add pairs, so don't require an exact match. - if err = validateEncryptionContext(encryptionContext, decryptOutput.EncryptionContext); err != nil { - panic(err) - } - // Validate Plaintext after decryption and Plaintext before encryption ARE the same - if string(decryptOutput.Plaintext) != exampleText { - panic("Plaintext after decryption and Plaintext before encryption are NOT the same") - } - fmt.Println("Set Encryption Algorithm Suite Example Completed Successfully") -} - -func generateAes256KeyBytes() ([]byte, error) { - numOfBytes := 32 // 256 bits = 32 bytes - key := make([]byte, numOfBytes) - // Use crypto/rand for cryptographically secure random numbers - _, err := rand.Read(key) - if err != nil { - return nil, err - } - return key, nil -} - -// This function only does subset matching because AWS Encryption SDK can add pairs, so don't require an exact match. -func validateEncryptionContext(expected, actual map[string]string) error { - for expectedKey, expectedValue := range expected { - actualValue, exists := actual[expectedKey] - if !exists || actualValue != expectedValue { - return fmt.Errorf("encryption context mismatch: expected key '%s' with value '%s'", - expectedKey, expectedValue) - } - } - return nil -} diff --git a/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go b/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go deleted file mode 100644 index 63639b5e6..000000000 --- a/AwsEncryptionSDK/runtimes/go/examples/examples/utils/exampleUtils.go +++ /dev/null @@ -1,321 +0,0 @@ -package utils - -import ( - "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "os" - - "github.com/aws/aws-cryptographic-material-providers-library/primitives/awscryptographyprimitivessmithygeneratedtypes" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -const ( - testKmsRsaPublicKey = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ -oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN -/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK -fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2 -r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9 -yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou -twIDAQAB ------END PUBLIC KEY-----` - testKmsRsaKeyID = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" - testDefaultKMSKeyId = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" - defaultKmsKeyRegion = "us-west-2" - testAlternateRegionKMSKeyId = "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" - testAlternateRegionKMSKeyRegion = "eu-central-1" - testDefaultMRKKeyId = "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" - defaultMRKKeyRegion = "us-east-1" - testAlternateRegionMrkKeyId = "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" - alternateRegionMrkKeyRegion = "eu-west-1" - testKeyStoreKMSKeyRegion = "us-west-2" - testKeyStoreKMSKeyID = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" - testLogicalKeyStoreName = "KeyStoreDdbTable" - testKeyStoreName = "KeyStoreDdbTable" - testKeyStoreRegion = "us-west-2" - defaultKMSKeyAccountID = "658956600833" - eccPrivateKeyFileNameSender = "sender_private.pem" - eccPrivateKeyFileNameRecipient = "recipient_private.pem" - eccPublicKeyFileNameRecipient = "recipient_public.pem" - kmsEccPublicKeyFileNameRecipient = "KmsEccKeyringExamplePublicKeyRecipient.pem" - kmsEccPublicKeyFileNameSender = "KmsEccKeyringExamplePublicKeySender.pem" - testKmsEcdhKeyIdP256SenderKeyId = "arn:aws:kms:us-west-2:370957321024:key/eabdf483-6be2-4d2d-8ee4-8c2583d416e9" - testKmsEcdhKeyIdP256RecipientKeyId = "arn:aws:kms:us-west-2:370957321024:key/0265c8e9-5b6a-4055-8f70-63719e09fda5" -) - -// Getter functions - -func GetKmsEcdhKeyIdP256SenderKeyId() string { - return testKmsEcdhKeyIdP256SenderKeyId -} - -func GetKmsEcdhKeyIdP256RecipientKeyId() string { - return testKmsEcdhKeyIdP256RecipientKeyId -} - -func GetKmsEccPublicKeyFileNameRecipient() string { - return kmsEccPublicKeyFileNameRecipient -} - -func GetKmsEccPublicKeyFileNameSender() string { - return kmsEccPublicKeyFileNameSender -} - -func GetEccPrivateKeyFileNameSender() string { - return eccPrivateKeyFileNameSender -} - -func GetEccPrivateKeyFileNameRecipient() string { - return eccPrivateKeyFileNameRecipient -} - -func GetEccPublicKeyFileNameRecipient() string { - return eccPublicKeyFileNameRecipient -} - -func GetRegionsOfMRKKeys() []string { - return []string{defaultMRKKeyRegion, alternateRegionMrkKeyRegion} -} - -func GetRegions() []string { - return []string{defaultKmsKeyRegion, testAlternateRegionKMSKeyRegion} -} - -func GetDefaultKmsKeyRegion() string { - return defaultKmsKeyRegion -} - -func GetDefaultMRKKeyRegion() string { - return defaultMRKKeyRegion -} - -func GetAlternateRegionMrkKeyRegion() string { - return alternateRegionMrkKeyRegion -} - -func GetAlternateRegionMrkKeyArn() string { - return testAlternateRegionMrkKeyId -} - -func GetDefaultRegionMrkKeyArn() string { - return testDefaultMRKKeyId -} - -func GetAlternateRegionKMSKeyRegion() string { - return testAlternateRegionKMSKeyRegion -} - -func GetAlternateRegionKMSKeyId() string { - return testAlternateRegionKMSKeyId -} - -func GetDefaultKMSKeyAccountID() string { - return defaultKMSKeyAccountID -} - -func GetDefaultKMSKeyId() string { - return testDefaultKMSKeyId -} - -func GetTestKmsRsaKeyID() string { - return testKmsRsaKeyID -} - -func GetKmsRSAPublicKey() []byte { - return []byte(testKmsRsaPublicKey) -} - -func GetKeyStoreRegion() string { - return testKeyStoreRegion -} - -func GetKeyStoreKMSKeyRegion() string { - return testKeyStoreKMSKeyRegion -} - -func GetKeyStoreKMSKeyID() string { - return testKeyStoreKMSKeyID -} - -func GetLogicalKeyStoreName() string { - return testLogicalKeyStoreName -} - -func GetKeyStoreName() string { - return testKeyStoreName -} - -// Utility functions - -func WriteRawEcdhEccKeys(ecdhCurveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) error { - // Safety check: Validate neither file is present - if FileExists(eccPrivateKeyFileNameSender) || - FileExists(eccPrivateKeyFileNameRecipient) || - FileExists(eccPublicKeyFileNameRecipient) { - return errors.New("WriteRawEcdhEccKeys will not overwrite existing PEM files") - } - - // Generate key pairs - _, privateKeySender, err := generateRawEccKeyPair(ecdhCurveSpec) - if err != nil { - return err - } - - publicKeyRecipient, privateKeyRecipient, err := generateRawEccKeyPair(ecdhCurveSpec) - if err != nil { - return err - } - - // Create PEM blocks - privateKeySenderPEM := &pem.Block{ - Type: "PRIVATE KEY", - Bytes: privateKeySender, - } - - privateKeyRecipientPEM := &pem.Block{ - Type: "PRIVATE KEY", - Bytes: privateKeyRecipient, - } - - publicKeyRecipientPEM := &pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKeyRecipient, - } - - // Write private key for sender in PEM format - err = os.WriteFile( - eccPrivateKeyFileNameSender, - pem.EncodeToMemory(privateKeySenderPEM), - 0600, - ) - if err != nil { - return fmt.Errorf("failed to write sender's private key: %w", err) - } - - // Write private key for recipient in PEM format - err = os.WriteFile( - eccPrivateKeyFileNameRecipient, - pem.EncodeToMemory(privateKeyRecipientPEM), - 0600, - ) - if err != nil { - return fmt.Errorf("failed to write recipient's private key: %w", err) - } - - // Write public key for recipient in PEM format - err = os.WriteFile( - eccPublicKeyFileNameRecipient, - pem.EncodeToMemory(publicKeyRecipientPEM), - 0600, - ) - if err != nil { - return fmt.Errorf("failed to write recipient's public key: %w", err) - } - - return nil -} - -func LoadPublicKeyFromPEM(filename string) ([]byte, error) { - // Read the PEM file content as string - pemContent, err := os.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("failed to read PEM file: %w", err) - } - // Parse PEM block - block, _ := pem.Decode(pemContent) - - if block == nil { - return nil, fmt.Errorf("failed to decode PEM block") - } - - // The block.Bytes contains the DER encoded key - return block.Bytes, nil -} - -func FileExists(filename string) bool { - _, err := os.Stat(filename) - return !os.IsNotExist(err) -} - -func generateRawEccKeyPair(curveSpec awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpec) ([]byte, []byte, error) { - // Select the appropriate elliptic curve based on the specification - var curve elliptic.Curve - switch curveSpec { - case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP256: - curve = elliptic.P256() - case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP384: - curve = elliptic.P384() - case awscryptographyprimitivessmithygeneratedtypes.ECDHCurveSpecEccNistP521: - curve = elliptic.P521() - default: - return nil, nil, fmt.Errorf("unsupported curve specification: %s", curveSpec) - } - // Generate the private key - privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) - if err != nil { - return nil, nil, fmt.Errorf("failed to generate private key: %w", err) - } - // Extract the public key - publicKey := &privateKey.PublicKey - // Marshal the private key to bytes (X.509 PKCS#8 format) - privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal private key: %w", err) - } - // Marshal the public key to bytes (X.509 SPKI format) - publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal public key: %w", err) - } - return publicKeyBytes, privateKeyBytes, nil -} - -func WriteKmsEcdhEccPublicKey(eccKeyArn, publicKeyFileName string, kmsClient *kms.Client) error { - // Safety check: Validate neither file is present - if FileExists(publicKeyFileName) { - return errors.New("WriteKmsEcdhEccPublicKey will not overwrite existing PEM files") - } - // Generate public key - publicKey, err := GenerateKmsEccPublicKey(eccKeyArn, kmsClient) - if err != nil { - return fmt.Errorf("failed to generate public key: %w", err) - } - // Create PEM block - pemBlock := &pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKey, - } - // Encode PEM - pemData := pem.EncodeToMemory(pemBlock) - if pemData == nil { - return errors.New("failed to encode PEM data") - } - // Write file with proper permissions - err = os.WriteFile(publicKeyFileName, pemData, 0600) - if err != nil { - return fmt.Errorf("failed to write public key file: %w", err) - } - return nil -} - -func GenerateKmsEccPublicKey(eccKeyArn string, kmsClient *kms.Client) ([]byte, error) { - ctx := context.Background() - // Get public key from KMS - response, err := kmsClient.GetPublicKey(ctx, &kms.GetPublicKeyInput{ - KeyId: &eccKeyArn, - }) - if err != nil { - return nil, fmt.Errorf("failed to get public key from KMS: %w", err) - } - // Check if public key is present - if response.PublicKey == nil { - return nil, errors.New("no public key in KMS response") - } - return response.PublicKey, nil -} From 16d53a2c9533c6190cf52d92b0b09ef0b1f629c5 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 13:00:37 -0800 Subject: [PATCH 05/11] auto commit --- .github/workflows/library_go_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/library_go_tests.yml b/.github/workflows/library_go_tests.yml index d52b6e467..1d84cd991 100644 --- a/.github/workflows/library_go_tests.yml +++ b/.github/workflows/library_go_tests.yml @@ -98,6 +98,7 @@ jobs: make test_go - name: Test Examples for Go + if: matrix.library == 'AwsEncryptionSDK' working-directory: ${{ matrix.library }}/runtimes/go/examples shell: bash run: | From a0d566cf9eea281e7637e4bb9676d60e74a11bcb Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 15:35:17 -0800 Subject: [PATCH 06/11] will it panic? --- .../runtimes/go/examples/misc/commitmentpolicy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go index f7cdb2636..2bdfec630 100644 --- a/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go +++ b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go @@ -119,8 +119,8 @@ func CommitmentPolicyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion s Keyring: awsKmsKeyring, Ciphertext: res.Ciphertext, }) - if err != nil { - panic(err) + if err == nil { + panic("err") } // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. From 1f120de4c4e944ecd9725535ded28474ebc90989 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 15:35:41 -0800 Subject: [PATCH 07/11] Revert "will it panic?" This reverts commit a0d566cf9eea281e7637e4bb9676d60e74a11bcb. --- .../runtimes/go/examples/misc/commitmentpolicy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go index 2bdfec630..f7cdb2636 100644 --- a/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go +++ b/AwsEncryptionSDK/runtimes/go/examples/misc/commitmentpolicy.go @@ -119,8 +119,8 @@ func CommitmentPolicyExample(exampleText, defaultKMSKeyId, defaultKmsKeyRegion s Keyring: awsKmsKeyring, Ciphertext: res.Ciphertext, }) - if err == nil { - panic("err") + if err != nil { + panic(err) } // If you do not specify the encryption context on Decrypt, it's recommended to check if the resulting encryption context matches. // The encryption context was specified on decrypt; we are validating the encryption context for demonstration only. From 6487e76e013f36cb9abf733786303daa970aa958 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 3 Jan 2025 15:41:57 -0800 Subject: [PATCH 08/11] Remove get in getters https://go.dev/doc/effective_go\#Getters --- .../requiredencryptioncontext.go | 5 +- AwsEncryptionSDK/runtimes/go/examples/main.go | 136 +++++++++--------- .../go/examples/utils/exampleUtils.go | 50 +++---- 3 files changed, 95 insertions(+), 96 deletions(-) diff --git a/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go index f0f42d548..0d85508ba 100644 --- a/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go +++ b/AwsEncryptionSDK/runtimes/go/examples/cryptographicmaterialsmanager/requiredencryptioncontext/requiredencryptioncontext.go @@ -18,7 +18,6 @@ import ( mpltypes "github.com/aws/aws-cryptographic-material-providers-library/mpl/awscryptographymaterialproviderssmithygeneratedtypes" client "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygenerated" esdktypes "github.com/aws/aws-encryption-sdk/awscryptographyencryptionsdksmithygeneratedtypes" - "github.com/aws/aws-encryption-sdk/examples/utils" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/kms" ) @@ -30,7 +29,7 @@ func RequiredEncryptionContextExample(exampleText, defaultKMSKeyId, defaultKmsKe panic(err) } kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { - o.Region = utils.GetDefaultKmsKeyRegion() + o.Region = defaultKmsKeyRegion }) // Step 2: Initialize the mpl client matProv, err := mpl.NewClient(mpltypes.MaterialProvidersConfig{}) @@ -40,7 +39,7 @@ func RequiredEncryptionContextExample(exampleText, defaultKMSKeyId, defaultKmsKe // Step 3: Create the keyring awsKmsKeyringInput := mpltypes.CreateAwsKmsKeyringInput{ KmsClient: kmsClient, - KmsKeyId: utils.GetDefaultKMSKeyId(), + KmsKeyId: defaultKMSKeyId, } awsKmsKeyring, err := matProv.CreateAwsKmsKeyring(context.Background(), awsKmsKeyringInput) if err != nil { diff --git a/AwsEncryptionSDK/runtimes/go/examples/main.go b/AwsEncryptionSDK/runtimes/go/examples/main.go index 3662ac551..434034783 100644 --- a/AwsEncryptionSDK/runtimes/go/examples/main.go +++ b/AwsEncryptionSDK/runtimes/go/examples/main.go @@ -30,132 +30,132 @@ func main() { const stringToEncrypt = "Text To encrypt" clientsupplier.ClientSupplierExample( stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultKMSKeyAccountID(), + utils.DefaultRegionMrkKeyArn(), + utils.DefaultKMSKeyAccountID(), []string{"eu-west-1"}) misc.CommitmentPolicyExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) + utils.DefaultKMSKeyId(), + utils.DefaultKmsKeyRegion()) misc.SetEncryptionAlgorithmSuiteExample(stringToEncrypt) var maxEncryptedDataKeys int64 = 3 misc.LimitEncryptedDataKeyExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion(), + utils.DefaultKMSKeyId(), + utils.DefaultKmsKeyRegion(), maxEncryptedDataKeys) requiredencryptioncontext.RequiredEncryptionContextExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) + utils.DefaultKMSKeyId(), + utils.DefaultKmsKeyRegion()) restrictalgorithmsuite.SigningOnlyExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion()) + utils.DefaultKMSKeyId(), + utils.DefaultKmsKeyRegion()) // keyrings ecdh.PublicKeyRawEcdhDiscoveryKeyringExample( stringToEncrypt, primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient(), - utils.GetEccPrivateKeyFileNameRecipient()) + utils.EccPublicKeyFileNameRecipient(), + utils.EccPrivateKeyFileNameRecipient()) ecdh.EphemeralRawECDHKeyringExample( stringToEncrypt, primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient()) + utils.EccPublicKeyFileNameRecipient()) ecdh.RawECDHKeyringExample( stringToEncrypt, primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetEccPublicKeyFileNameRecipient(), - utils.GetEccPrivateKeyFileNameSender()) + utils.EccPublicKeyFileNameRecipient(), + utils.EccPrivateKeyFileNameSender()) ecdh.AwsKmsEcdhKeyringExample( stringToEncrypt, primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetKmsEcdhKeyIdP256RecipientKeyId(), - utils.GetKmsEcdhKeyIdP256SenderKeyId(), - utils.GetKmsEccPublicKeyFileNameSender(), - utils.GetKmsEccPublicKeyFileNameRecipient()) + utils.KmsEcdhKeyIdP256RecipientKeyId(), + utils.KmsEcdhKeyIdP256SenderKeyId(), + utils.KmsEccPublicKeyFileNameSender(), + utils.KmsEccPublicKeyFileNameRecipient()) ecdh.AwsKmsEcdhDiscoveryKeyringExample( stringToEncrypt, primitivestypes.ECDHCurveSpecEccNistP256, - utils.GetKmsEcdhKeyIdP256RecipientKeyId(), - utils.GetKmsEcdhKeyIdP256SenderKeyId(), - utils.GetKmsEccPublicKeyFileNameSender(), - utils.GetKmsEccPublicKeyFileNameRecipient()) + utils.KmsEcdhKeyIdP256RecipientKeyId(), + utils.KmsEcdhKeyIdP256SenderKeyId(), + utils.KmsEccPublicKeyFileNameSender(), + utils.KmsEccPublicKeyFileNameRecipient()) awskmskeyring.AwsKmsKeyringExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID()) + utils.DefaultKMSKeyId(), + utils.DefaultKMSKeyAccountID()) awskmsrsakeyring.AwsKmsRsaExample( stringToEncrypt, - utils.GetTestKmsRsaKeyID(), - utils.GetKmsRSAPublicKey()) + utils.TestKmsRsaKeyID(), + utils.KmsRSAPublicKey()) awskmsmultikeyring.AwsKmsMultiKeyringExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetAlternateRegionKMSKeyId(), - utils.GetAlternateRegionKMSKeyRegion()) + utils.DefaultKMSKeyId(), + utils.AlternateRegionKMSKeyId(), + utils.AlternateRegionKMSKeyRegion()) awskmsdiscoverykeyring.AwsKmsDiscoveryKeyringExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID()) + utils.DefaultKMSKeyId(), + utils.DefaultKMSKeyAccountID()) awskmsdiscoverymultikeyring.AwsKmsDiscoveryMultiKeyringExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKMSKeyAccountID(), - utils.GetRegions()) + utils.DefaultKMSKeyId(), + utils.DefaultKMSKeyAccountID(), + utils.Regions()) rawrsakeyring.RawRsaExample(stringToEncrypt) awskmsmrkkeyring.AwsKmsMrkKeyringExample( stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetAlternateRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetAlternateRegionMrkKeyRegion()) + utils.DefaultRegionMrkKeyArn(), + utils.AlternateRegionMrkKeyArn(), + utils.DefaultMRKKeyRegion(), + utils.AlternateRegionMrkKeyRegion()) awskmsmrkmultikeyring.AwsKmsMrkMultiKeyringExample( stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetAlternateRegionMrkKeyArn(), - utils.GetDefaultKMSKeyId(), - utils.GetAlternateRegionMrkKeyRegion()) + utils.DefaultRegionMrkKeyArn(), + utils.AlternateRegionMrkKeyArn(), + utils.DefaultKMSKeyId(), + utils.AlternateRegionMrkKeyRegion()) awskmsmrkdiscoverykeyring.AwsKmsMrkDiscoveryKeyringExample( stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetAlternateRegionMrkKeyRegion(), - utils.GetDefaultKMSKeyAccountID()) + utils.DefaultRegionMrkKeyArn(), + utils.DefaultMRKKeyRegion(), + utils.AlternateRegionMrkKeyRegion(), + utils.DefaultKMSKeyAccountID()) awskmsmrkdiscoverymultikeyring.AwsKmsMrkDiscoveryMultiKeyringExample( stringToEncrypt, - utils.GetDefaultRegionMrkKeyArn(), - utils.GetDefaultMRKKeyRegion(), - utils.GetDefaultKMSKeyAccountID(), - utils.GetRegionsOfMRKKeys(), + utils.DefaultRegionMrkKeyArn(), + utils.DefaultMRKKeyRegion(), + utils.DefaultKMSKeyAccountID(), + utils.RegionsOfMRKKeys(), ) awskmshierarchicalkeyring.AwsKmsHKeyExample( stringToEncrypt, - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), + utils.KeyStoreKMSKeyRegion(), + utils.KeyStoreRegion(), + utils.KeyStoreKMSKeyID(), + utils.KeyStoreName(), + utils.LogicalKeyStoreName(), ) awskmshierarchicalkeyring.CreateAndVersionBranchKeyId( - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), + utils.KeyStoreKMSKeyRegion(), + utils.KeyStoreRegion(), + utils.KeyStoreKMSKeyID(), + utils.KeyStoreName(), + utils.LogicalKeyStoreName(), ) awskmshierarchicalkeyring.SharedCacheExample( stringToEncrypt, - utils.GetKeyStoreKMSKeyRegion(), - utils.GetKeyStoreRegion(), - utils.GetKeyStoreKMSKeyID(), - utils.GetKeyStoreName(), - utils.GetLogicalKeyStoreName(), + utils.KeyStoreKMSKeyRegion(), + utils.KeyStoreRegion(), + utils.KeyStoreKMSKeyID(), + utils.KeyStoreName(), + utils.LogicalKeyStoreName(), ) rawaeskeyring.RawAesExample(stringToEncrypt) multikeyring.MultiKeyringExample( stringToEncrypt, - utils.GetDefaultKMSKeyId(), - utils.GetDefaultKmsKeyRegion(), + utils.DefaultKMSKeyId(), + utils.DefaultKmsKeyRegion(), ) } diff --git a/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go b/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go index 63639b5e6..ddc75bf1b 100644 --- a/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go +++ b/AwsEncryptionSDK/runtimes/go/examples/utils/exampleUtils.go @@ -51,103 +51,103 @@ twIDAQAB // Getter functions -func GetKmsEcdhKeyIdP256SenderKeyId() string { +func KmsEcdhKeyIdP256SenderKeyId() string { return testKmsEcdhKeyIdP256SenderKeyId } -func GetKmsEcdhKeyIdP256RecipientKeyId() string { +func KmsEcdhKeyIdP256RecipientKeyId() string { return testKmsEcdhKeyIdP256RecipientKeyId } -func GetKmsEccPublicKeyFileNameRecipient() string { +func KmsEccPublicKeyFileNameRecipient() string { return kmsEccPublicKeyFileNameRecipient } -func GetKmsEccPublicKeyFileNameSender() string { +func KmsEccPublicKeyFileNameSender() string { return kmsEccPublicKeyFileNameSender } -func GetEccPrivateKeyFileNameSender() string { +func EccPrivateKeyFileNameSender() string { return eccPrivateKeyFileNameSender } -func GetEccPrivateKeyFileNameRecipient() string { +func EccPrivateKeyFileNameRecipient() string { return eccPrivateKeyFileNameRecipient } -func GetEccPublicKeyFileNameRecipient() string { +func EccPublicKeyFileNameRecipient() string { return eccPublicKeyFileNameRecipient } -func GetRegionsOfMRKKeys() []string { +func RegionsOfMRKKeys() []string { return []string{defaultMRKKeyRegion, alternateRegionMrkKeyRegion} } -func GetRegions() []string { +func Regions() []string { return []string{defaultKmsKeyRegion, testAlternateRegionKMSKeyRegion} } -func GetDefaultKmsKeyRegion() string { +func DefaultKmsKeyRegion() string { return defaultKmsKeyRegion } -func GetDefaultMRKKeyRegion() string { +func DefaultMRKKeyRegion() string { return defaultMRKKeyRegion } -func GetAlternateRegionMrkKeyRegion() string { +func AlternateRegionMrkKeyRegion() string { return alternateRegionMrkKeyRegion } -func GetAlternateRegionMrkKeyArn() string { +func AlternateRegionMrkKeyArn() string { return testAlternateRegionMrkKeyId } -func GetDefaultRegionMrkKeyArn() string { +func DefaultRegionMrkKeyArn() string { return testDefaultMRKKeyId } -func GetAlternateRegionKMSKeyRegion() string { +func AlternateRegionKMSKeyRegion() string { return testAlternateRegionKMSKeyRegion } -func GetAlternateRegionKMSKeyId() string { +func AlternateRegionKMSKeyId() string { return testAlternateRegionKMSKeyId } -func GetDefaultKMSKeyAccountID() string { +func DefaultKMSKeyAccountID() string { return defaultKMSKeyAccountID } -func GetDefaultKMSKeyId() string { +func DefaultKMSKeyId() string { return testDefaultKMSKeyId } -func GetTestKmsRsaKeyID() string { +func TestKmsRsaKeyID() string { return testKmsRsaKeyID } -func GetKmsRSAPublicKey() []byte { +func KmsRSAPublicKey() []byte { return []byte(testKmsRsaPublicKey) } -func GetKeyStoreRegion() string { +func KeyStoreRegion() string { return testKeyStoreRegion } -func GetKeyStoreKMSKeyRegion() string { +func KeyStoreKMSKeyRegion() string { return testKeyStoreKMSKeyRegion } -func GetKeyStoreKMSKeyID() string { +func KeyStoreKMSKeyID() string { return testKeyStoreKMSKeyID } -func GetLogicalKeyStoreName() string { +func LogicalKeyStoreName() string { return testLogicalKeyStoreName } -func GetKeyStoreName() string { +func KeyStoreName() string { return testKeyStoreName } From 37c3f6b479a9dcc318cafdcf5991996a5cefafc5 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 6 Jan 2025 14:58:28 -0800 Subject: [PATCH 09/11] Add examples --- .../runtimes/go/examples/readme.md | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 AwsEncryptionSDK/runtimes/go/examples/readme.md diff --git a/AwsEncryptionSDK/runtimes/go/examples/readme.md b/AwsEncryptionSDK/runtimes/go/examples/readme.md new file mode 100644 index 000000000..df7de5e1b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/go/examples/readme.md @@ -0,0 +1,90 @@ +# AWS Encryption SDK for Go Examples + +This section features examples that show you +how to use the AWS Encryption SDK. +We demonstrate how to use the encryption and decryption APIs +and how to set up some common configuration patterns. + +## APIs + +The AWS Encryption SDK provides two high-level APIs: +one-step APIs that process the entire operation in memory +and streaming APIs. + +You can find examples that demonstrate these APIs +in the [`examples/`](./) directory. + +* [How to encrypt and decrypt](./keyring/awskmskeyring/awskmskeyring.go) +* [How to change the algorithm suite](./misc/setencryptionalgorithmsuite.go) +* [How to set the commitment policy](./misc/commitmentpolicy.go) +* [How to limit the number of encrypted data keys (EDKs)](./misc/limitencrypteddatakeysexample.go) + +## Configuration + +To use the encryption and decryption APIs, +you need to describe how you want the library to protect your data keys. +You can do this by configuring +[keyrings](#keyrings) or [cryptographic materials managers](#cryptographic-materials-managers). +These examples will show you how to use the configuration tools that we include for you +and how to create some of your own. +We start with AWS KMS examples, then show how to use other wrapping keys. + +* Using AWS Key Management Service (AWS KMS) + * [How to use one AWS KMS key](./keyring/awskmskeyring/awskmskeyring.go) + * [How to use multiple AWS KMS keys in different regions](./keyring/awskmsmrkmultikeyring/awskmsmrkmultikeyring.go) + * [How to decrypt when you don't know the AWS KMS key](./keyring/awskmsdiscoverykeyring/awskmsdiscoverykeyring.go) + * [How to limit decryption to a single region](./keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go) + * [How to decrypt with a preferred region but failover to others](./keyring/awskmsmrkdiscoverykeyring/awskmsmrkdiscoverykeyring.go) + * [How to reproduce the behavior of an AWS KMS master key provider](./keyring/awskmsmultikeyring/awskmsmultikeyring.go) +* Using raw wrapping keys + * [How to use a raw AES wrapping key](./keyring/rawaeskeyring/rawaeskeyring.go) + * [How to use a raw RSA wrapping key](./keyring/rawrsakeyring/rawrasakeyring.go) +* Combining wrapping keys + * [How to combine AWS KMS with an offline escrow key](./keyring/multikeyring/multikeyring.go) +* How to restrict algorithm suites + * [with a custom cryptographic materials manager](./cryptographicmaterialsmanager/restrictalgorithmsuite/signingsuiteonlycmm.go) + +### Keyrings + +Keyrings are the most common way for you to configure the AWS Encryption SDK. +They determine how the AWS Encryption SDK protects your data. +You can find these examples in [`examples/keyring`](./keyring). + +### Cryptographic Materials Managers + +Keyrings define how your data keys are protected, +but there is more going on here than just protecting data keys. + +Cryptographic materials managers give you higher-level controls +over how the AWS Encryption SDK protects your data. +This can include things like +enforcing the use of certain algorithm suites or encryption context settings, +reusing data keys across messages, +or changing how you interact with keyrings. +You can find these examples in +[`examples/cryptographic_materials_manager`](./cryptographicmaterialsmanager). + +### Client Supplier + +The AWS Encryption SDK creates AWS KMS clients when interacting with AWS KMS. +In case the default AWS KMS client configuration doesn't suit your needs, +you can configure clients by defining a custom Client Supplier. +For example, your Client Supplier could tune +the retry and timeout settings on the client, or use different credentials +based on which region is being called. In our +[regional_role_client_supplier](./clientsupplier/regionalroleclientsupplier.go) +example, we show how you can build a custom Client Supplier which +creates clients by assuming different IAM roles for different regions. + +# Writing Examples + +If you want to contribute a new example, that's awesome! +To make sure that your example runs in our CI, +please make sure that it meets the following requirements: + +1. The example MUST be a distinct subdirectory or file in the [`examples/`](./) directory. +1. The example MAY be nested arbitrarily deeply. +1. Each example file MUST contain exactly one example. +1. Each example filename MUST be descriptive. +1. Each example file MUST contain validation checks to check for expected returned values and MUST panic is the returned value is no expected. +1. Each example MUST also be called inside the `main` function of [main.go](./main.go). \ No newline at end of file From a6b0a3485e45289dfbcd88ce37cb4d2543c4f76f Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Wed, 8 Jan 2025 14:49:34 -0800 Subject: [PATCH 10/11] rename readme.md to README.md --- AwsEncryptionSDK/runtimes/go/examples/{readme.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AwsEncryptionSDK/runtimes/go/examples/{readme.md => README.md} (100%) diff --git a/AwsEncryptionSDK/runtimes/go/examples/readme.md b/AwsEncryptionSDK/runtimes/go/examples/README.md similarity index 100% rename from AwsEncryptionSDK/runtimes/go/examples/readme.md rename to AwsEncryptionSDK/runtimes/go/examples/README.md From c432f3b61c2ce946a6d9c3fc93557c446a8de435 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Wed, 8 Jan 2025 14:54:11 -0800 Subject: [PATCH 11/11] remove redundant code --- AwsEncryptionSDK/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/AwsEncryptionSDK/Makefile b/AwsEncryptionSDK/Makefile index aba19acc4..ce35387ba 100644 --- a/AwsEncryptionSDK/Makefile +++ b/AwsEncryptionSDK/Makefile @@ -107,6 +107,3 @@ restore_directories: echo "Directory $$dir not found"; \ fi \ done - -_polymorph_dependencies: - @echo "No polymorphing of dependency" \ No newline at end of file