CBC padding oracle in AWS S3 Crypto SDK for GoLang
Description
A padding oracle vulnerability exists in the AWS S3 Crypto SDK for GoLang versions prior to V2. The SDK allows users to encrypt files with AES-CBC without computing a Message Authentication Code (MAC), which then allows an attacker who has write access to the target's S3 bucket and can observe whether or not an endpoint with access to the key can decrypt a file, they can reconstruct the plaintext with (on average) 128*length (plaintext) queries to the endpoint, by exploiting CBC's ability to manipulate the bytes of the next block and PKCS5 padding errors. It is recommended to update your SDK to V2 or later, and re-encrypt your files.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A padding oracle vulnerability in the AWS S3 Crypto SDK for GoLang (before V2) allows attackers with S3 write access and decryption oracle to recover plaintext via AES-CBC without a MAC.
Vulnerability
Overview
CVE-2020-8911 describes a padding oracle vulnerability in the AWS S3 Crypto SDK for GoLang prior to version 2. The root cause is that the SDK allowed users to encrypt files using AES-CBC mode without computing a Message Authentication Code (MAC) to verify ciphertext integrity [1][2]. This omission enables an attacker to exploit the malleability of CBC-encrypted data combined with PKCS5 padding error messages from a decryption oracle.
Attack
Prerequisites and Exploitation
The attack requires two conditions: (1) write access to the target's S3 bucket, and (2) the ability to observe whether an endpoint with access to the encryption key can successfully decrypt a manipulated file [1][2]. The attacker can then manipulate bytes in one ciphertext block to influence the decrypted plaintext of the next block, and use padding error messages to incrementally recover the plaintext. With an average of 128 * length(plaintext) queries to the decryption oracle, an attacker can reconstruct the entire plaintext without knowing the encryption key [1].
Impact
A successful exploit allows an attacker who can write ciphertexts to an S3 bucket and observe decryption outcomes to recover the original plaintext content of affected files. This breaks the confidentiality guarantees expected from client-side encryption, potentially exposing sensitive data stored in S3.
Mitigation
The issue was fixed by AWS in the SDK version 1.34.0 (August 7, 2020), and the recommended action is to update to AWS SDK for Go V2 or later, and re-encrypt all files that were previously encrypted with the vulnerable version [1][3][4]. AWS also updated the S3 Encryption Client to use authenticated encryption (AES-GCM by default) to prevent similar attacks [2].
- GitHub - aws/aws-sdk-go: This SDK has reached end-of-support. The AWS SDK for Go v2 is available here: https://github.com/aws/aws-sdk-go-v2
- Updates to the Amazon S3 Encryption Client | Amazon Web Services
- Release v1.34.0 (2020-08-07) · aws/aws-sdk-go@ae9b9fd
- Merge commit '12ff57a16373dda5a0c22eafdf0fa1c4c224f7c4' into release · aws/aws-sdk-go@1e84382
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/aws/aws-sdk-goGo | < 1.34.0 | 1.34.0 |
Affected products
13- osv-coords12 versionspkg:apk/chainguard/grafana-10.1pkg:apk/chainguard/k3dpkg:apk/chainguard/k3d-proxypkg:apk/chainguard/k3d-toolspkg:apk/chainguard/kubevirt-cdi-uploadserver-1.5pkg:apk/chainguard/kubevirt-cdi-uploadserver-1.59pkg:apk/chainguard/kubevirt-cdi-uploadserver-fips-1.5pkg:apk/chainguard/kubevirt-cdi-uploadserver-fips-1.59pkg:apk/wolfi/k3dpkg:apk/wolfi/k3d-proxypkg:apk/wolfi/k3d-toolspkg:golang/github.com/aws/aws-sdk-go
< 0+ 11 more
- (no CPE)range: < 0
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 1.34.0
- Google LLC/AWS S3 Crypto SDK for GoLangv5Range: stable
Patches
2ae9b9fd92af1Release v1.34.0 (2020-08-07)
22 files changed · +3850 −1036
aws/endpoints/defaults.go+14 −0 modified@@ -2084,6 +2084,7 @@ var awsPartition = partition{ Protocols: []string{"http", "https"}, }, Endpoints: endpoints{ + "af-south-1": endpoint{}, "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, @@ -2093,6 +2094,7 @@ var awsPartition = partition{ "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-north-1": endpoint{}, + "eu-south-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, @@ -3959,6 +3961,12 @@ var awsPartition = partition{ "neptune": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{ + Hostname: "rds.ap-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-east-1", + }, + }, "ap-northeast-1": endpoint{ Hostname: "rds.ap-northeast-1.amazonaws.com", CredentialScope: credentialScope{ @@ -4031,6 +4039,12 @@ var awsPartition = partition{ Region: "me-south-1", }, }, + "sa-east-1": endpoint{ + Hostname: "rds.sa-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "sa-east-1", + }, + }, "us-east-1": endpoint{ Hostname: "rds.us-east-1.amazonaws.com", CredentialScope: credentialScope{
aws/version.go+1 −1 modified@@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.33.21" +const SDKVersion = "1.34.0"
CHANGELOG.md+16 −0 modified@@ -1,3 +1,19 @@ +Release v1.34.0 (2020-08-07) +=== + +### Service Client Updates +* `service/glue`: Updates service API and documentation + * AWS Glue now adds support for Network connection type enabling you to access resources inside your VPC using Glue crawlers and Glue ETL jobs. +* `service/organizations`: Updates service API and documentation + * Documentation updates for some new error reasons. +* `service/s3`: Updates service documentation and examples + * Updates Amazon S3 API reference documentation. +* `service/sms`: Updates service API and documentation + * In this release, AWS Server Migration Service (SMS) has added new features: 1. APIs to work with application and instance level validation 2. Import application catalog from AWS Application Discovery Service 3. For an application you can start on-demand replication + +### SDK Features +* `service/s3/s3crypto`: Updates to the Amazon S3 Encryption Client - This change includes fixes for issues that were reported by Sophie Schmieg from the Google ISE team, and for issues that were discovered by AWS Cryptography. + Release v1.33.21 (2020-08-06) ===
CHANGELOG_PENDING.md+0 −1 modified@@ -1,5 +1,4 @@ ### SDK Features -* `service/s3/s3crypto`: Updates to the Amazon S3 Encryption Client - This change includes fixes for issues that were reported by Sophie Schmieg from the Google ISE team, and for issues that were discovered by AWS Cryptography. ### SDK Enhancements
models/apis/glue/2017-03-31/api-2.json+4 −2 modified@@ -2772,7 +2772,8 @@ "JDBC", "SFTP", "MONGODB", - "KAFKA" + "KAFKA", + "NETWORK" ] }, "ConnectionsList":{ @@ -5679,7 +5680,8 @@ "type":"structure", "members":{ "Path":{"shape":"Path"}, - "Exclusions":{"shape":"PathList"} + "Exclusions":{"shape":"PathList"}, + "ConnectionName":{"shape":"ConnectionName"} } }, "S3TargetList":{
models/apis/glue/2017-03-31/docs-2.json+3 −2 modified@@ -766,7 +766,8 @@ "ConnectionName": { "base": null, "refs": { - "JdbcTarget$ConnectionName": "<p>The name of the connection to use to connect to the JDBC target.</p>" + "JdbcTarget$ConnectionName": "<p>The name of the connection to use to connect to the JDBC target.</p>", + "S3Target$ConnectionName": "<p>The name of a connection which allows a job or crawler to access data in Amazon S3 within an Amazon Virtual Private Cloud environment (Amazon VPC).</p>" } }, "ConnectionPasswordEncryption": { @@ -792,7 +793,7 @@ "base": null, "refs": { "Connection$ConnectionType": "<p>The type of the connection. Currently, SFTP is not supported.</p>", - "ConnectionInput$ConnectionType": "<p>The type of the connection. Currently, these types are supported:</p> <ul> <li> <p> <code>JDBC</code> - Designates a connection to a database through Java Database Connectivity (JDBC).</p> </li> <li> <p> <code>KAFKA</code> - Designates a connection to an Apache Kafka streaming platform.</p> </li> <li> <p> <code>MONGODB</code> - Designates a connection to a MongoDB document database.</p> </li> </ul> <p>SFTP is not supported.</p>", + "ConnectionInput$ConnectionType": "<p>The type of the connection. Currently, these types are supported:</p> <ul> <li> <p> <code>JDBC</code> - Designates a connection to a database through Java Database Connectivity (JDBC).</p> </li> <li> <p> <code>KAFKA</code> - Designates a connection to an Apache Kafka streaming platform.</p> </li> <li> <p> <code>MONGODB</code> - Designates a connection to a MongoDB document database.</p> </li> <li> <p> <code>NETWORK</code> - Designates a network connection to a data source within an Amazon Virtual Private Cloud environment (Amazon VPC).</p> </li> </ul> <p>SFTP is not supported.</p>", "GetConnectionsFilter$ConnectionType": "<p>The type of connections to return. Currently, SFTP is not supported.</p>" } },
models/apis/organizations/2016-11-28/api-2.json+3 −1 modified@@ -1208,7 +1208,9 @@ "INVALID_EMAIL", "CONCURRENT_ACCOUNT_MODIFICATION", "INTERNAL_FAILURE", - "GOVCLOUD_ACCOUNT_ALREADY_EXISTS" + "GOVCLOUD_ACCOUNT_ALREADY_EXISTS", + "MISSING_BUSINESS_VALIDATION", + "MISSING_PAYMENT_INSTRUMENT" ] }, "CreateAccountRequest":{
models/apis/organizations/2016-11-28/docs-2.json+1 −1 modified@@ -251,7 +251,7 @@ "CreateAccountFailureReason": { "base": null, "refs": { - "CreateAccountStatus$FailureReason": "<p>If the request failed, a description of the reason for the failure.</p> <ul> <li> <p>ACCOUNT_LIMIT_EXCEEDED: The account could not be created because you have reached the limit on the number of accounts in your organization.</p> </li> <li> <p>EMAIL_ALREADY_EXISTS: The account could not be created because another AWS account with that email address already exists.</p> </li> <li> <p>GOVCLOUD_ACCOUNT_ALREADY_EXISTS: The account in the AWS GovCloud (US) Region could not be created because this Region already includes an account with that email address.</p> </li> <li> <p>INVALID_ADDRESS: The account could not be created because the address you provided is not valid.</p> </li> <li> <p>INVALID_EMAIL: The account could not be created because the email address you provided is not valid.</p> </li> <li> <p>INTERNAL_FAILURE: The account could not be created because of an internal failure. Try again later. If the problem persists, contact Customer Support.</p> </li> </ul>" + "CreateAccountStatus$FailureReason": "<p>If the request failed, a description of the reason for the failure.</p> <ul> <li> <p>ACCOUNT_LIMIT_EXCEEDED: The account could not be created because you have reached the limit on the number of accounts in your organization.</p> </li> <li> <p>CONCURRENT_ACCOUNT_MODIFICATION: You already submitted a request with the same information.</p> </li> <li> <p>EMAIL_ALREADY_EXISTS: The account could not be created because another AWS account with that email address already exists.</p> </li> <li> <p>GOVCLOUD_ACCOUNT_ALREADY_EXISTS: The account in the AWS GovCloud (US) Region could not be created because this Region already includes an account with that email address.</p> </li> <li> <p>INVALID_ADDRESS: The account could not be created because the address you provided is not valid.</p> </li> <li> <p>INVALID_EMAIL: The account could not be created because the email address you provided is not valid.</p> </li> <li> <p>INTERNAL_FAILURE: The account could not be created because of an internal failure. Try again later. If the problem persists, contact Customer Support.</p> </li> <li> <p>MISSING_BUSINESS_VALIDATION: The AWS account that owns your organization has not received Business Validation.</p> </li> <li> <p> MISSING_PAYMENT_INSTRUMENT: You must configure the master account with a valid payment method, such as a credit card.</p> </li> </ul>" } }, "CreateAccountRequest": {
models/apis/s3/2006-03-01/docs-2.json+96 −96 modifiedmodels/apis/s3/2006-03-01/examples-1.json+104 −104 modified@@ -292,39 +292,39 @@ { "input": { "Bucket": "examplebucket", - "Key": "HappyFace.jpg", - "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + "Key": "HappyFace.jpg" }, "output": { - "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + "VersionId": "null" }, "comments": { "input": { }, "output": { } }, - "description": "The following example removes tag set associated with the specified object version. The request specifies both the object key and object version.", - "id": "to-remove-tag-set-from-an-object-version-1483145285913", - "title": "To remove tag set from an object version" + "description": "The following example removes tag set associated with the specified object. If the bucket is versioning enabled, the operation removes tag set from the latest object version.", + "id": "to-remove-tag-set-from-an-object-1483145342862", + "title": "To remove tag set from an object" }, { "input": { "Bucket": "examplebucket", - "Key": "HappyFace.jpg" + "Key": "HappyFace.jpg", + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" }, "output": { - "VersionId": "null" + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" }, "comments": { "input": { }, "output": { } }, - "description": "The following example removes tag set associated with the specified object. If the bucket is versioning enabled, the operation removes tag set from the latest object version.", - "id": "to-remove-tag-set-from-an-object-1483145342862", - "title": "To remove tag set from an object" + "description": "The following example removes tag set associated with the specified object version. The request specifies both the object key and object version.", + "id": "to-remove-tag-set-from-an-object-version-1483145285913", + "title": "To remove tag set from an object version" } ], "DeleteObjects": [ @@ -989,47 +989,37 @@ "ListMultipartUploads": [ { "input": { - "Bucket": "examplebucket", - "KeyMarker": "nextkeyfrompreviousresponse", - "MaxUploads": "2", - "UploadIdMarker": "valuefrompreviousresponse" + "Bucket": "examplebucket" }, "output": { - "Bucket": "acl1", - "IsTruncated": true, - "KeyMarker": "", - "MaxUploads": "2", - "NextKeyMarker": "someobjectkey", - "NextUploadIdMarker": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--", - "UploadIdMarker": "", "Uploads": [ { "Initiated": "2014-05-01T05:40:58.000Z", "Initiator": { - "DisplayName": "ownder-display-name", + "DisplayName": "display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "Key": "JavaFile", "Owner": { - "DisplayName": "mohanataws", - "ID": "852b113e7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + "DisplayName": "display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "StorageClass": "STANDARD", - "UploadId": "gZ30jIqlUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" + "UploadId": "examplelUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" }, { "Initiated": "2014-05-01T05:41:27.000Z", "Initiator": { - "DisplayName": "ownder-display-name", + "DisplayName": "display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "Key": "JavaFile", "Owner": { - "DisplayName": "ownder-display-name", + "DisplayName": "display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "StorageClass": "STANDARD", - "UploadId": "b7tZSqIlo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" + "UploadId": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" } ] }, @@ -1039,43 +1029,53 @@ "output": { } }, - "description": "The following example specifies the upload-id-marker and key-marker from previous truncated response to retrieve next setup of multipart uploads.", - "id": "list-next-set-of-multipart-uploads-when-previous-result-is-truncated-1482428106748", - "title": "List next set of multipart uploads when previous result is truncated" + "description": "The following example lists in-progress multipart uploads on a specific bucket.", + "id": "to-list-in-progress-multipart-uploads-on-a-bucket-1481852775260", + "title": "To list in-progress multipart uploads on a bucket" }, { "input": { - "Bucket": "examplebucket" + "Bucket": "examplebucket", + "KeyMarker": "nextkeyfrompreviousresponse", + "MaxUploads": "2", + "UploadIdMarker": "valuefrompreviousresponse" }, "output": { + "Bucket": "acl1", + "IsTruncated": true, + "KeyMarker": "", + "MaxUploads": "2", + "NextKeyMarker": "someobjectkey", + "NextUploadIdMarker": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--", + "UploadIdMarker": "", "Uploads": [ { "Initiated": "2014-05-01T05:40:58.000Z", "Initiator": { - "DisplayName": "display-name", + "DisplayName": "ownder-display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "Key": "JavaFile", "Owner": { - "DisplayName": "display-name", - "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + "DisplayName": "mohanataws", + "ID": "852b113e7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "StorageClass": "STANDARD", - "UploadId": "examplelUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" + "UploadId": "gZ30jIqlUa.CInXklLQtSMJITdUnoZ1Y5GACB5UckOtspm5zbDMCkPF_qkfZzMiFZ6dksmcnqxJyIBvQMG9X9Q--" }, { "Initiated": "2014-05-01T05:41:27.000Z", "Initiator": { - "DisplayName": "display-name", + "DisplayName": "ownder-display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "Key": "JavaFile", "Owner": { - "DisplayName": "display-name", + "DisplayName": "ownder-display-name", "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" }, "StorageClass": "STANDARD", - "UploadId": "examplelo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" + "UploadId": "b7tZSqIlo91lv1iwvWpvCiJWugw2xXLPAD7Z8cJyX9.WiIRgNrdG6Ldsn.9FtS63TCl1Uf5faTB.1U5Ckcbmdw--" } ] }, @@ -1085,9 +1085,9 @@ "output": { } }, - "description": "The following example lists in-progress multipart uploads on a specific bucket.", - "id": "to-list-in-progress-multipart-uploads-on-a-bucket-1481852775260", - "title": "To list in-progress multipart uploads on a bucket" + "description": "The following example specifies the upload-id-marker and key-marker from previous truncated response to retrieve next setup of multipart uploads.", + "id": "list-next-set-of-multipart-uploads-when-previous-result-is-truncated-1482428106748", + "title": "List next set of multipart uploads when previous result is truncated" } ], "ListObjectVersions": [ @@ -1570,152 +1570,152 @@ "Body": "filetoupload", "Bucket": "examplebucket", "Key": "exampleobject", - "ServerSideEncryption": "AES256", - "Tagging": "key1=value1&key2=value2" + "Metadata": { + "metadata1": "value1", + "metadata2": "value2" + } }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "ServerSideEncryption": "AES256", - "VersionId": "Ri.vC6qVlA4dEnjgRV4ZHsHoFIjqEMNt" + "VersionId": "pSKidl4pHBiNwukdbcPXAIs.sshFFOc0" }, "comments": { "input": { }, "output": { } }, - "description": "The following example uploads and object. The request specifies the optional server-side encryption option. The request also specifies optional object tags. If the bucket is versioning enabled, S3 returns version ID in response.", - "id": "to-upload-an-object-and-specify-server-side-encryption-and-object-tags-1483398331831", - "title": "To upload an object and specify server-side encryption and object tags" + "description": "The following example creates an object. The request also specifies optional metadata. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-object-and-specify-user-defined-metadata-1483396974757", + "title": "To upload object and specify user-defined metadata" }, { "input": { + "ACL": "authenticated-read", "Body": "filetoupload", "Bucket": "examplebucket", - "Key": "objectkey" + "Key": "exampleobject" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "VersionId": "Bvq0EDKxOcXLJXNo_Lkz37eM3R4pfzyQ" + "VersionId": "Kirh.unyZwjQ69YxcQLA8z4F5j3kJJKr" }, "comments": { "input": { }, "output": { } }, - "description": "The following example creates an object. If the bucket is versioning enabled, S3 returns version ID in response.", - "id": "to-create-an-object-1483147613675", - "title": "To create an object." + "description": "The following example uploads and object. The request specifies optional canned ACL (access control list) to all READ access to authenticated users. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-an-object-and-specify-canned-acl-1483397779571", + "title": "To upload an object and specify canned ACL." }, { "input": { - "Body": "c:\\HappyFace.jpg", + "Body": "filetoupload", "Bucket": "examplebucket", - "Key": "HappyFace.jpg", - "Tagging": "key1=value1&key2=value2" + "Key": "objectkey" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "VersionId": "psM2sYY4.o1501dSx8wMvnkOzSBB.V4a" + "VersionId": "Bvq0EDKxOcXLJXNo_Lkz37eM3R4pfzyQ" }, "comments": { "input": { }, "output": { } }, - "description": "The following example uploads an object. The request specifies optional object tags. The bucket is versioned, therefore S3 returns version ID of the newly created object.", - "id": "to-upload-an-object-and-specify-optional-tags-1481762310955", - "title": "To upload an object and specify optional tags" + "description": "The following example creates an object. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-create-an-object-1483147613675", + "title": "To create an object." }, { "input": { "Body": "HappyFace.jpg", "Bucket": "examplebucket", - "Key": "HappyFace.jpg", - "ServerSideEncryption": "AES256", - "StorageClass": "STANDARD_IA" + "Key": "HappyFace.jpg" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "ServerSideEncryption": "AES256", - "VersionId": "CG612hodqujkf8FaaNfp8U..FIhLROcp" + "VersionId": "tpf3zF08nBplQK1XLOefGskR7mGDwcDk" }, "comments": { "input": { }, "output": { } }, - "description": "The following example uploads an object. The request specifies optional request headers to directs S3 to use specific storage class and use server-side encryption.", - "id": "to-upload-an-object-(specify-optional-headers)", - "title": "To upload an object (specify optional headers)" + "description": "The following example uploads an object to a versioning-enabled bucket. The source file is specified using Windows file syntax. S3 returns VersionId of the newly created object.", + "id": "to-upload-an-object-1481760101010", + "title": "To upload an object" }, { "input": { - "Body": "filetoupload", + "Body": "c:\\HappyFace.jpg", "Bucket": "examplebucket", - "Key": "exampleobject", - "Metadata": { - "metadata1": "value1", - "metadata2": "value2" - } + "Key": "HappyFace.jpg", + "Tagging": "key1=value1&key2=value2" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "VersionId": "pSKidl4pHBiNwukdbcPXAIs.sshFFOc0" + "VersionId": "psM2sYY4.o1501dSx8wMvnkOzSBB.V4a" }, "comments": { "input": { }, "output": { } }, - "description": "The following example creates an object. The request also specifies optional metadata. If the bucket is versioning enabled, S3 returns version ID in response.", - "id": "to-upload-object-and-specify-user-defined-metadata-1483396974757", - "title": "To upload object and specify user-defined metadata" + "description": "The following example uploads an object. The request specifies optional object tags. The bucket is versioned, therefore S3 returns version ID of the newly created object.", + "id": "to-upload-an-object-and-specify-optional-tags-1481762310955", + "title": "To upload an object and specify optional tags" }, { "input": { - "ACL": "authenticated-read", "Body": "filetoupload", "Bucket": "examplebucket", - "Key": "exampleobject" + "Key": "exampleobject", + "ServerSideEncryption": "AES256", + "Tagging": "key1=value1&key2=value2" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "VersionId": "Kirh.unyZwjQ69YxcQLA8z4F5j3kJJKr" + "ServerSideEncryption": "AES256", + "VersionId": "Ri.vC6qVlA4dEnjgRV4ZHsHoFIjqEMNt" }, "comments": { "input": { }, "output": { } }, - "description": "The following example uploads and object. The request specifies optional canned ACL (access control list) to all READ access to authenticated users. If the bucket is versioning enabled, S3 returns version ID in response.", - "id": "to-upload-an-object-and-specify-canned-acl-1483397779571", - "title": "To upload an object and specify canned ACL." + "description": "The following example uploads and object. The request specifies the optional server-side encryption option. The request also specifies optional object tags. If the bucket is versioning enabled, S3 returns version ID in response.", + "id": "to-upload-an-object-and-specify-server-side-encryption-and-object-tags-1483398331831", + "title": "To upload an object and specify server-side encryption and object tags" }, { "input": { "Body": "HappyFace.jpg", "Bucket": "examplebucket", - "Key": "HappyFace.jpg" + "Key": "HappyFace.jpg", + "ServerSideEncryption": "AES256", + "StorageClass": "STANDARD_IA" }, "output": { "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", - "VersionId": "tpf3zF08nBplQK1XLOefGskR7mGDwcDk" + "ServerSideEncryption": "AES256", + "VersionId": "CG612hodqujkf8FaaNfp8U..FIhLROcp" }, "comments": { "input": { }, "output": { } }, - "description": "The following example uploads an object to a versioning-enabled bucket. The source file is specified using Windows file syntax. S3 returns VersionId of the newly created object.", - "id": "to-upload-an-object-1481760101010", - "title": "To upload an object" + "description": "The following example uploads an object. The request specifies optional request headers to directs S3 to use specific storage class and use server-side encryption.", + "id": "to-upload-an-object-(specify-optional-headers)", + "title": "To upload an object (specify optional headers)" } ], "PutObjectAcl": [ @@ -1826,14 +1826,15 @@ "input": { "Bucket": "examplebucket", "CopySource": "/bucketname/sourceobjectkey", + "CopySourceRange": "bytes=1-100000", "Key": "examplelargeobject", - "PartNumber": "1", + "PartNumber": "2", "UploadId": "exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--" }, "output": { "CopyPartResult": { - "ETag": "\"b0c6f0e7e054ab8fa2536a2677f8734d\"", - "LastModified": "2016-12-29T21:24:43.000Z" + "ETag": "\"65d16d19e65a7508a51f043180edcc36\"", + "LastModified": "2016-12-29T21:44:28.000Z" } }, "comments": { @@ -1842,23 +1843,22 @@ "output": { } }, - "description": "The following example uploads a part of a multipart upload by copying data from an existing object as data source.", - "id": "to-upload-a-part-by-copying-data-from-an-existing-object-as-data-source-1483046746348", - "title": "To upload a part by copying data from an existing object as data source" + "description": "The following example uploads a part of a multipart upload by copying a specified byte range from an existing object as data source.", + "id": "to-upload-a-part-by-copying-byte-range-from-an-existing-object-as-data-source-1483048068594", + "title": "To upload a part by copying byte range from an existing object as data source" }, { "input": { "Bucket": "examplebucket", "CopySource": "/bucketname/sourceobjectkey", - "CopySourceRange": "bytes=1-100000", "Key": "examplelargeobject", - "PartNumber": "2", + "PartNumber": "1", "UploadId": "exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--" }, "output": { "CopyPartResult": { - "ETag": "\"65d16d19e65a7508a51f043180edcc36\"", - "LastModified": "2016-12-29T21:44:28.000Z" + "ETag": "\"b0c6f0e7e054ab8fa2536a2677f8734d\"", + "LastModified": "2016-12-29T21:24:43.000Z" } }, "comments": { @@ -1867,9 +1867,9 @@ "output": { } }, - "description": "The following example uploads a part of a multipart upload by copying a specified byte range from an existing object as data source.", - "id": "to-upload-a-part-by-copying-byte-range-from-an-existing-object-as-data-source-1483048068594", - "title": "To upload a part by copying byte range from an existing object as data source" + "description": "The following example uploads a part of a multipart upload by copying data from an existing object as data source.", + "id": "to-upload-a-part-by-copying-data-from-an-existing-object-as-data-source-1483046746348", + "title": "To upload a part by copying data from an existing object as data source" } ] }
models/apis/sms/2016-10-24/api-2.json+416 −7 modified@@ -97,6 +97,22 @@ {"shape":"OperationNotPermittedException"} ] }, + "DeleteAppValidationConfiguration":{ + "name":"DeleteAppValidationConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteAppValidationConfigurationRequest"}, + "output":{"shape":"DeleteAppValidationConfigurationResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, "DeleteReplicationJob":{ "name":"DeleteReplicationJob", "http":{ @@ -223,6 +239,38 @@ {"shape":"OperationNotPermittedException"} ] }, + "GetAppValidationConfiguration":{ + "name":"GetAppValidationConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetAppValidationConfigurationRequest"}, + "output":{"shape":"GetAppValidationConfigurationResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, + "GetAppValidationOutput":{ + "name":"GetAppValidationOutput", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetAppValidationOutputRequest"}, + "output":{"shape":"GetAppValidationOutputResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, "GetConnectors":{ "name":"GetConnectors", "http":{ @@ -272,7 +320,26 @@ "input":{"shape":"GetServersRequest"}, "output":{"shape":"GetServersResponse"}, "errors":[ - {"shape":"UnauthorizedOperationException"} + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"} + ] + }, + "ImportAppCatalog":{ + "name":"ImportAppCatalog", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ImportAppCatalogRequest"}, + "output":{"shape":"ImportAppCatalogResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} ] }, "ImportServerCatalog":{ @@ -323,6 +390,22 @@ {"shape":"OperationNotPermittedException"} ] }, + "NotifyAppValidationOutput":{ + "name":"NotifyAppValidationOutput", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"NotifyAppValidationOutputRequest"}, + "output":{"shape":"NotifyAppValidationOutputResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, "PutAppLaunchConfiguration":{ "name":"PutAppLaunchConfiguration", "http":{ @@ -355,6 +438,22 @@ {"shape":"OperationNotPermittedException"} ] }, + "PutAppValidationConfiguration":{ + "name":"PutAppValidationConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutAppValidationConfigurationRequest"}, + "output":{"shape":"PutAppValidationConfigurationResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, "StartAppReplication":{ "name":"StartAppReplication", "http":{ @@ -371,6 +470,22 @@ {"shape":"OperationNotPermittedException"} ] }, + "StartOnDemandAppReplication":{ + "name":"StartOnDemandAppReplication", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"StartOnDemandAppReplicationRequest"}, + "output":{"shape":"StartOnDemandAppReplicationResponse"}, + "errors":[ + {"shape":"UnauthorizedOperationException"}, + {"shape":"InvalidParameterException"}, + {"shape":"MissingRequiredParameterException"}, + {"shape":"InternalError"}, + {"shape":"OperationNotPermittedException"} + ] + }, "StartOnDemandReplicationRun":{ "name":"StartOnDemandReplicationRun", "http":{ @@ -384,7 +499,8 @@ {"shape":"MissingRequiredParameterException"}, {"shape":"UnauthorizedOperationException"}, {"shape":"OperationNotPermittedException"}, - {"shape":"ReplicationRunLimitExceededException"} + {"shape":"ReplicationRunLimitExceededException"}, + {"shape":"DryRunOperationException"} ] }, "StopAppReplication":{ @@ -459,10 +575,21 @@ "AmiId":{"type":"string"}, "AppDescription":{"type":"string"}, "AppId":{"type":"string"}, + "AppIdWithValidation":{ + "type":"string", + "pattern":"^app-[0-9a-f]{17}$" + }, "AppIds":{ "type":"list", "member":{"shape":"AppId"} }, + "AppLaunchConfigurationStatus":{ + "type":"string", + "enum":[ + "NOT_CONFIGURED", + "CONFIGURED" + ] + }, "AppLaunchStatus":{ "type":"string", "enum":[ @@ -474,6 +601,7 @@ "LAUNCH_PENDING", "LAUNCH_IN_PROGRESS", "LAUNCHED", + "PARTIALLY_LAUNCHED", "DELTA_LAUNCH_IN_PROGRESS", "DELTA_LAUNCH_FAILED", "LAUNCH_FAILED", @@ -484,6 +612,13 @@ }, "AppLaunchStatusMessage":{"type":"string"}, "AppName":{"type":"string"}, + "AppReplicationConfigurationStatus":{ + "type":"string", + "enum":[ + "NOT_CONFIGURED", + "CONFIGURED" + ] + }, "AppReplicationStatus":{ "type":"string", "enum":[ @@ -495,6 +630,7 @@ "REPLICATION_PENDING", "REPLICATION_IN_PROGRESS", "REPLICATED", + "PARTIALLY_REPLICATED", "DELTA_REPLICATION_IN_PROGRESS", "DELTA_REPLICATED", "DELTA_REPLICATION_FAILED", @@ -521,13 +657,16 @@ "type":"structure", "members":{ "appId":{"shape":"AppId"}, + "importedAppId":{"shape":"ImportedAppId"}, "name":{"shape":"AppName"}, "description":{"shape":"AppDescription"}, "status":{"shape":"AppStatus"}, "statusMessage":{"shape":"AppStatusMessage"}, + "replicationConfigurationStatus":{"shape":"AppReplicationConfigurationStatus"}, "replicationStatus":{"shape":"AppReplicationStatus"}, "replicationStatusMessage":{"shape":"AppReplicationStatusMessage"}, "latestReplicationTime":{"shape":"Timestamp"}, + "launchConfigurationStatus":{"shape":"AppLaunchConfigurationStatus"}, "launchStatus":{"shape":"AppLaunchStatus"}, "launchStatusMessage":{"shape":"AppLaunchStatusMessage"}, "launchDetails":{"shape":"LaunchDetails"}, @@ -538,13 +677,42 @@ "totalServers":{"shape":"TotalServers"} } }, + "AppValidationConfiguration":{ + "type":"structure", + "members":{ + "validationId":{"shape":"ValidationId"}, + "name":{"shape":"NonEmptyStringWithMaxLen255"}, + "appValidationStrategy":{"shape":"AppValidationStrategy"}, + "ssmValidationParameters":{"shape":"SSMValidationParameters"} + } + }, + "AppValidationConfigurations":{ + "type":"list", + "member":{"shape":"AppValidationConfiguration"} + }, + "AppValidationOutput":{ + "type":"structure", + "members":{ + "ssmOutput":{"shape":"SSMOutput"} + } + }, + "AppValidationStrategy":{ + "type":"string", + "enum":["SSM"] + }, "Apps":{ "type":"list", "member":{"shape":"AppSummary"} }, "AssociatePublicIpAddress":{"type":"boolean"}, + "AutoLaunch":{"type":"boolean"}, "BucketName":{"type":"string"}, "ClientToken":{"type":"string"}, + "Command":{ + "type":"string", + "max":64000, + "min":1 + }, "Connector":{ "type":"structure", "members":{ @@ -566,7 +734,8 @@ "VSPHERE", "SCVMM", "HYPERV-MANAGER", - "SNAPSHOT_BATCHING" + "SNAPSHOT_BATCHING", + "SMS_OPTIMIZED" ] }, "ConnectorCapabilityList":{ @@ -665,6 +834,18 @@ "members":{ } }, + "DeleteAppValidationConfigurationRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppIdWithValidation"} + } + }, + "DeleteAppValidationConfigurationResponse":{ + "type":"structure", + "members":{ + } + }, "DeleteReplicationJobRequest":{ "type":"structure", "required":["replicationJobId"], @@ -700,9 +881,21 @@ "members":{ } }, + "DryRunOperationException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "exception":true + }, "EC2KeyName":{"type":"string"}, "Encrypted":{"type":"boolean"}, "ErrorMessage":{"type":"string"}, + "ExecutionTimeoutSeconds":{ + "type":"integer", + "max":28800, + "min":60 + }, "ForceStopAppReplication":{"type":"boolean"}, "ForceTerminateApp":{"type":"boolean"}, "Frequency":{"type":"integer"}, @@ -743,6 +936,7 @@ "members":{ "appId":{"shape":"AppId"}, "roleName":{"shape":"RoleName"}, + "autoLaunch":{"shape":"AutoLaunch"}, "serverGroupLaunchConfigurations":{"shape":"ServerGroupLaunchConfigurations"} } }, @@ -772,6 +966,33 @@ "tags":{"shape":"Tags"} } }, + "GetAppValidationConfigurationRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppIdWithValidation"} + } + }, + "GetAppValidationConfigurationResponse":{ + "type":"structure", + "members":{ + "appValidationConfigurations":{"shape":"AppValidationConfigurations"}, + "serverGroupValidationConfigurations":{"shape":"ServerGroupValidationConfigurations"} + } + }, + "GetAppValidationOutputRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppIdWithValidation"} + } + }, + "GetAppValidationOutputResponse":{ + "type":"structure", + "members":{ + "validationOutputList":{"shape":"ValidationOutputList"} + } + }, "GetConnectorsRequest":{ "type":"structure", "members":{ @@ -835,6 +1056,17 @@ "nextToken":{"shape":"NextToken"} } }, + "ImportAppCatalogRequest":{ + "type":"structure", + "members":{ + "roleName":{"shape":"RoleName"} + } + }, + "ImportAppCatalogResponse":{ + "type":"structure", + "members":{ + } + }, "ImportServerCatalogRequest":{ "type":"structure", "members":{ @@ -845,6 +1077,11 @@ "members":{ } }, + "ImportedAppId":{"type":"string"}, + "InstanceId":{ + "type":"string", + "pattern":"(^i-(\\w{8}|\\w{17})$)|(^mi-\\w{17}$)" + }, "InstanceType":{"type":"string"}, "InternalError":{ "type":"structure", @@ -862,7 +1099,6 @@ "exception":true }, "IpAddress":{"type":"string"}, - "KeyName":{"type":"string"}, "KmsKeyId":{"type":"string"}, "LaunchAppRequest":{ "type":"structure", @@ -924,6 +1160,33 @@ }, "exception":true }, + "NonEmptyStringWithMaxLen255":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\S]+$" + }, + "NotificationContext":{ + "type":"structure", + "members":{ + "validationId":{"shape":"ValidationId"}, + "status":{"shape":"ValidationStatus"}, + "statusMessage":{"shape":"ValidationStatusMessage"} + } + }, + "NotifyAppValidationOutputRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppIdWithValidation"}, + "notificationContext":{"shape":"NotificationContext"} + } + }, + "NotifyAppValidationOutputResponse":{ + "type":"structure", + "members":{ + } + }, "NumberOfRecentAmisToKeep":{"type":"integer"}, "OperationNotPermittedException":{ "type":"structure", @@ -944,6 +1207,7 @@ "members":{ "appId":{"shape":"AppId"}, "roleName":{"shape":"RoleName"}, + "autoLaunch":{"shape":"AutoLaunch"}, "serverGroupLaunchConfigurations":{"shape":"ServerGroupLaunchConfigurations"} } }, @@ -964,6 +1228,20 @@ "members":{ } }, + "PutAppValidationConfigurationRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppIdWithValidation"}, + "appValidationConfigurations":{"shape":"AppValidationConfigurations"}, + "serverGroupValidationConfigurations":{"shape":"ServerGroupValidationConfigurations"} + } + }, + "PutAppValidationConfigurationResponse":{ + "type":"structure", + "members":{ + } + }, "ReplicationJob":{ "type":"structure", "members":{ @@ -1080,13 +1358,46 @@ }, "RoleName":{"type":"string"}, "RunOnce":{"type":"boolean"}, + "S3BucketName":{ + "type":"string", + "max":63, + "min":3 + }, + "S3KeyName":{ + "type":"string", + "max":1024 + }, "S3Location":{ "type":"structure", "members":{ - "bucket":{"shape":"BucketName"}, - "key":{"shape":"KeyName"} + "bucket":{"shape":"S3BucketName"}, + "key":{"shape":"S3KeyName"} } }, + "SSMOutput":{ + "type":"structure", + "members":{ + "s3Location":{"shape":"S3Location"} + } + }, + "SSMValidationParameters":{ + "type":"structure", + "members":{ + "source":{"shape":"Source"}, + "instanceId":{"shape":"InstanceId"}, + "scriptType":{"shape":"ScriptType"}, + "command":{"shape":"Command"}, + "executionTimeoutSeconds":{"shape":"ExecutionTimeoutSeconds"}, + "outputS3BucketName":{"shape":"BucketName"} + } + }, + "ScriptType":{ + "type":"string", + "enum":[ + "SHELL_SCRIPT", + "POWERSHELL_SCRIPT" + ] + }, "SecurityGroup":{"type":"string"}, "Server":{ "type":"structure", @@ -1148,6 +1459,17 @@ "type":"list", "member":{"shape":"ServerGroupReplicationConfiguration"} }, + "ServerGroupValidationConfiguration":{ + "type":"structure", + "members":{ + "serverGroupId":{"shape":"ServerGroupId"}, + "serverValidationConfigurations":{"shape":"ServerValidationConfigurations"} + } + }, + "ServerGroupValidationConfigurations":{ + "type":"list", + "member":{"shape":"ServerGroupValidationConfiguration"} + }, "ServerGroups":{ "type":"list", "member":{"shape":"ServerGroup"} @@ -1164,7 +1486,10 @@ "ec2KeyName":{"shape":"EC2KeyName"}, "userData":{"shape":"UserData"}, "instanceType":{"shape":"InstanceType"}, - "associatePublicIpAddress":{"shape":"AssociatePublicIpAddress"} + "associatePublicIpAddress":{"shape":"AssociatePublicIpAddress"}, + "iamInstanceProfileName":{"shape":"RoleName"}, + "configureScript":{"shape":"S3Location"}, + "configureScriptType":{"shape":"ScriptType"} } }, "ServerLaunchConfigurations":{ @@ -1202,6 +1527,36 @@ "type":"string", "enum":["VIRTUAL_MACHINE"] }, + "ServerValidationConfiguration":{ + "type":"structure", + "members":{ + "server":{"shape":"Server"}, + "validationId":{"shape":"ValidationId"}, + "name":{"shape":"NonEmptyStringWithMaxLen255"}, + "serverValidationStrategy":{"shape":"ServerValidationStrategy"}, + "userDataValidationParameters":{"shape":"UserDataValidationParameters"} + } + }, + "ServerValidationConfigurations":{ + "type":"list", + "member":{"shape":"ServerValidationConfiguration"} + }, + "ServerValidationOutput":{ + "type":"structure", + "members":{ + "server":{"shape":"Server"} + } + }, + "ServerValidationStrategy":{ + "type":"string", + "enum":["USERDATA"] + }, + "Source":{ + "type":"structure", + "members":{ + "s3Location":{"shape":"S3Location"} + } + }, "StackId":{"type":"string"}, "StackName":{"type":"string"}, "StartAppReplicationRequest":{ @@ -1215,6 +1570,19 @@ "members":{ } }, + "StartOnDemandAppReplicationRequest":{ + "type":"structure", + "required":["appId"], + "members":{ + "appId":{"shape":"AppId"}, + "description":{"shape":"Description"} + } + }, + "StartOnDemandAppReplicationResponse":{ + "type":"structure", + "members":{ + } + }, "StartOnDemandReplicationRunRequest":{ "type":"structure", "required":["replicationJobId"], @@ -1327,7 +1695,48 @@ "s3Location":{"shape":"S3Location"} } }, + "UserDataValidationParameters":{ + "type":"structure", + "members":{ + "source":{"shape":"Source"}, + "scriptType":{"shape":"ScriptType"} + } + }, "VPC":{"type":"string"}, + "ValidationId":{ + "type":"string", + "pattern":"^val-[0-9a-f]{17}$" + }, + "ValidationOutput":{ + "type":"structure", + "members":{ + "validationId":{"shape":"ValidationId"}, + "name":{"shape":"NonEmptyStringWithMaxLen255"}, + "status":{"shape":"ValidationStatus"}, + "statusMessage":{"shape":"ValidationStatusMessage"}, + "latestValidationTime":{"shape":"Timestamp"}, + "appValidationOutput":{"shape":"AppValidationOutput"}, + "serverValidationOutput":{"shape":"ServerValidationOutput"} + } + }, + "ValidationOutputList":{ + "type":"list", + "member":{"shape":"ValidationOutput"} + }, + "ValidationStatus":{ + "type":"string", + "enum":[ + "READY_FOR_VALIDATION", + "PENDING", + "IN_PROGRESS", + "SUCCEEDED", + "FAILED" + ] + }, + "ValidationStatusMessage":{ + "type":"string", + "max":2500 + }, "VmId":{"type":"string"}, "VmManagerId":{"type":"string"}, "VmManagerName":{"type":"string"},
models/apis/sms/2016-10-24/docs-2.json+455 −158 modified@@ -1,85 +1,109 @@ { "version": "2.0", - "service": "<fullname>AAWS Sever Migration Service</fullname> <p>This is the <i>AWS Sever Migration Service API Reference</i>. It provides descriptions, syntax, and usage examples for each of the actions and data types for the AWS Sever Migration Service (AWS SMS). The topic for each action shows the Query API request parameters and the XML response. You can also view the XML request elements in the WSDL.</p> <p>Alternatively, you can use one of the AWS SDKs to access an API that's tailored to the programming language or platform that you're using. For more information, see <a href=\"http://aws.amazon.com/tools/#SDKs\">AWS SDKs</a>.</p> <p>To learn more about the Server Migration Service, see the following resources:</p> <ul> <li> <p> <a href=\"https://aws.amazon.com/server-migration-service/\">AWS Sever Migration Service product page</a> </p> </li> <li> <p> <a href=\"https://docs.aws.amazon.com/server-migration-service/latest/userguide/server-migration.html\">AWS Sever Migration Service User Guide</a> </p> </li> </ul>", + "service": "<fullname>AWS Server Migration Service</fullname> <p>AWS Server Migration Service (AWS SMS) makes it easier and faster for you to migrate your on-premises workloads to AWS. To learn more about AWS SMS, see the following resources:</p> <ul> <li> <p> <a href=\"http://aws.amazon.com/server-migration-service/\">AWS Server Migration Service product page</a> </p> </li> <li> <p> <a href=\"https://docs.aws.amazon.com/server-migration-service/latest/userguide/\">AWS Server Migration Service User Guide</a> </p> </li> </ul>", "operations": { "CreateApp": "<p>Creates an application. An application consists of one or more server groups. Each server group contain one or more servers.</p>", "CreateReplicationJob": "<p>Creates a replication job. The replication job schedules periodic replication runs to replicate your server to AWS. Each replication run creates an Amazon Machine Image (AMI).</p>", - "DeleteApp": "<p>Deletes an existing application. Optionally deletes the launched stack associated with the application and all AWS SMS replication jobs for servers in the application.</p>", - "DeleteAppLaunchConfiguration": "<p>Deletes existing launch configuration for an application.</p>", - "DeleteAppReplicationConfiguration": "<p>Deletes existing replication configuration for an application.</p>", + "DeleteApp": "<p>Deletes the specified application. Optionally deletes the launched stack associated with the application and all AWS SMS replication jobs for servers in the application.</p>", + "DeleteAppLaunchConfiguration": "<p>Deletes the launch configuration for the specified application.</p>", + "DeleteAppReplicationConfiguration": "<p>Deletes the replication configuration for the specified application.</p>", + "DeleteAppValidationConfiguration": "<p>Deletes the validation configuration for the specified application.</p>", "DeleteReplicationJob": "<p>Deletes the specified replication job.</p> <p>After you delete a replication job, there are no further replication runs. AWS deletes the contents of the Amazon S3 bucket used to store AWS SMS artifacts. The AMIs created by the replication runs are not deleted.</p>", "DeleteServerCatalog": "<p>Deletes all servers from your server catalog.</p>", "DisassociateConnector": "<p>Disassociates the specified connector from AWS SMS.</p> <p>After you disassociate a connector, it is no longer available to support replication jobs.</p>", "GenerateChangeSet": "<p>Generates a target change set for a currently launched stack and writes it to an Amazon S3 object in the customer’s Amazon S3 bucket.</p>", - "GenerateTemplate": "<p>Generates an Amazon CloudFormation template based on the current launch configuration and writes it to an Amazon S3 object in the customer’s Amazon S3 bucket.</p>", - "GetApp": "<p>Retrieve information about an application.</p>", - "GetAppLaunchConfiguration": "<p>Retrieves the application launch configuration associated with an application.</p>", - "GetAppReplicationConfiguration": "<p>Retrieves an application replication configuration associatd with an application.</p>", + "GenerateTemplate": "<p>Generates an AWS CloudFormation template based on the current launch configuration and writes it to an Amazon S3 object in the customer’s Amazon S3 bucket.</p>", + "GetApp": "<p>Retrieve information about the specified application.</p>", + "GetAppLaunchConfiguration": "<p>Retrieves the application launch configuration associated with the specified application.</p>", + "GetAppReplicationConfiguration": "<p>Retrieves the application replication configuration associated with the specified application.</p>", + "GetAppValidationConfiguration": "<p>Retrieves information about a configuration for validating an application.</p>", + "GetAppValidationOutput": "<p>Retrieves output from validating an application.</p>", "GetConnectors": "<p>Describes the connectors registered with the AWS SMS.</p>", "GetReplicationJobs": "<p>Describes the specified replication job or all of your replication jobs.</p>", "GetReplicationRuns": "<p>Describes the replication runs for the specified replication job.</p>", "GetServers": "<p>Describes the servers in your server catalog.</p> <p>Before you can describe your servers, you must import them using <a>ImportServerCatalog</a>.</p>", - "ImportServerCatalog": "<p>Gathers a complete list of on-premises servers. Connectors must be installed and monitoring all servers that you want to import.</p> <p>This call returns immediately, but might take additional time to retrieve all the servers.</p>", - "LaunchApp": "<p>Launches an application stack.</p>", - "ListApps": "<p>Returns a list of summaries for all applications.</p>", - "PutAppLaunchConfiguration": "<p>Creates a launch configuration for an application.</p>", - "PutAppReplicationConfiguration": "<p>Creates or updates a replication configuration for an application.</p>", - "StartAppReplication": "<p>Starts replicating an application.</p>", - "StartOnDemandReplicationRun": "<p>Starts an on-demand replication run for the specified replication job. This replication run starts immediately. This replication run is in addition to the ones already scheduled.</p> <p>There is a limit on the number of on-demand replications runs you can request in a 24-hour period.</p>", - "StopAppReplication": "<p>Stops replicating an application.</p>", - "TerminateApp": "<p>Terminates the stack for an application.</p>", - "UpdateApp": "<p>Updates an application.</p>", + "ImportAppCatalog": "<p>Allows application import from AWS Migration Hub.</p>", + "ImportServerCatalog": "<p>Gathers a complete list of on-premises servers. Connectors must be installed and monitoring all servers to import.</p> <p>This call returns immediately, but might take additional time to retrieve all the servers.</p>", + "LaunchApp": "<p>Launches the specified application as a stack in AWS CloudFormation.</p>", + "ListApps": "<p>Retrieves summaries for all applications.</p>", + "NotifyAppValidationOutput": "<p>Provides information to AWS SMS about whether application validation is successful.</p>", + "PutAppLaunchConfiguration": "<p>Creates or updates the launch configuration for the specified application.</p>", + "PutAppReplicationConfiguration": "<p>Creates or updates the replication configuration for the specified application.</p>", + "PutAppValidationConfiguration": "<p>Creates or updates a validation configuration for the specified application.</p>", + "StartAppReplication": "<p>Starts replicating the specified application by creating replication jobs for each server in the application.</p>", + "StartOnDemandAppReplication": "<p>Starts an on-demand replication run for the specified application.</p>", + "StartOnDemandReplicationRun": "<p>Starts an on-demand replication run for the specified replication job. This replication run starts immediately. This replication run is in addition to the ones already scheduled.</p> <p>There is a limit on the number of on-demand replications runs that you can request in a 24-hour period.</p>", + "StopAppReplication": "<p>Stops replicating the specified application by deleting the replication job for each server in the application.</p>", + "TerminateApp": "<p>Terminates the stack for the specified application.</p>", + "UpdateApp": "<p>Updates the specified application.</p>", "UpdateReplicationJob": "<p>Updates the specified settings for the specified replication job.</p>" }, "shapes": { "AmiId": { "base": null, "refs": { "ReplicationJob$latestAmiId": "<p>The ID of the latest Amazon Machine Image (AMI).</p>", - "ReplicationRun$amiId": "<p>The identifier of the Amazon Machine Image (AMI) from the replication run.</p>" + "ReplicationRun$amiId": "<p>The ID of the Amazon Machine Image (AMI) from the replication run.</p>" } }, "AppDescription": { "base": null, "refs": { - "AppSummary$description": "<p>Description of the application.</p>", - "CreateAppRequest$description": "<p>Description of the new application</p>", - "UpdateAppRequest$description": "<p>New description of the application.</p>" + "AppSummary$description": "<p>The description of the application.</p>", + "CreateAppRequest$description": "<p>The description of the new application</p>", + "UpdateAppRequest$description": "<p>The new description of the application.</p>" } }, "AppId": { "base": null, "refs": { "AppIds$member": null, - "AppSummary$appId": "<p>Unique ID of the application.</p>", - "DeleteAppLaunchConfigurationRequest$appId": "<p>ID of the application associated with the launch configuration.</p>", - "DeleteAppReplicationConfigurationRequest$appId": "<p>ID of the application associated with the replication configuration.</p>", - "DeleteAppRequest$appId": "<p>ID of the application to delete.</p>", - "GenerateChangeSetRequest$appId": "<p>ID of the application associated with the change set.</p>", - "GenerateTemplateRequest$appId": "<p>ID of the application associated with the Amazon CloudFormation template.</p>", - "GetAppLaunchConfigurationRequest$appId": "<p>ID of the application launch configuration.</p>", - "GetAppLaunchConfigurationResponse$appId": "<p>ID of the application associated with the launch configuration.</p>", - "GetAppReplicationConfigurationRequest$appId": "<p>ID of the application associated with the replication configuration.</p>", - "GetAppRequest$appId": "<p>ID of the application whose information is being retrieved.</p>", - "LaunchAppRequest$appId": "<p>ID of the application to launch.</p>", - "PutAppLaunchConfigurationRequest$appId": "<p>ID of the application associated with the launch configuration.</p>", - "PutAppReplicationConfigurationRequest$appId": "<p>ID of the application tassociated with the replication configuration.</p>", - "StartAppReplicationRequest$appId": "<p>ID of the application to replicate.</p>", - "StopAppReplicationRequest$appId": "<p>ID of the application to stop replicating.</p>", - "TerminateAppRequest$appId": "<p>ID of the application to terminate.</p>", - "UpdateAppRequest$appId": "<p>ID of the application to update.</p>" + "AppSummary$appId": "<p>The unique ID of the application.</p>", + "DeleteAppLaunchConfigurationRequest$appId": "<p>The ID of the application.</p>", + "DeleteAppReplicationConfigurationRequest$appId": "<p>The ID of the application.</p>", + "DeleteAppRequest$appId": "<p>The ID of the application.</p>", + "GenerateChangeSetRequest$appId": "<p>The ID of the application associated with the change set.</p>", + "GenerateTemplateRequest$appId": "<p>The ID of the application associated with the AWS CloudFormation template.</p>", + "GetAppLaunchConfigurationRequest$appId": "<p>The ID of the application.</p>", + "GetAppLaunchConfigurationResponse$appId": "<p>The ID of the application.</p>", + "GetAppReplicationConfigurationRequest$appId": "<p>The ID of the application.</p>", + "GetAppRequest$appId": "<p>The ID of the application.</p>", + "LaunchAppRequest$appId": "<p>The ID of the application.</p>", + "PutAppLaunchConfigurationRequest$appId": "<p>The ID of the application.</p>", + "PutAppReplicationConfigurationRequest$appId": "<p>The ID of the application.</p>", + "StartAppReplicationRequest$appId": "<p>The ID of the application.</p>", + "StartOnDemandAppReplicationRequest$appId": "<p>The ID of the application.</p>", + "StopAppReplicationRequest$appId": "<p>The ID of the application.</p>", + "TerminateAppRequest$appId": "<p>The ID of the application.</p>", + "UpdateAppRequest$appId": "<p>The ID of the application.</p>" + } + }, + "AppIdWithValidation": { + "base": null, + "refs": { + "DeleteAppValidationConfigurationRequest$appId": "<p>The ID of the application.</p>", + "GetAppValidationConfigurationRequest$appId": "<p>The ID of the application.</p>", + "GetAppValidationOutputRequest$appId": "<p>The ID of the application.</p>", + "NotifyAppValidationOutputRequest$appId": "<p>The ID of the application.</p>", + "PutAppValidationConfigurationRequest$appId": "<p>The ID of the application.</p>" } }, "AppIds": { "base": null, "refs": { - "ListAppsRequest$appIds": "<p/>" + "ListAppsRequest$appIds": "<p>The unique application IDs.</p>" + } + }, + "AppLaunchConfigurationStatus": { + "base": null, + "refs": { + "AppSummary$launchConfigurationStatus": "<p>Status of the launch configuration.</p>" } }, "AppLaunchStatus": { "base": null, "refs": { - "AppSummary$launchStatus": "<p>Launch status of the application.</p>" + "AppSummary$launchStatus": "<p>The launch status of the application.</p>" } }, "AppLaunchStatusMessage": { @@ -91,15 +115,21 @@ "AppName": { "base": null, "refs": { - "AppSummary$name": "<p>Name of the application.</p>", - "CreateAppRequest$name": "<p>Name of the new application.</p>", - "UpdateAppRequest$name": "<p>New name of the application.</p>" + "AppSummary$name": "<p>The name of the application.</p>", + "CreateAppRequest$name": "<p>The name of the new application.</p>", + "UpdateAppRequest$name": "<p>The new name of the application.</p>" + } + }, + "AppReplicationConfigurationStatus": { + "base": null, + "refs": { + "AppSummary$replicationConfigurationStatus": "<p>Status of the replication configuration.</p>" } }, "AppReplicationStatus": { "base": null, "refs": { - "AppSummary$replicationStatus": "<p>Replication status of the application.</p>" + "AppSummary$replicationStatus": "<p>The replication status of the application.</p>" } }, "AppReplicationStatusMessage": { @@ -124,33 +154,71 @@ "base": "<p>Information about the application.</p>", "refs": { "Apps$member": null, - "CreateAppResponse$appSummary": "<p>Summary description of the application.</p>", + "CreateAppResponse$appSummary": "<p>A summary description of the application.</p>", "GetAppResponse$appSummary": "<p>Information about the application.</p>", - "UpdateAppResponse$appSummary": "<p>Summary description of the application.</p>" + "UpdateAppResponse$appSummary": "<p>A summary description of the application.</p>" + } + }, + "AppValidationConfiguration": { + "base": "<p>Configuration for validating an application.</p>", + "refs": { + "AppValidationConfigurations$member": null + } + }, + "AppValidationConfigurations": { + "base": null, + "refs": { + "GetAppValidationConfigurationResponse$appValidationConfigurations": "<p>The configuration for application validation.</p>", + "PutAppValidationConfigurationRequest$appValidationConfigurations": "<p>The configuration for application validation.</p>" + } + }, + "AppValidationOutput": { + "base": "<p>Output from validating an application.</p>", + "refs": { + "ValidationOutput$appValidationOutput": "<p>The output from validating an application.</p>" + } + }, + "AppValidationStrategy": { + "base": null, + "refs": { + "AppValidationConfiguration$appValidationStrategy": "<p>The validation strategy.</p>" } }, "Apps": { "base": null, "refs": { - "ListAppsResponse$apps": "<p>A list of application summaries.</p>" + "ListAppsResponse$apps": "<p>The application summaries.</p>" } }, "AssociatePublicIpAddress": { "base": null, "refs": { - "ServerLaunchConfiguration$associatePublicIpAddress": "<p>If true, a publicly accessible IP address is created when launching the server.</p>" + "ServerLaunchConfiguration$associatePublicIpAddress": "<p>Indicates whether a publicly accessible IP address is created when launching the server.</p>" + } + }, + "AutoLaunch": { + "base": null, + "refs": { + "GetAppLaunchConfigurationResponse$autoLaunch": "<p>Indicates whether the application is configured to launch automatically after replication is complete.</p>", + "PutAppLaunchConfigurationRequest$autoLaunch": "<p>Indicates whether the application is configured to launch automatically after replication is complete.</p>" } }, "BucketName": { "base": null, "refs": { - "S3Location$bucket": "<p>Amazon S3 bucket name.</p>" + "SSMValidationParameters$outputS3BucketName": "<p>The name of the S3 bucket for output.</p>" } }, "ClientToken": { "base": null, "refs": { - "CreateAppRequest$clientToken": "<p>A unique, case-sensitive identifier you provide to ensure idempotency of application creation.</p>" + "CreateAppRequest$clientToken": "<p>A unique, case-sensitive identifier that you provide to ensure the idempotency of application creation.</p>" + } + }, + "Command": { + "base": null, + "refs": { + "SSMValidationParameters$command": "<p>The command to run the validation script</p>" } }, "Connector": { @@ -174,8 +242,8 @@ "ConnectorId": { "base": null, "refs": { - "Connector$connectorId": "<p>The identifier of the connector.</p>", - "DisassociateConnectorRequest$connectorId": "<p>The identifier of the connector.</p>" + "Connector$connectorId": "<p>The ID of the connector.</p>", + "DisassociateConnectorRequest$connectorId": "<p>The ID of the connector.</p>" } }, "ConnectorList": { @@ -246,6 +314,16 @@ "refs": { } }, + "DeleteAppValidationConfigurationRequest": { + "base": null, + "refs": { + } + }, + "DeleteAppValidationConfigurationResponse": { + "base": null, + "refs": { + } + }, "DeleteReplicationJobRequest": { "base": null, "refs": { @@ -272,6 +350,7 @@ "CreateReplicationJobRequest$description": "<p>The description of the replication job.</p>", "ReplicationJob$description": "<p>The description of the replication job.</p>", "ReplicationRun$description": "<p>The description of the replication run.</p>", + "StartOnDemandAppReplicationRequest$description": "<p>The description of the replication run.</p>", "StartOnDemandReplicationRunRequest$description": "<p>The description of the replication run.</p>", "UpdateReplicationJobRequest$description": "<p>The description of the replication job.</p>" } @@ -286,25 +365,31 @@ "refs": { } }, + "DryRunOperationException": { + "base": "<p>The user has the required permissions, so the request would have succeeded, but a dry run was performed.</p>", + "refs": { + } + }, "EC2KeyName": { "base": null, "refs": { - "ServerLaunchConfiguration$ec2KeyName": "<p>Name of the EC2 SSH Key to be used for connecting to the launched server.</p>" + "ServerLaunchConfiguration$ec2KeyName": "<p>The name of the Amazon EC2 SSH key to be used for connecting to the launched server.</p>" } }, "Encrypted": { "base": null, "refs": { - "CreateReplicationJobRequest$encrypted": "<p>When <i>true</i>, the replication job produces encrypted AMIs. See also <code>KmsKeyId</code> below.</p>", - "ReplicationJob$encrypted": "<p>Whether the replication job should produce encrypted AMIs or not. See also <code>KmsKeyId</code> below.</p>", - "ReplicationRun$encrypted": "<p>Whether the replication run should produce encrypted AMI or not. See also <code>KmsKeyId</code> below.</p>", - "ServerReplicationParameters$encrypted": "<p>When true, the replication job produces encrypted AMIs. See also <code>KmsKeyId</code> below.</p>", - "UpdateReplicationJobRequest$encrypted": "<p>When true, the replication job produces encrypted AMIs . See also <code>KmsKeyId</code> below.</p>" + "CreateReplicationJobRequest$encrypted": "<p>Indicates whether the replication job produces encrypted AMIs.</p>", + "ReplicationJob$encrypted": "<p>Indicates whether the replication job should produce encrypted AMIs.</p>", + "ReplicationRun$encrypted": "<p>Indicates whether the replication run should produce an encrypted AMI.</p>", + "ServerReplicationParameters$encrypted": "<p>Indicates whether the replication job produces encrypted AMIs.</p>", + "UpdateReplicationJobRequest$encrypted": "<p>When true, the replication job produces encrypted AMIs. For more information, <code>KmsKeyId</code>.</p>" } }, "ErrorMessage": { "base": null, "refs": { + "DryRunOperationException$message": null, "InternalError$message": null, "InvalidParameterException$message": null, "MissingRequiredParameterException$message": null, @@ -317,24 +402,30 @@ "UnauthorizedOperationException$message": null } }, + "ExecutionTimeoutSeconds": { + "base": null, + "refs": { + "SSMValidationParameters$executionTimeoutSeconds": "<p>The timeout interval, in seconds.</p>" + } + }, "ForceStopAppReplication": { "base": null, "refs": { - "DeleteAppRequest$forceStopAppReplication": "<p>While deleting the application, stop all replication jobs corresponding to the servers in the application.</p>" + "DeleteAppRequest$forceStopAppReplication": "<p>Indicates whether to stop all replication jobs corresponding to the servers in the application while deleting the application.</p>" } }, "ForceTerminateApp": { "base": null, "refs": { - "DeleteAppRequest$forceTerminateApp": "<p>While deleting the application, terminate the stack corresponding to the application.</p>" + "DeleteAppRequest$forceTerminateApp": "<p>Indicates whether to terminate the stack corresponding to the application while deleting the application.</p>" } }, "Frequency": { "base": null, "refs": { "CreateReplicationJobRequest$frequency": "<p>The time between consecutive replication runs, in hours.</p>", "ReplicationJob$frequency": "<p>The time between consecutive replication runs, in hours.</p>", - "ServerReplicationParameters$frequency": "<p>Frequency of creating replication jobs for the server.</p>", + "ServerReplicationParameters$frequency": "<p>The frequency of creating replication jobs for the server.</p>", "UpdateReplicationJobRequest$frequency": "<p>The time between consecutive replication runs, in hours.</p>" } }, @@ -388,6 +479,26 @@ "refs": { } }, + "GetAppValidationConfigurationRequest": { + "base": null, + "refs": { + } + }, + "GetAppValidationConfigurationResponse": { + "base": null, + "refs": { + } + }, + "GetAppValidationOutputRequest": { + "base": null, + "refs": { + } + }, + "GetAppValidationOutputResponse": { + "base": null, + "refs": { + } + }, "GetConnectorsRequest": { "base": null, "refs": { @@ -428,6 +539,16 @@ "refs": { } }, + "ImportAppCatalogRequest": { + "base": null, + "refs": { + } + }, + "ImportAppCatalogResponse": { + "base": null, + "refs": { + } + }, "ImportServerCatalogRequest": { "base": null, "refs": { @@ -438,10 +559,22 @@ "refs": { } }, + "ImportedAppId": { + "base": null, + "refs": { + "AppSummary$importedAppId": "<p>The ID of the application.</p>" + } + }, + "InstanceId": { + "base": null, + "refs": { + "SSMValidationParameters$instanceId": "<p>The ID of the instance. The instance must have the following tag: UserForSMSApplicationValidation=true.</p>" + } + }, "InstanceType": { "base": null, "refs": { - "ServerLaunchConfiguration$instanceType": "<p>Instance type to be used for launching the server.</p>" + "ServerLaunchConfiguration$instanceType": "<p>The instance type to use when launching the server.</p>" } }, "InternalError": { @@ -460,20 +593,14 @@ "Connector$ipAddress": "<p>The IP address of the connector.</p>" } }, - "KeyName": { - "base": null, - "refs": { - "S3Location$key": "<p>Amazon S3 bucket key.</p>" - } - }, "KmsKeyId": { "base": null, "refs": { - "CreateReplicationJobRequest$kmsKeyId": "<p>KMS key ID for replication jobs that produce encrypted AMIs. Can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to KMS key ID</p> </li> <li> <p>ARN referring to KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key id is not specified, the customer's default KMS key for EBS is used. </p>", - "ReplicationJob$kmsKeyId": "<p>KMS key ID for replication jobs that produce encrypted AMIs. Can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to KMS key ID</p> </li> <li> <p>ARN referring to KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key id is not specified, the customer's default KMS key for EBS is used. </p>", - "ReplicationRun$kmsKeyId": "<p>KMS key ID for replication jobs that produce encrypted AMIs. Can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to KMS key ID</p> </li> <li> <p>ARN referring to KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key id is not specified, the customer's default KMS key for EBS is used. </p>", - "ServerReplicationParameters$kmsKeyId": "<p/> <p>KMS key ID for replication jobs that produce encrypted AMIs. Can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to KMS key ID</p> </li> <li> <p>ARN referring to KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key id is not specified, the customer's default KMS key for EBS is used. </p>", - "UpdateReplicationJobRequest$kmsKeyId": "<p/> <p>KMS key ID for replication jobs that produce encrypted AMIs. Can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to KMS key ID</p> </li> <li> <p>ARN referring to KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key id is not specified, the customer's default KMS key for EBS is used. </p>" + "CreateReplicationJobRequest$kmsKeyId": "<p>The ID of the KMS key for replication jobs that produce encrypted AMIs. This value can be any of the following:</p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to the KMS key ID</p> </li> <li> <p>ARN referring to the KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key ID is not specified, the customer's default KMS key for Amazon EBS is used. </p>", + "ReplicationJob$kmsKeyId": "<p>The ID of the KMS key for replication jobs that produce encrypted AMIs. This value can be any of the following: </p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to the KMS key ID</p> </li> <li> <p>ARN referring to the KMS key alias</p> </li> </ul> <p>If encrypted is enabled but a KMS key ID is not specified, the customer's default KMS key for Amazon EBS is used.</p>", + "ReplicationRun$kmsKeyId": "<p>The ID of the KMS key for replication jobs that produce encrypted AMIs. This value can be any of the following:</p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to the KMS key ID</p> </li> <li> <p>ARN referring to the KMS key alias</p> </li> </ul> <p> If encrypted is <i>true</i> but a KMS key ID is not specified, the customer's default KMS key for Amazon EBS is used. </p>", + "ServerReplicationParameters$kmsKeyId": "<p>The ID of the KMS key for replication jobs that produce encrypted AMIs. This value can be any of the following:</p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to the KMS key ID</p> </li> <li> <p>ARN referring to the KMS key alias</p> </li> </ul> <p>If encrypted is enabled but a KMS key ID is not specified, the customer's default KMS key for Amazon EBS is used.</p>", + "UpdateReplicationJobRequest$kmsKeyId": "<p>The ID of the KMS key for replication jobs that produce encrypted AMIs. This value can be any of the following:</p> <ul> <li> <p>KMS key ID</p> </li> <li> <p>KMS key alias</p> </li> <li> <p>ARN referring to the KMS key ID</p> </li> <li> <p>ARN referring to the KMS key alias</p> </li> </ul> <p>If encrypted is enabled but a KMS key ID is not specified, the customer's default KMS key for Amazon EBS is used.</p>" } }, "LaunchAppRequest": { @@ -495,15 +622,15 @@ "LaunchOrder": { "base": null, "refs": { - "ServerGroupLaunchConfiguration$launchOrder": "<p>Launch order of servers in the server group.</p>" + "ServerGroupLaunchConfiguration$launchOrder": "<p>The launch order of servers in the server group.</p>" } }, "LicenseType": { "base": null, "refs": { "CreateReplicationJobRequest$licenseType": "<p>The license type to be used for the AMI created by a successful replication run.</p>", "ReplicationJob$licenseType": "<p>The license type to be used for the AMI created by a successful replication run.</p>", - "ServerReplicationParameters$licenseType": "<p>License type for creating a replication job for the server.</p>", + "ServerReplicationParameters$licenseType": "<p>The license type for creating a replication job for the server.</p>", "UpdateReplicationJobRequest$licenseType": "<p>The license type to be used for the AMI created by a successful replication run.</p>" } }, @@ -520,7 +647,7 @@ "LogicalId": { "base": null, "refs": { - "ServerLaunchConfiguration$logicalId": "<p>Logical ID of the server in the Amazon CloudFormation template.</p>" + "ServerLaunchConfiguration$logicalId": "<p>The logical ID of the server in the AWS CloudFormation template.</p>" } }, "MacAddress": { @@ -536,7 +663,7 @@ "GetReplicationJobsRequest$maxResults": "<p>The maximum number of results to return in a single call. The default value is 50. To retrieve the remaining results, make another call with the returned <code>NextToken</code> value.</p>", "GetReplicationRunsRequest$maxResults": "<p>The maximum number of results to return in a single call. The default value is 50. To retrieve the remaining results, make another call with the returned <code>NextToken</code> value.</p>", "GetServersRequest$maxResults": "<p>The maximum number of results to return in a single call. The default value is 50. To retrieve the remaining results, make another call with the returned <code>NextToken</code> value.</p>", - "ListAppsRequest$maxResults": "<p>The maximum number of results to return in a single call. The default value is 50. To retrieve the remaining results, make another call with the returned <code>NextToken</code> value. </p>" + "ListAppsRequest$maxResults": "<p>The maximum number of results to return in a single call. The default value is 100. To retrieve the remaining results, make another call with the returned <code>NextToken</code> value. </p>" } }, "MissingRequiredParameterException": { @@ -564,13 +691,37 @@ "refs": { } }, + "NonEmptyStringWithMaxLen255": { + "base": null, + "refs": { + "AppValidationConfiguration$name": "<p>The name of the configuration.</p>", + "ServerValidationConfiguration$name": "<p>The name of the configuration.</p>", + "ValidationOutput$name": "<p>The name of the validation.</p>" + } + }, + "NotificationContext": { + "base": "<p>Contains the status of validating an application.</p>", + "refs": { + "NotifyAppValidationOutputRequest$notificationContext": "<p>The notification information.</p>" + } + }, + "NotifyAppValidationOutputRequest": { + "base": null, + "refs": { + } + }, + "NotifyAppValidationOutputResponse": { + "base": null, + "refs": { + } + }, "NumberOfRecentAmisToKeep": { "base": null, "refs": { - "CreateReplicationJobRequest$numberOfRecentAmisToKeep": "<p>The maximum number of SMS-created AMIs to retain. The oldest will be deleted once the maximum number is reached and a new AMI is created.</p>", - "ReplicationJob$numberOfRecentAmisToKeep": "<p>Number of recent AMIs to keep in the customer's account for a replication job. By default the value is set to zero, meaning that all AMIs are kept.</p>", - "ServerReplicationParameters$numberOfRecentAmisToKeep": "<p>Number of recent AMIs to keep when creating a replication job for this server.</p>", - "UpdateReplicationJobRequest$numberOfRecentAmisToKeep": "<p>The maximum number of SMS-created AMIs to retain. The oldest will be deleted once the maximum number is reached and a new AMI is created.</p>" + "CreateReplicationJobRequest$numberOfRecentAmisToKeep": "<p>The maximum number of SMS-created AMIs to retain. The oldest is deleted after the maximum number is reached and a new AMI is created.</p>", + "ReplicationJob$numberOfRecentAmisToKeep": "<p>The number of recent AMIs to keep in the customer's account for a replication job. By default, the value is set to zero, meaning that all AMIs are kept.</p>", + "ServerReplicationParameters$numberOfRecentAmisToKeep": "<p>The number of recent AMIs to keep when creating a replication job for this server.</p>", + "UpdateReplicationJobRequest$numberOfRecentAmisToKeep": "<p>The maximum number of SMS-created AMIs to retain. The oldest is deleted after the maximum number is reached and a new AMI is created.</p>" } }, "OperationNotPermittedException": { @@ -581,8 +732,8 @@ "OutputFormat": { "base": null, "refs": { - "GenerateChangeSetRequest$changesetFormat": "<p>Format for the change set.</p>", - "GenerateTemplateRequest$templateFormat": "<p>Format for generating the Amazon CloudFormation template.</p>" + "GenerateChangeSetRequest$changesetFormat": "<p>The format for the change set.</p>", + "GenerateTemplateRequest$templateFormat": "<p>The format for generating the AWS CloudFormation template.</p>" } }, "PutAppLaunchConfigurationRequest": { @@ -605,6 +756,16 @@ "refs": { } }, + "PutAppValidationConfigurationRequest": { + "base": null, + "refs": { + } + }, + "PutAppValidationConfigurationResponse": { + "base": null, + "refs": { + } + }, "ReplicationJob": { "base": "<p>Represents a replication job.</p>", "refs": { @@ -621,13 +782,13 @@ "base": null, "refs": { "CreateReplicationJobResponse$replicationJobId": "<p>The unique identifier of the replication job.</p>", - "DeleteReplicationJobRequest$replicationJobId": "<p>The identifier of the replication job.</p>", - "GetReplicationJobsRequest$replicationJobId": "<p>The identifier of the replication job.</p>", - "GetReplicationRunsRequest$replicationJobId": "<p>The identifier of the replication job.</p>", - "ReplicationJob$replicationJobId": "<p>The identifier of the replication job.</p>", - "Server$replicationJobId": "<p>The identifier of the replication job.</p>", - "StartOnDemandReplicationRunRequest$replicationJobId": "<p>The identifier of the replication job.</p>", - "UpdateReplicationJobRequest$replicationJobId": "<p>The identifier of the replication job.</p>" + "DeleteReplicationJobRequest$replicationJobId": "<p>The ID of the replication job.</p>", + "GetReplicationJobsRequest$replicationJobId": "<p>The ID of the replication job.</p>", + "GetReplicationRunsRequest$replicationJobId": "<p>The ID of the replication job.</p>", + "ReplicationJob$replicationJobId": "<p>The ID of the replication job.</p>", + "Server$replicationJobId": "<p>The ID of the replication job.</p>", + "StartOnDemandReplicationRunRequest$replicationJobId": "<p>The ID of the replication job.</p>", + "UpdateReplicationJobRequest$replicationJobId": "<p>The ID of the replication job.</p>" } }, "ReplicationJobList": { @@ -668,8 +829,8 @@ "ReplicationRunId": { "base": null, "refs": { - "ReplicationRun$replicationRunId": "<p>The identifier of the replication run.</p>", - "StartOnDemandReplicationRunResponse$replicationRunId": "<p>The identifier of the replication run.</p>" + "ReplicationRun$replicationRunId": "<p>The ID of the replication run.</p>", + "StartOnDemandReplicationRunResponse$replicationRunId": "<p>The ID of the replication run.</p>" } }, "ReplicationRunLimitExceededException": { @@ -687,19 +848,19 @@ "ReplicationRunStage": { "base": null, "refs": { - "ReplicationRunStageDetails$stage": "<p>String describing the current stage of a replication run.</p>" + "ReplicationRunStageDetails$stage": "<p>The current stage of a replication run.</p>" } }, "ReplicationRunStageDetails": { "base": "<p>Details of the current stage of a replication run.</p>", "refs": { - "ReplicationRun$stageDetails": "<p>Details of the current stage of the replication run.</p>" + "ReplicationRun$stageDetails": "<p>Details about the current stage of the replication run.</p>" } }, "ReplicationRunStageProgress": { "base": null, "refs": { - "ReplicationRunStageDetails$stageProgress": "<p>String describing the progress of the current stage of a replication run.</p>" + "ReplicationRunStageDetails$stageProgress": "<p>The progress of the current stage of a replication run.</p>" } }, "ReplicationRunState": { @@ -723,44 +884,83 @@ "RoleName": { "base": null, "refs": { - "AppSummary$roleName": "<p>Name of the service role in the customer's account used by AWS SMS.</p>", - "CreateAppRequest$roleName": "<p>Name of service role in customer's account to be used by AWS SMS.</p>", + "AppSummary$roleName": "<p>The name of the service role in the customer's account used by AWS SMS.</p>", + "CreateAppRequest$roleName": "<p>The name of the service role in the customer's account to be used by AWS SMS.</p>", "CreateReplicationJobRequest$roleName": "<p>The name of the IAM role to be used by the AWS SMS.</p>", - "GetAppLaunchConfigurationResponse$roleName": "<p>Name of the service role in the customer's account that Amazon CloudFormation uses to launch the application.</p>", - "PutAppLaunchConfigurationRequest$roleName": "<p>Name of service role in the customer's account that Amazon CloudFormation uses to launch the application.</p>", - "ReplicationJob$roleName": "<p>The name of the IAM role to be used by the Server Migration Service.</p>", - "UpdateAppRequest$roleName": "<p>Name of the service role in the customer's account used by AWS SMS.</p>", + "GetAppLaunchConfigurationResponse$roleName": "<p>The name of the service role in the customer's account that AWS CloudFormation uses to launch the application.</p>", + "ImportAppCatalogRequest$roleName": "<p>The name of the service role. If you omit this parameter, we create a service-linked role for AWS Migration Hub in your account. Otherwise, the role that you provide must have the <a href=\"https://docs.aws.amazon.com/migrationhub/latest/ug/new-customer-setup.html#sms-managed\">policy and trust policy</a> described in the <i>AWS Migration Hub User Guide</i>.</p>", + "PutAppLaunchConfigurationRequest$roleName": "<p>The name of service role in the customer's account that AWS CloudFormation uses to launch the application.</p>", + "ReplicationJob$roleName": "<p>The name of the IAM role to be used by AWS SMS.</p>", + "ServerLaunchConfiguration$iamInstanceProfileName": "<p>The name of the IAM instance profile.</p>", + "UpdateAppRequest$roleName": "<p>The name of the service role in the customer's account used by AWS SMS.</p>", "UpdateReplicationJobRequest$roleName": "<p>The name of the IAM role to be used by AWS SMS.</p>" } }, "RunOnce": { "base": null, "refs": { - "CreateReplicationJobRequest$runOnce": "<p/>", - "ReplicationJob$runOnce": "<p/>", - "ServerReplicationParameters$runOnce": "<p/>" + "CreateReplicationJobRequest$runOnce": "<p>Indicates whether to run the replication job one time.</p>", + "ReplicationJob$runOnce": "<p>Indicates whether to run the replication job one time.</p>", + "ServerReplicationParameters$runOnce": "<p>Indicates whether to run the replication job one time.</p>" + } + }, + "S3BucketName": { + "base": null, + "refs": { + "S3Location$bucket": "<p>The Amazon S3 bucket name.</p>" + } + }, + "S3KeyName": { + "base": null, + "refs": { + "S3Location$key": "<p>The Amazon S3 bucket key.</p>" } }, "S3Location": { - "base": "<p>Location of the Amazon S3 object in the customer's account.</p>", + "base": "<p>Location of an Amazon S3 object.</p>", "refs": { - "GenerateChangeSetResponse$s3Location": "<p>Location of the Amazon S3 object.</p>", - "GenerateTemplateResponse$s3Location": "<p>Location of the Amazon S3 object.</p>", + "GenerateChangeSetResponse$s3Location": "<p>The location of the Amazon S3 object.</p>", + "GenerateTemplateResponse$s3Location": "<p>The location of the Amazon S3 object.</p>", + "SSMOutput$s3Location": null, + "ServerLaunchConfiguration$configureScript": null, + "Source$s3Location": null, "UserData$s3Location": "<p>Amazon S3 location of the user-data script.</p>" } }, + "SSMOutput": { + "base": "<p>Contains the location of validation output.</p>", + "refs": { + "AppValidationOutput$ssmOutput": "<p>Output from using SSM to validate the application.</p>" + } + }, + "SSMValidationParameters": { + "base": "<p>Contains validation parameters.</p>", + "refs": { + "AppValidationConfiguration$ssmValidationParameters": "<p>The validation parameters.</p>" + } + }, + "ScriptType": { + "base": null, + "refs": { + "SSMValidationParameters$scriptType": "<p>The type of validation script.</p>", + "ServerLaunchConfiguration$configureScriptType": "<p>The type of configuration script.</p>", + "UserDataValidationParameters$scriptType": "<p>The type of validation script.</p>" + } + }, "SecurityGroup": { "base": null, "refs": { - "ServerLaunchConfiguration$securityGroup": "<p>Identifier of the security group that applies to the launched server.</p>" + "ServerLaunchConfiguration$securityGroup": "<p>The ID of the security group that applies to the launched server.</p>" } }, "Server": { "base": "<p>Represents a server.</p>", "refs": { - "ServerLaunchConfiguration$server": "<p>Identifier of the server the launch configuration is associated with.</p>", + "ServerLaunchConfiguration$server": "<p>The ID of the server with which the launch configuration is associated.</p>", "ServerList$member": null, - "ServerReplicationConfiguration$server": "<p>Identifier of the server this replication configuration is associated with.</p>" + "ServerReplicationConfiguration$server": "<p>The ID of the server with which this replication configuration is associated.</p>", + "ServerValidationConfiguration$server": null, + "ServerValidationOutput$server": null } }, "ServerCannotBeReplicatedException": { @@ -775,17 +975,18 @@ } }, "ServerGroup": { - "base": "<p>A logical grouping of servers.</p>", + "base": "<p>Logical grouping of servers.</p>", "refs": { "ServerGroups$member": null } }, "ServerGroupId": { "base": null, "refs": { - "ServerGroup$serverGroupId": "<p>Identifier of a server group.</p>", - "ServerGroupLaunchConfiguration$serverGroupId": "<p>Identifier of the server group the launch configuration is associated with.</p>", - "ServerGroupReplicationConfiguration$serverGroupId": "<p>Identifier of the server group this replication configuration is associated with.</p>" + "ServerGroup$serverGroupId": "<p>The ID of a server group.</p>", + "ServerGroupLaunchConfiguration$serverGroupId": "<p>The ID of the server group with which the launch configuration is associated.</p>", + "ServerGroupReplicationConfiguration$serverGroupId": "<p>The ID of the server group with which this replication configuration is associated.</p>", + "ServerGroupValidationConfiguration$serverGroupId": "<p>The ID of the server group.</p>" } }, "ServerGroupLaunchConfiguration": { @@ -797,14 +998,14 @@ "ServerGroupLaunchConfigurations": { "base": null, "refs": { - "GetAppLaunchConfigurationResponse$serverGroupLaunchConfigurations": "<p>List of launch configurations for server groups in this application.</p>", - "PutAppLaunchConfigurationRequest$serverGroupLaunchConfigurations": "<p>Launch configurations for server groups in the application.</p>" + "GetAppLaunchConfigurationResponse$serverGroupLaunchConfigurations": "<p>The launch configurations for server groups in this application.</p>", + "PutAppLaunchConfigurationRequest$serverGroupLaunchConfigurations": "<p>Information about the launch configurations for server groups in the application.</p>" } }, "ServerGroupName": { "base": null, "refs": { - "ServerGroup$name": "<p>Name of a server group.</p>" + "ServerGroup$name": "<p>The name of a server group.</p>" } }, "ServerGroupReplicationConfiguration": { @@ -816,26 +1017,39 @@ "ServerGroupReplicationConfigurations": { "base": null, "refs": { - "GetAppReplicationConfigurationResponse$serverGroupReplicationConfigurations": "<p>Replication configurations associated with server groups in this application.</p>", - "PutAppReplicationConfigurationRequest$serverGroupReplicationConfigurations": "<p>Replication configurations for server groups in the application.</p>" + "GetAppReplicationConfigurationResponse$serverGroupReplicationConfigurations": "<p>The replication configurations associated with server groups in this application.</p>", + "PutAppReplicationConfigurationRequest$serverGroupReplicationConfigurations": "<p>Information about the replication configurations for server groups in the application.</p>" + } + }, + "ServerGroupValidationConfiguration": { + "base": "<p>Configuration for validating an instance.</p>", + "refs": { + "ServerGroupValidationConfigurations$member": null + } + }, + "ServerGroupValidationConfigurations": { + "base": null, + "refs": { + "GetAppValidationConfigurationResponse$serverGroupValidationConfigurations": "<p>The configuration for instance validation.</p>", + "PutAppValidationConfigurationRequest$serverGroupValidationConfigurations": "<p>The configuration for instance validation.</p>" } }, "ServerGroups": { "base": null, "refs": { - "CreateAppRequest$serverGroups": "<p>List of server groups to include in the application.</p>", - "CreateAppResponse$serverGroups": "<p>List of server groups included in the application.</p>", - "GetAppResponse$serverGroups": "<p>List of server groups belonging to the application.</p>", - "UpdateAppRequest$serverGroups": "<p>List of server groups in the application to update.</p>", - "UpdateAppResponse$serverGroups": "<p>List of updated server groups in the application.</p>" + "CreateAppRequest$serverGroups": "<p>The server groups to include in the application.</p>", + "CreateAppResponse$serverGroups": "<p>The server groups included in the application.</p>", + "GetAppResponse$serverGroups": "<p>The server groups that belong to the application.</p>", + "UpdateAppRequest$serverGroups": "<p>The server groups in the application to update.</p>", + "UpdateAppResponse$serverGroups": "<p>The updated server groups in the application.</p>" } }, "ServerId": { "base": null, "refs": { - "CreateReplicationJobRequest$serverId": "<p>The identifier of the server.</p>", - "ReplicationJob$serverId": "<p>The identifier of the server.</p>", - "Server$serverId": "<p>The identifier of the server.</p>" + "CreateReplicationJobRequest$serverId": "<p>The ID of the server.</p>", + "ReplicationJob$serverId": "<p>The ID of the server.</p>", + "Server$serverId": "<p>The ID of the server.</p>" } }, "ServerLaunchConfiguration": { @@ -847,14 +1061,14 @@ "ServerLaunchConfigurations": { "base": null, "refs": { - "ServerGroupLaunchConfiguration$serverLaunchConfigurations": "<p>Launch configuration for servers in the server group.</p>" + "ServerGroupLaunchConfiguration$serverLaunchConfigurations": "<p>The launch configuration for servers in the server group.</p>" } }, "ServerList": { "base": null, "refs": { "GetServersResponse$serverList": "<p>Information about the servers.</p>", - "ServerGroup$serverList": "<p>List of servers belonging to a server group.</p>" + "ServerGroup$serverList": "<p>The servers that belong to a server group.</p>" } }, "ServerReplicationConfiguration": { @@ -866,13 +1080,13 @@ "ServerReplicationConfigurations": { "base": null, "refs": { - "ServerGroupReplicationConfiguration$serverReplicationConfigurations": "<p>Replication configuration for servers in the server group.</p>" + "ServerGroupReplicationConfiguration$serverReplicationConfigurations": "<p>The replication configuration for servers in the server group.</p>" } }, "ServerReplicationParameters": { - "base": "<p>Replication parameters for replicating a server.</p>", + "base": "<p>The replication parameters for replicating a server.</p>", "refs": { - "ServerReplicationConfiguration$serverReplicationParameters": "<p>Parameters for replicating the server.</p>" + "ServerReplicationConfiguration$serverReplicationParameters": "<p>The parameters for replicating the server.</p>" } }, "ServerType": { @@ -882,16 +1096,47 @@ "Server$serverType": "<p>The type of server.</p>" } }, + "ServerValidationConfiguration": { + "base": "<p>Configuration for validating an instance.</p>", + "refs": { + "ServerValidationConfigurations$member": null + } + }, + "ServerValidationConfigurations": { + "base": null, + "refs": { + "ServerGroupValidationConfiguration$serverValidationConfigurations": "<p>The validation configuration.</p>" + } + }, + "ServerValidationOutput": { + "base": "<p>Contains output from validating an instance.</p>", + "refs": { + "ValidationOutput$serverValidationOutput": "<p>The output from validation an instance.</p>" + } + }, + "ServerValidationStrategy": { + "base": null, + "refs": { + "ServerValidationConfiguration$serverValidationStrategy": "<p>The validation strategy.</p>" + } + }, + "Source": { + "base": "<p>Contains the location of a validation script.</p>", + "refs": { + "SSMValidationParameters$source": "<p>The location of the validation script.</p>", + "UserDataValidationParameters$source": "<p>The location of the validation script.</p>" + } + }, "StackId": { "base": null, "refs": { - "LaunchDetails$stackId": "<p>Identifier of the latest stack launched for this application.</p>" + "LaunchDetails$stackId": "<p>The ID of the latest stack launched for this application.</p>" } }, "StackName": { "base": null, "refs": { - "LaunchDetails$stackName": "<p>Name of the latest stack launched for this application.</p>" + "LaunchDetails$stackName": "<p>The name of the latest stack launched for this application.</p>" } }, "StartAppReplicationRequest": { @@ -904,6 +1149,16 @@ "refs": { } }, + "StartOnDemandAppReplicationRequest": { + "base": null, + "refs": { + } + }, + "StartOnDemandAppReplicationResponse": { + "base": null, + "refs": { + } + }, "StartOnDemandReplicationRunRequest": { "base": null, "refs": { @@ -927,35 +1182,35 @@ "Subnet": { "base": null, "refs": { - "ServerLaunchConfiguration$subnet": "<p>Identifier of the subnet the server should be launched into.</p>" + "ServerLaunchConfiguration$subnet": "<p>The ID of the subnet the server should be launched into.</p>" } }, "Tag": { - "base": "<p>A label that can be assigned to an application.</p>", + "base": "<p>Key/value pair that can be assigned to an application.</p>", "refs": { "Tags$member": null } }, "TagKey": { "base": null, "refs": { - "Tag$key": "<p>Tag key.</p>" + "Tag$key": "<p>The tag key.</p>" } }, "TagValue": { "base": null, "refs": { - "Tag$value": "<p>Tag value.</p>" + "Tag$value": "<p>The tag value.</p>" } }, "Tags": { "base": null, "refs": { - "CreateAppRequest$tags": "<p>List of tags to be associated with the application.</p>", - "CreateAppResponse$tags": "<p>List of taags associated with the application.</p>", - "GetAppResponse$tags": "<p>List of tags associated with the application.</p>", - "UpdateAppRequest$tags": "<p>List of tags to associate with the application.</p>", - "UpdateAppResponse$tags": "<p>List of tags associated with the application.</p>" + "CreateAppRequest$tags": "<p>The tags to be associated with the application.</p>", + "CreateAppResponse$tags": "<p>The tags associated with the application.</p>", + "GetAppResponse$tags": "<p>The tags associated with the application.</p>", + "UpdateAppRequest$tags": "<p>The tags to associate with the application.</p>", + "UpdateAppResponse$tags": "<p>The tags associated with the application.</p>" } }, "TemporarilyUnavailableException": { @@ -976,31 +1231,32 @@ "Timestamp": { "base": null, "refs": { - "AppSummary$latestReplicationTime": "<p>Timestamp of the application's most recent successful replication.</p>", - "AppSummary$creationTime": "<p>Time of creation of this application.</p>", - "AppSummary$lastModified": "<p>Timestamp of the application's creation.</p>", + "AppSummary$latestReplicationTime": "<p>The timestamp of the application's most recent successful replication.</p>", + "AppSummary$creationTime": "<p>The creation time of the application.</p>", + "AppSummary$lastModified": "<p>The last modified time of the application.</p>", "Connector$associatedOn": "<p>The time the connector was associated.</p>", "CreateReplicationJobRequest$seedReplicationTime": "<p>The seed replication time.</p>", "GetServersResponse$lastModifiedOn": "<p>The time when the server was last modified.</p>", - "LaunchDetails$latestLaunchTime": "<p>Latest time this application was launched successfully.</p>", + "LaunchDetails$latestLaunchTime": "<p>The latest time that this application was launched successfully.</p>", "ReplicationJob$seedReplicationTime": "<p>The seed replication time.</p>", "ReplicationJob$nextReplicationRunStartTime": "<p>The start time of the next replication run.</p>", "ReplicationRun$scheduledStartTime": "<p>The start time of the next replication run.</p>", "ReplicationRun$completedTime": "<p>The completion time of the last replication run.</p>", - "ServerReplicationParameters$seedTime": "<p>Seed time for creating a replication job for the server.</p>", - "UpdateReplicationJobRequest$nextReplicationRunStartTime": "<p>The start time of the next replication run.</p>" + "ServerReplicationParameters$seedTime": "<p>The seed time for creating a replication job for the server.</p>", + "UpdateReplicationJobRequest$nextReplicationRunStartTime": "<p>The start time of the next replication run.</p>", + "ValidationOutput$latestValidationTime": "<p>The latest time that the validation was performed.</p>" } }, "TotalServerGroups": { "base": null, "refs": { - "AppSummary$totalServerGroups": "<p>Number of server groups present in the application.</p>" + "AppSummary$totalServerGroups": "<p>The number of server groups present in the application.</p>" } }, "TotalServers": { "base": null, "refs": { - "AppSummary$totalServers": "<p>Number of servers present in the application.</p>" + "AppSummary$totalServers": "<p>The number of servers present in the application.</p>" } }, "UnauthorizedOperationException": { @@ -1034,23 +1290,64 @@ "ServerLaunchConfiguration$userData": "<p>Location of the user-data script to be executed when launching the server.</p>" } }, + "UserDataValidationParameters": { + "base": "<p>Contains validation parameters.</p>", + "refs": { + "ServerValidationConfiguration$userDataValidationParameters": "<p>The validation parameters.</p>" + } + }, "VPC": { "base": null, "refs": { - "ServerLaunchConfiguration$vpc": "<p>Identifier of the VPC the server should be launched into.</p>" + "ServerLaunchConfiguration$vpc": "<p>The ID of the VPC into which the server should be launched.</p>" + } + }, + "ValidationId": { + "base": null, + "refs": { + "AppValidationConfiguration$validationId": "<p>The ID of the validation.</p>", + "NotificationContext$validationId": "<p>The ID of the validation.</p>", + "ServerValidationConfiguration$validationId": "<p>The ID of the validation.</p>", + "ValidationOutput$validationId": "<p>The ID of the validation.</p>" + } + }, + "ValidationOutput": { + "base": "<p>Contains validation output.</p>", + "refs": { + "ValidationOutputList$member": null + } + }, + "ValidationOutputList": { + "base": null, + "refs": { + "GetAppValidationOutputResponse$validationOutputList": "<p>The validation output.</p>" + } + }, + "ValidationStatus": { + "base": null, + "refs": { + "NotificationContext$status": "<p>The status of the validation.</p>", + "ValidationOutput$status": "<p>The status of the validation.</p>" + } + }, + "ValidationStatusMessage": { + "base": null, + "refs": { + "NotificationContext$statusMessage": "<p>The status message.</p>", + "ValidationOutput$statusMessage": "<p>The status message.</p>" } }, "VmId": { "base": null, "refs": { - "VmServerAddress$vmId": "<p>The identifier of the VM.</p>" + "VmServerAddress$vmId": "<p>The ID of the VM.</p>" } }, "VmManagerId": { "base": null, "refs": { - "Connector$vmManagerId": "<p>The identifier of the VM manager.</p>", - "VmServerAddress$vmManagerId": "<p>The identifier of the VM manager.</p>" + "Connector$vmManagerId": "<p>The ID of the VM manager.</p>", + "VmServerAddress$vmManagerId": "<p>The ID of the VM manager.</p>" } }, "VmManagerName": { @@ -1089,14 +1386,14 @@ "VmServerAddress": { "base": "<p>Represents a VM server location.</p>", "refs": { - "VmServer$vmServerAddress": "<p>Information about the VM server location.</p>", + "VmServer$vmServerAddress": "<p>The VM server location.</p>", "VmServerAddressList$member": null } }, "VmServerAddressList": { "base": null, "refs": { - "GetServersRequest$vmServerAddressList": "<p>List of <code>VmServerAddress</code> objects</p>" + "GetServersRequest$vmServerAddressList": "<p>The server addresses.</p>" } } }
models/endpoints/endpoints.json+14 −0 modified@@ -1954,6 +1954,7 @@ "protocols" : [ "http", "https" ] }, "endpoints" : { + "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, @@ -1963,6 +1964,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3781,6 +3783,12 @@ }, "neptune" : { "endpoints" : { + "ap-east-1" : { + "credentialScope" : { + "region" : "ap-east-1" + }, + "hostname" : "rds.ap-east-1.amazonaws.com" + }, "ap-northeast-1" : { "credentialScope" : { "region" : "ap-northeast-1" @@ -3853,6 +3861,12 @@ }, "hostname" : "rds.me-south-1.amazonaws.com" }, + "sa-east-1" : { + "credentialScope" : { + "region" : "sa-east-1" + }, + "hostname" : "rds.sa-east-1.amazonaws.com" + }, "us-east-1" : { "credentialScope" : { "region" : "us-east-1"
service/glue/api.go+16 −0 modified@@ -16459,6 +16459,9 @@ type ConnectionInput struct { // // * MONGODB - Designates a connection to a MongoDB document database. // + // * NETWORK - Designates a network connection to a data source within an + // Amazon Virtual Private Cloud environment (Amazon VPC). + // // SFTP is not supported. // // ConnectionType is a required field @@ -31277,6 +31280,10 @@ func (s *S3Encryption) SetS3EncryptionMode(v string) *S3Encryption { type S3Target struct { _ struct{} `type:"structure"` + // The name of a connection which allows a job or crawler to access data in + // Amazon S3 within an Amazon Virtual Private Cloud environment (Amazon VPC). + ConnectionName *string `type:"string"` + // A list of glob patterns used to exclude from the crawl. For more information, // see Catalog Tables with a Crawler (https://docs.aws.amazon.com/glue/latest/dg/add-crawler.html). Exclusions []*string `type:"list"` @@ -31295,6 +31302,12 @@ func (s S3Target) GoString() string { return s.String() } +// SetConnectionName sets the ConnectionName field's value. +func (s *S3Target) SetConnectionName(v string) *S3Target { + s.ConnectionName = &v + return s +} + // SetExclusions sets the Exclusions field's value. func (s *S3Target) SetExclusions(v []*string) *S3Target { s.Exclusions = v @@ -37449,6 +37462,9 @@ const ( // ConnectionTypeKafka is a ConnectionType enum value ConnectionTypeKafka = "KAFKA" + + // ConnectionTypeNetwork is a ConnectionType enum value + ConnectionTypeNetwork = "NETWORK" ) const (
service/organizations/api.go+15 −0 modified@@ -14842,6 +14842,9 @@ type CreateAccountStatus struct { // * ACCOUNT_LIMIT_EXCEEDED: The account could not be created because you // have reached the limit on the number of accounts in your organization. // + // * CONCURRENT_ACCOUNT_MODIFICATION: You already submitted a request with + // the same information. + // // * EMAIL_ALREADY_EXISTS: The account could not be created because another // AWS account with that email address already exists. // @@ -14857,6 +14860,12 @@ type CreateAccountStatus struct { // // * INTERNAL_FAILURE: The account could not be created because of an internal // failure. Try again later. If the problem persists, contact Customer Support. + // + // * MISSING_BUSINESS_VALIDATION: The AWS account that owns your organization + // has not received Business Validation. + // + // * MISSING_PAYMENT_INSTRUMENT: You must configure the master account with + // a valid payment method, such as a credit card. FailureReason *string `type:"string" enum:"CreateAccountFailureReason"` // If the account was created successfully, the unique identifier (ID) of the @@ -22127,6 +22136,12 @@ const ( // CreateAccountFailureReasonGovcloudAccountAlreadyExists is a CreateAccountFailureReason enum value CreateAccountFailureReasonGovcloudAccountAlreadyExists = "GOVCLOUD_ACCOUNT_ALREADY_EXISTS" + + // CreateAccountFailureReasonMissingBusinessValidation is a CreateAccountFailureReason enum value + CreateAccountFailureReasonMissingBusinessValidation = "MISSING_BUSINESS_VALIDATION" + + // CreateAccountFailureReasonMissingPaymentInstrument is a CreateAccountFailureReason enum value + CreateAccountFailureReasonMissingPaymentInstrument = "MISSING_PAYMENT_INSTRUMENT" ) const (
service/s3/api.go+358 −310 modified@@ -75,23 +75,23 @@ func (c *S3) AbortMultipartUploadRequest(input *AbortMultipartUploadInput) (req // times in order to completely free all storage consumed by all parts. // // To verify that all parts have been removed, so you don't get charged for -// the part storage, you should call the ListParts operation and ensure that -// the parts list is empty. +// the part storage, you should call the ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) +// operation and ensure that the parts list is empty. // // For information about permissions required to use the multipart upload API, // see Multipart Upload API and Permissions (https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html). // // The following operations are related to AbortMultipartUpload: // -// * CreateMultipartUpload +// * CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * CompleteMultipartUpload +// * CompleteMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) // -// * ListParts +// * ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) // -// * ListMultipartUploads +// * ListMultipartUploads (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -173,14 +173,15 @@ func (c *S3) CompleteMultipartUploadRequest(input *CompleteMultipartUploadInput) // Completes a multipart upload by assembling previously uploaded parts. // // You first initiate the multipart upload and then upload all parts using the -// UploadPart operation. After successfully uploading all relevant parts of -// an upload, you call this operation to complete the upload. Upon receiving -// this request, Amazon S3 concatenates all the parts in ascending order by -// part number to create a new object. In the Complete Multipart Upload request, -// you must provide the parts list. You must ensure that the parts list is complete. -// This operation concatenates the parts that you provide in the list. For each -// part in the list, you must provide the part number and the ETag value, returned -// after that part was uploaded. +// UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) +// operation. After successfully uploading all relevant parts of an upload, +// you call this operation to complete the upload. Upon receiving this request, +// Amazon S3 concatenates all the parts in ascending order by part number to +// create a new object. In the Complete Multipart Upload request, you must provide +// the parts list. You must ensure that the parts list is complete. This operation +// concatenates the parts that you provide in the list. For each part in the +// list, you must provide the part number and the ETag value, returned after +// that part was uploaded. // // Processing of a Complete Multipart Upload request could take several minutes // to complete. After Amazon S3 begins processing the request, it sends an HTTP @@ -200,7 +201,7 @@ func (c *S3) CompleteMultipartUploadRequest(input *CompleteMultipartUploadInput) // For information about permissions required to use the multipart upload API, // see Multipart Upload API and Permissions (https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html). // -// GetBucketLifecycle has the following special errors: +// CompleteMultipartUpload has the following special errors: // // * Error code: EntityTooSmall Description: Your proposed upload is smaller // than the minimum allowed object size. Each part must be at least 5 MB @@ -220,15 +221,15 @@ func (c *S3) CompleteMultipartUploadRequest(input *CompleteMultipartUploadInput) // // The following operations are related to CompleteMultipartUpload: // -// * CreateMultipartUpload +// * CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * AbortMultipartUpload +// * AbortMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) // -// * ListParts +// * ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) // -// * ListMultipartUploads +// * ListMultipartUploads (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -441,13 +442,13 @@ func (c *S3) CopyObjectRequest(input *CopyObjectInput) (req *request.Request, ou // // If the source object's storage class is GLACIER, you must restore a copy // of this object before you can use it as a source object for the copy operation. -// For more information, see . +// For more information, see RestoreObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html). // // The following operations are related to CopyObject: // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // // For more information, see Copying Objects (https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjectsExamples.html). // @@ -587,9 +588,9 @@ func (c *S3) CreateBucketRequest(input *CreateBucketInput) (req *request.Request // // The following operations are related to CreateBucket: // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * DeleteBucket +// * DeleteBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -679,8 +680,9 @@ func (c *S3) CreateMultipartUploadRequest(input *CreateMultipartUploadInput) (re // This operation initiates a multipart upload and returns an upload ID. This // upload ID is used to associate all of the parts in the specific multipart // upload. You specify this upload ID in each of your subsequent upload part -// requests (see UploadPart). You also include this upload ID in the final request -// to either complete or abort the multipart upload request. +// requests (see UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html)). +// You also include this upload ID in the final request to either complete or +// abort the multipart upload request. // // For more information about multipart uploads, see Multipart Upload Overview // (https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html). @@ -713,9 +715,10 @@ func (c *S3) CreateMultipartUploadRequest(input *CreateMultipartUploadInput) (re // and decrypts it when you access it. You can provide your own encryption key, // or use AWS Key Management Service (AWS KMS) customer master keys (CMKs) or // Amazon S3-managed encryption keys. If you choose to provide your own encryption -// key, the request headers you provide in UploadPart) and UploadPartCopy) requests -// must match the headers you used in the request to initiate the upload by -// using CreateMultipartUpload. +// key, the request headers you provide in UploadPart (AmazonS3/latest/API/API_UploadPart.html) +// and UploadPartCopy (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) +// requests must match the headers you used in the request to initiate the upload +// by using CreateMultipartUpload. // // To perform a multipart upload with encryption using an AWS KMS CMK, the requester // must have permission to the kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, @@ -759,7 +762,7 @@ func (c *S3) CreateMultipartUploadRequest(input *CreateMultipartUploadInput) (re // * Use encryption keys managed by Amazon S3 or customer master keys (CMKs) // stored in AWS Key Management Service (AWS KMS) – If you want AWS to // manage the keys used to encrypt data, specify the following headers in -// the request. x-amz-server-side-encryption x-amz-server-side-encryption-aws-kms-key-id +// the request. x-amz-server-side-encryption x-amz-server-side-encryption-aws-kms-key-id // x-amz-server-side-encryption-context If you specify x-amz-server-side-encryption:aws:kms, // but don't provide x-amz-server-side-encryption-aws-kms-key-id, Amazon // S3 uses the AWS managed CMK in AWS KMS to protect the data. All GET and @@ -770,11 +773,10 @@ func (c *S3) CreateMultipartUploadRequest(input *CreateMultipartUploadInput) (re // // * Use customer-provided encryption keys – If you want to manage your // own encryption keys, provide all the following headers in the request. -// x-amz-server-side-encryption-customer-algorithm x-amz-server-side-encryption-customer-key -// x-amz-server-side-encryption-customer-key-MD5 For more information -// about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see -// Protecting Data Using Server-Side Encryption with CMKs stored in AWS KMS -// (https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html). +// x-amz-server-side-encryption-customer-algorithm x-amz-server-side-encryption-customer-key +// x-amz-server-side-encryption-customer-key-MD5 For more information about +// server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting +// Data Using Server-Side Encryption with CMKs stored in AWS KMS (https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html). // // Access-Control-List (ACL)-Specific Request Headers // @@ -815,15 +817,15 @@ func (c *S3) CreateMultipartUploadRequest(input *CreateMultipartUploadInput) (re // // The following operations are related to CreateMultipartUpload: // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * CompleteMultipartUpload +// * CompleteMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) // -// * AbortMultipartUpload +// * AbortMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) // -// * ListParts +// * ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) // -// * ListMultipartUploads +// * ListMultipartUploads (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -903,9 +905,9 @@ func (c *S3) DeleteBucketRequest(input *DeleteBucketInput) (req *request.Request // // Related Resources // -// * +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * +// * DeleteObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -994,11 +996,11 @@ func (c *S3) DeleteBucketAnalyticsConfigurationRequest(input *DeleteBucketAnalyt // // The following operations are related to DeleteBucketAnalyticsConfiguration: // -// * +// * GetBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAnalyticsConfiguration.html) // -// * +// * ListBucketAnalyticsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketAnalyticsConfigurations.html) // -// * +// * PutBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAnalyticsConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1084,9 +1086,9 @@ func (c *S3) DeleteBucketCorsRequest(input *DeleteBucketCorsInput) (req *request // // Related Resources: // -// * +// * PutBucketCors (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html) // -// * RESTOPTIONSobject +// * RESTOPTIONSobject (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTOPTIONSobject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1175,9 +1177,9 @@ func (c *S3) DeleteBucketEncryptionRequest(input *DeleteBucketEncryptionInput) ( // // Related Resources // -// * PutBucketEncryption +// * PutBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html) // -// * GetBucketEncryption +// * GetBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1266,11 +1268,11 @@ func (c *S3) DeleteBucketInventoryConfigurationRequest(input *DeleteBucketInvent // // Operations related to DeleteBucketInventoryConfiguration include: // -// * GetBucketInventoryConfiguration +// * GetBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketInventoryConfiguration.html) // -// * PutBucketInventoryConfiguration +// * PutBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketInventoryConfiguration.html) // -// * ListBucketInventoryConfigurations +// * ListBucketInventoryConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketInventoryConfigurations.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1363,9 +1365,9 @@ func (c *S3) DeleteBucketLifecycleRequest(input *DeleteBucketLifecycleInput) (re // // Related actions include: // -// * PutBucketLifecycleConfiguration +// * PutBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html) // -// * GetBucketLifecycleConfiguration +// * GetBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1455,11 +1457,11 @@ func (c *S3) DeleteBucketMetricsConfigurationRequest(input *DeleteBucketMetricsC // // The following operations are related to DeleteBucketMetricsConfiguration: // -// * GetBucketMetricsConfiguration +// * GetBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketMetricsConfiguration.html) // -// * PutBucketMetricsConfiguration +// * PutBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketMetricsConfiguration.html) // -// * ListBucketMetricsConfigurations +// * ListBucketMetricsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketMetricsConfigurations.html) // // * Monitoring Metrics with Amazon CloudWatch (https://docs.aws.amazon.com/AmazonS3/latest/dev/cloudwatch-monitoring.html) // @@ -1556,9 +1558,9 @@ func (c *S3) DeleteBucketPolicyRequest(input *DeleteBucketPolicyInput) (req *req // // The following operations are related to DeleteBucketPolicy // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * DeleteObject +// * DeleteObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1649,9 +1651,9 @@ func (c *S3) DeleteBucketReplicationRequest(input *DeleteBucketReplicationInput) // // The following operations are related to DeleteBucketReplication: // -// * PutBucketReplication +// * PutBucketReplication (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketReplication.html) // -// * GetBucketReplication +// * GetBucketReplication (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketReplication.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1734,9 +1736,9 @@ func (c *S3) DeleteBucketTaggingRequest(input *DeleteBucketTaggingInput) (req *r // // The following operations are related to DeleteBucketTagging: // -// * GetBucketTagging +// * GetBucketTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketTagging.html) // -// * PutBucketTagging +// * PutBucketTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketTagging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1828,9 +1830,9 @@ func (c *S3) DeleteBucketWebsiteRequest(input *DeleteBucketWebsiteInput) (req *r // // The following operations are related to DeleteBucketWebsite: // -// * GetBucketWebsite +// * GetBucketWebsite (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketWebsite.html) // -// * PutBucketWebsite +// * PutBucketWebsite (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketWebsite.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -1922,14 +1924,15 @@ func (c *S3) DeleteObjectRequest(input *DeleteObjectInput) (req *request.Request // To see sample requests that use versioning, see Sample Request (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html#ExampleVersionObjectDelete). // // You can delete objects by explicitly calling the DELETE Object API or configure -// its lifecycle (PutBucketLifecycle) to enable Amazon S3 to remove them for -// you. If you want to block users or accounts from removing or deleting objects -// from your bucket, you must deny them the s3:DeleteObject, s3:DeleteObjectVersion, -// and s3:PutLifeCycleConfiguration actions. +// its lifecycle (PutBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycle.html)) +// to enable Amazon S3 to remove them for you. If you want to block users or +// accounts from removing or deleting objects from your bucket, you must deny +// them the s3:DeleteObject, s3:DeleteObjectVersion, and s3:PutLifeCycleConfiguration +// actions. // // The following operation is related to DeleteObject: // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2015,9 +2018,9 @@ func (c *S3) DeleteObjectTaggingRequest(input *DeleteObjectTaggingInput) (req *r // // The following operations are related to DeleteBucketMetricsConfiguration: // -// * PutObjectTagging +// * PutObjectTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectTagging.html) // -// * GetObjectTagging +// * GetObjectTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectTagging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2128,15 +2131,15 @@ func (c *S3) DeleteObjectsRequest(input *DeleteObjectsInput) (req *request.Reque // // The following operations are related to DeleteObjects: // -// * CreateMultipartUpload +// * CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * CompleteMultipartUpload +// * CompleteMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) // -// * ListParts +// * ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) // -// * AbortMultipartUpload +// * AbortMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2221,11 +2224,11 @@ func (c *S3) DeletePublicAccessBlockRequest(input *DeletePublicAccessBlockInput) // // * Using Amazon S3 Block Public Access (https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html) // -// * GetPublicAccessBlock +// * GetPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetPublicAccessBlock.html) // -// * PutPublicAccessBlock +// * PutPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutPublicAccessBlock.html) // -// * GetBucketPolicyStatus +// * GetBucketPolicyStatus (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicyStatus.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2312,7 +2315,8 @@ func (c *S3) GetBucketAccelerateConfigurationRequest(input *GetBucketAccelerateC // in the Amazon Simple Storage Service Developer Guide. // // You set the Transfer Acceleration state of an existing bucket to Enabled -// or Suspended by using the PutBucketAccelerateConfiguration operation. +// or Suspended by using the PutBucketAccelerateConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAccelerateConfiguration.html) +// operation. // // A GET accelerate request does not return a state value for a bucket that // has no transfer acceleration state. A bucket has no Transfer Acceleration @@ -2324,7 +2328,7 @@ func (c *S3) GetBucketAccelerateConfigurationRequest(input *GetBucketAccelerateC // // Related Resources // -// * PutBucketAccelerateConfiguration +// * PutBucketAccelerateConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAccelerateConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2406,7 +2410,7 @@ func (c *S3) GetBucketAclRequest(input *GetBucketAclInput) (req *request.Request // // Related Resources // -// * +// * ListObjects (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2496,11 +2500,11 @@ func (c *S3) GetBucketAnalyticsConfigurationRequest(input *GetBucketAnalyticsCon // // Related Resources // -// * +// * DeleteBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketAnalyticsConfiguration.html) // -// * +// * ListBucketAnalyticsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketAnalyticsConfigurations.html) // -// * +// * PutBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAnalyticsConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2585,9 +2589,9 @@ func (c *S3) GetBucketCorsRequest(input *GetBucketCorsInput) (req *request.Reque // // The following operations are related to GetBucketCors: // -// * PutBucketCors +// * PutBucketCors (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketCors.html) // -// * DeleteBucketCors +// * DeleteBucketCors (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2673,9 +2677,9 @@ func (c *S3) GetBucketEncryptionRequest(input *GetBucketEncryptionInput) (req *r // // The following operations are related to GetBucketEncryption: // -// * PutBucketEncryption +// * PutBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html) // -// * DeleteBucketEncryption +// * DeleteBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2763,11 +2767,11 @@ func (c *S3) GetBucketInventoryConfigurationRequest(input *GetBucketInventoryCon // // The following operations are related to GetBucketInventoryConfiguration: // -// * DeleteBucketInventoryConfiguration +// * DeleteBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketInventoryConfiguration.html) // -// * ListBucketInventoryConfigurations +// * ListBucketInventoryConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketInventoryConfigurations.html) // -// * PutBucketInventoryConfiguration +// * PutBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketInventoryConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2847,7 +2851,7 @@ func (c *S3) GetBucketLifecycleRequest(input *GetBucketLifecycleInput) (req *req // GetBucketLifecycle API operation for Amazon Simple Storage Service. // // -// For an updated version of this API, see GetBucketLifecycleConfiguration. +// For an updated version of this API, see GetBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html). // If you configured a bucket lifecycle using the filter element, you should // see the updated version of this topic. This topic is provided for backward // compatibility. @@ -2869,11 +2873,11 @@ func (c *S3) GetBucketLifecycleRequest(input *GetBucketLifecycleInput) (req *req // // The following operations are related to GetBucketLifecycle: // -// * GetBucketLifecycleConfiguration +// * GetBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) // -// * PutBucketLifecycle +// * PutBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycle.html) // -// * DeleteBucketLifecycle +// * DeleteBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -2958,7 +2962,7 @@ func (c *S3) GetBucketLifecycleConfigurationRequest(input *GetBucketLifecycleCon // the new filter element that you can use to specify a filter to select a subset // of objects to which the rule applies. If you are still using previous version // of the lifecycle configuration, it works. For the earlier API description, -// see GetBucketLifecycle. +// see GetBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycle.html). // // Returns the lifecycle configuration information set on the bucket. For information // about lifecycle configuration, see Object Lifecycle Management (https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lifecycle-mgmt.html). @@ -2977,11 +2981,11 @@ func (c *S3) GetBucketLifecycleConfigurationRequest(input *GetBucketLifecycleCon // // The following operations are related to GetBucketLifecycleConfiguration: // -// * GetBucketLifecycle +// * GetBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycle.html) // -// * PutBucketLifecycle +// * PutBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycle.html) // -// * DeleteBucketLifecycle +// * DeleteBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3057,15 +3061,15 @@ func (c *S3) GetBucketLocationRequest(input *GetBucketLocationInput) (req *reque // // Returns the Region the bucket resides in. You set the bucket's Region using // the LocationConstraint request parameter in a CreateBucket request. For more -// information, see CreateBucket. +// information, see CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html). // // To use this implementation of the operation, you must be the bucket owner. // // The following operations are related to GetBucketLocation: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3144,9 +3148,9 @@ func (c *S3) GetBucketLoggingRequest(input *GetBucketLoggingInput) (req *request // // The following operations are related to GetBucketLogging: // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * PutBucketLogging +// * PutBucketLogging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLogging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3234,11 +3238,11 @@ func (c *S3) GetBucketMetricsConfigurationRequest(input *GetBucketMetricsConfigu // // The following operations are related to GetBucketMetricsConfiguration: // -// * PutBucketMetricsConfiguration +// * PutBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketMetricsConfiguration.html) // -// * DeleteBucketMetricsConfiguration +// * DeleteBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketMetricsConfiguration.html) // -// * ListBucketMetricsConfigurations +// * ListBucketMetricsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketMetricsConfigurations.html) // // * Monitoring Metrics with Amazon CloudWatch (https://docs.aws.amazon.com/AmazonS3/latest/dev/cloudwatch-monitoring.html) // @@ -3319,7 +3323,7 @@ func (c *S3) GetBucketNotificationRequest(input *GetBucketNotificationConfigurat // GetBucketNotification API operation for Amazon Simple Storage Service. // -// No longer used, see GetBucketNotificationConfiguration. +// No longer used, see GetBucketNotificationConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketNotificationConfiguration.html). // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3413,7 +3417,7 @@ func (c *S3) GetBucketNotificationConfigurationRequest(input *GetBucketNotificat // // The following operation is related to GetBucketNotification: // -// * PutBucketNotification +// * PutBucketNotification (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketNotification.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3506,7 +3510,7 @@ func (c *S3) GetBucketPolicyRequest(input *GetBucketPolicyInput) (req *request.R // // The following operation is related to GetBucketPolicy: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3592,11 +3596,11 @@ func (c *S3) GetBucketPolicyStatusRequest(input *GetBucketPolicyStatusInput) (re // // * Using Amazon S3 Block Public Access (https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html) // -// * GetPublicAccessBlock +// * GetPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetPublicAccessBlock.html) // -// * PutPublicAccessBlock +// * PutPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutPublicAccessBlock.html) // -// * DeletePublicAccessBlock +// * DeletePublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeletePublicAccessBlock.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3687,13 +3691,14 @@ func (c *S3) GetBucketReplicationRequest(input *GetBucketReplicationInput) (req // also include the DeleteMarkerReplication and Priority elements. The response // also returns those elements. // -// For information about GetBucketReplication errors, see ReplicationErrorCodeList +// For information about GetBucketReplication errors, see List of replication-related +// error codes (https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ReplicationErrorCodeList) // // The following operations are related to GetBucketReplication: // -// * PutBucketReplication +// * PutBucketReplication (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketReplication.html) // -// * DeleteBucketReplication +// * DeleteBucketReplication (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketReplication.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3773,7 +3778,7 @@ func (c *S3) GetBucketRequestPaymentRequest(input *GetBucketRequestPaymentInput) // // The following operations are related to GetBucketRequestPayment: // -// * ListObjects +// * ListObjects (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3860,9 +3865,9 @@ func (c *S3) GetBucketTaggingRequest(input *GetBucketTaggingInput) (req *request // // The following operations are related to GetBucketTagging: // -// * PutBucketTagging +// * PutBucketTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketTagging.html) // -// * DeleteBucketTagging +// * DeleteBucketTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketTagging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -3946,11 +3951,11 @@ func (c *S3) GetBucketVersioningRequest(input *GetBucketVersioningInput) (req *r // // The following operations are related to GetBucketVersioning: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * DeleteObject +// * DeleteObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4036,9 +4041,9 @@ func (c *S3) GetBucketWebsiteRequest(input *GetBucketWebsiteInput) (req *request // // The following operations are related to DeleteBucketWebsite: // -// * DeleteBucketWebsite +// * DeleteBucketWebsite (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketWebsite.html) // -// * PutBucketWebsite +// * PutBucketWebsite (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketWebsite.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4132,13 +4137,14 @@ func (c *S3) GetObjectRequest(input *GetObjectInput) (req *request.Request, outp // // To distribute large files to many people, you can save bandwidth costs by // using BitTorrent. For more information, see Amazon S3 Torrent (https://docs.aws.amazon.com/AmazonS3/latest/dev/S3Torrent.html). -// For more information about returning the ACL of an object, see GetObjectAcl. +// For more information about returning the ACL of an object, see GetObjectAcl +// (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html). // // If the object you are retrieving is stored in the GLACIER or DEEP_ARCHIVE // storage classes, before you can retrieve the object you must first restore -// a copy using . Otherwise, this operation returns an InvalidObjectStateError -// error. For information about restoring archived objects, see Restoring Archived -// Objects (https://docs.aws.amazon.com/AmazonS3/latest/dev/restoring-objects.html). +// a copy using RestoreObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html). +// Otherwise, this operation returns an InvalidObjectStateError error. For information +// about restoring archived objects, see Restoring Archived Objects (https://docs.aws.amazon.com/AmazonS3/latest/dev/restoring-objects.html). // // Encryption request headers, like x-amz-server-side-encryption, should not // be sent for GET requests if your object uses server-side encryption with @@ -4150,18 +4156,19 @@ func (c *S3) GetObjectRequest(input *GetObjectInput) (req *request.Request, outp // encryption keys (SSE-C) when you store the object in Amazon S3, then when // you GET the object, you must use the following headers: // -// * x-amz-server-side-encryption-customer-algorithm +// * x-amz-server-side-encryption-customer-algorithm // -// * x-amz-server-side-encryption-customer-key +// * x-amz-server-side-encryption-customer-key // -// * x-amz-server-side-encryption-customer-key-MD5 +// * x-amz-server-side-encryption-customer-key-MD5 // // For more information about SSE-C, see Server-Side Encryption (Using Customer-Provided // Encryption Keys) (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html). // // Assuming you have permission to read object tags (permission for the s3:GetObjectVersionTagging // action), the response also returns the x-amz-tagging-count header that provides // the count of number of tags associated with the object. You can use GetObjectTagging +// (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectTagging.html) // to retrieve the tag set associated with an object. // // Permissions @@ -4186,7 +4193,7 @@ func (c *S3) GetObjectRequest(input *GetObjectInput) (req *request.Request, outp // as if the object was deleted and includes x-amz-delete-marker: true in the // response. // -// For more information about versioning, see PutBucketVersioning. +// For more information about versioning, see PutBucketVersioning (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketVersioning.html). // // Overriding Response Header Values // @@ -4233,9 +4240,9 @@ func (c *S3) GetObjectRequest(input *GetObjectInput) (req *request.Request, outp // // The following operations are related to GetObject: // -// * ListBuckets +// * ListBuckets (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html) // -// * GetObjectAcl +// * GetObjectAcl (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4324,11 +4331,11 @@ func (c *S3) GetObjectAclRequest(input *GetObjectAclInput) (req *request.Request // // The following operations are related to GetObjectAcl: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * DeleteObject +// * DeleteObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4652,7 +4659,7 @@ func (c *S3) GetObjectTaggingRequest(input *GetObjectTaggingInput) (req *request // // The following operation is related to GetObjectTagging: // -// * PutObjectTagging +// * PutObjectTagging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectTagging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4738,7 +4745,7 @@ func (c *S3) GetObjectTorrentRequest(input *GetObjectTorrentInput) (req *request // // The following operation is related to GetObjectTorrent: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -4831,11 +4838,11 @@ func (c *S3) GetPublicAccessBlockRequest(input *GetPublicAccessBlockInput) (req // // * Using Amazon S3 Block Public Access (https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-block-public-access.html) // -// * PutPublicAccessBlock +// * PutPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutPublicAccessBlock.html) // -// * GetPublicAccessBlock +// * GetPublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetPublicAccessBlock.html) // -// * DeletePublicAccessBlock +// * DeletePublicAccessBlock (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeletePublicAccessBlock.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5010,11 +5017,11 @@ func (c *S3) HeadObjectRequest(input *HeadObjectInput) (req *request.Request, ou // encryption keys (SSE-C) when you store the object in Amazon S3, then when // you retrieve the metadata from the object, you must use the following headers: // -// * x-amz-server-side-encryption-customer-algorithm +// * x-amz-server-side-encryption-customer-algorithm // -// * x-amz-server-side-encryption-customer-key +// * x-amz-server-side-encryption-customer-key // -// * x-amz-server-side-encryption-customer-key-MD5 +// * x-amz-server-side-encryption-customer-key-MD5 // // For more information about SSE-C, see Server-Side Encryption (Using Customer-Provided // Encryption Keys) (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html). @@ -5057,7 +5064,7 @@ func (c *S3) HeadObjectRequest(input *HeadObjectInput) (req *request.Request, ou // // The following operation is related to HeadObject: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // // See http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#RESTErrorResponses // for more information on returned errors. @@ -5156,11 +5163,11 @@ func (c *S3) ListBucketAnalyticsConfigurationsRequest(input *ListBucketAnalytics // // The following operations are related to ListBucketAnalyticsConfigurations: // -// * GetBucketAnalyticsConfiguration +// * GetBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAnalyticsConfiguration.html) // -// * DeleteBucketAnalyticsConfiguration +// * DeleteBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketAnalyticsConfiguration.html) // -// * PutBucketAnalyticsConfiguration +// * PutBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAnalyticsConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5256,11 +5263,11 @@ func (c *S3) ListBucketInventoryConfigurationsRequest(input *ListBucketInventory // // The following operations are related to ListBucketInventoryConfigurations: // -// * GetBucketInventoryConfiguration +// * GetBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketInventoryConfiguration.html) // -// * DeleteBucketInventoryConfiguration +// * DeleteBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketInventoryConfiguration.html) // -// * PutBucketInventoryConfiguration +// * PutBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketInventoryConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5357,11 +5364,11 @@ func (c *S3) ListBucketMetricsConfigurationsRequest(input *ListBucketMetricsConf // // The following operations are related to ListBucketMetricsConfigurations: // -// * PutBucketMetricsConfiguration +// * PutBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketMetricsConfiguration.html) // -// * GetBucketMetricsConfiguration +// * GetBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketMetricsConfiguration.html) // -// * DeleteBucketMetricsConfiguration +// * DeleteBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketMetricsConfiguration.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5540,15 +5547,15 @@ func (c *S3) ListMultipartUploadsRequest(input *ListMultipartUploadsInput) (req // // The following operations are related to ListMultipartUploads: // -// * CreateMultipartUpload +// * CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * CompleteMultipartUpload +// * CompleteMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) // -// * ListParts +// * ListParts (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html) // -// * AbortMultipartUpload +// * AbortMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5691,13 +5698,13 @@ func (c *S3) ListObjectVersionsRequest(input *ListObjectVersionsInput) (req *req // // The following operations are related to ListObjectVersions: // -// * ListObjectsV2 +// * ListObjectsV2 (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * DeleteObject +// * DeleteObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -5835,21 +5842,22 @@ func (c *S3) ListObjectsRequest(input *ListObjectsInput) (req *request.Request, // to design your application to parse the contents of the response and handle // it appropriately. // -// This API has been revised. We recommend that you use the newer version, ListObjectsV2, +// This API has been revised. We recommend that you use the newer version, ListObjectsV2 +// (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html), // when developing applications. For backward compatibility, Amazon S3 continues // to support ListObjects. // // The following operations are related to ListObjects: // -// * ListObjectsV2 +// * ListObjectsV2 (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * ListBuckets +// * ListBuckets (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6003,17 +6011,18 @@ func (c *S3) ListObjectsV2Request(input *ListObjectsV2Input) (req *request.Reque // // This section describes the latest revision of the API. We recommend that // you use this revised API for application development. For backward compatibility, -// Amazon S3 continues to support the prior version of this API, ListObjects. +// Amazon S3 continues to support the prior version of this API, ListObjects +// (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html). // -// To get a list of your buckets, see ListBuckets. +// To get a list of your buckets, see ListBuckets (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html). // // The following operations are related to ListObjectsV2: // -// * GetObject +// * GetObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6152,14 +6161,14 @@ func (c *S3) ListPartsRequest(input *ListPartsInput) (req *request.Request, outp // // Lists the parts that have been uploaded for a specific multipart upload. // This operation must include the upload ID, which you obtain by sending the -// initiate multipart upload request (see CreateMultipartUpload). This request -// returns a maximum of 1,000 uploaded parts. The default number of parts returned -// is 1,000 parts. You can restrict the number of parts returned by specifying -// the max-parts request parameter. If your multipart upload consists of more -// than 1,000 parts, the response returns an IsTruncated field with the value -// of true, and a NextPartNumberMarker element. In subsequent ListParts requests -// you can include the part-number-marker query string parameter and set its -// value to the NextPartNumberMarker field value from the previous response. +// initiate multipart upload request (see CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html)). +// This request returns a maximum of 1,000 uploaded parts. The default number +// of parts returned is 1,000 parts. You can restrict the number of parts returned +// by specifying the max-parts request parameter. If your multipart upload consists +// of more than 1,000 parts, the response returns an IsTruncated field with +// the value of true, and a NextPartNumberMarker element. In subsequent ListParts +// requests you can include the part-number-marker query string parameter and +// set its value to the NextPartNumberMarker field value from the previous response. // // For more information on multipart uploads, see Uploading Objects Using Multipart // Upload (https://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html). @@ -6169,15 +6178,15 @@ func (c *S3) ListPartsRequest(input *ListPartsInput) (req *request.Request, outp // // The following operations are related to ListParts: // -// * CreateMultipartUpload +// * CreateMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) // -// * UploadPart +// * UploadPart (https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) // -// * CompleteMultipartUpload +// * CompleteMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) // -// * AbortMultipartUpload +// * AbortMultipartUpload (https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) // -// * ListMultipartUploads +// * ListMultipartUploads (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6321,8 +6330,8 @@ func (c *S3) PutBucketAccelerateConfigurationRequest(input *PutBucketAccelerateC // // * Suspended – Disables accelerated data transfers to the bucket. // -// The GetBucketAccelerateConfiguration operation returns the transfer acceleration -// state of a bucket. +// The GetBucketAccelerateConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAccelerateConfiguration.html) +// operation returns the transfer acceleration state of a bucket. // // After setting the Transfer Acceleration state of a bucket to Enabled, it // might take up to thirty minutes before the data transfer rates to the bucket @@ -6336,9 +6345,9 @@ func (c *S3) PutBucketAccelerateConfigurationRequest(input *PutBucketAccelerateC // // The following operations are related to PutBucketAccelerateConfiguration: // -// * GetBucketAccelerateConfiguration +// * GetBucketAccelerateConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAccelerateConfiguration.html) // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6498,11 +6507,11 @@ func (c *S3) PutBucketAclRequest(input *PutBucketAclInput) (req *request.Request // // Related Resources // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * DeleteBucket +// * DeleteBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html) // -// * GetObjectAcl +// * GetObjectAcl (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAcl.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6617,11 +6626,11 @@ func (c *S3) PutBucketAnalyticsConfigurationRequest(input *PutBucketAnalyticsCon // // Related Resources // -// * +// * GetBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAnalyticsConfiguration.html) // -// * +// * DeleteBucketAnalyticsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketAnalyticsConfiguration.html) // -// * +// * ListBucketAnalyticsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketAnalyticsConfigurations.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6738,11 +6747,11 @@ func (c *S3) PutBucketCorsRequest(input *PutBucketCorsInput) (req *request.Reque // // Related Resources // -// * GetBucketCors +// * GetBucketCors (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketCors.html) // -// * DeleteBucketCors +// * DeleteBucketCors (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketCors.html) // -// * RESTOPTIONSobject +// * RESTOPTIONSobject (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTOPTIONSobject.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6841,9 +6850,9 @@ func (c *S3) PutBucketEncryptionRequest(input *PutBucketEncryptionInput) (req *r // // Related Resources // -// * GetBucketEncryption +// * GetBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html) // -// * DeleteBucketEncryption +// * DeleteBucketEncryption (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -6961,11 +6970,11 @@ func (c *S3) PutBucketInventoryConfigurationRequest(input *PutBucketInventoryCon // // Related Resources // -// * GetBucketInventoryConfiguration +// * GetBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketInventoryConfiguration.html) // -// * DeleteBucketInventoryConfiguration +// * DeleteBucketInventoryConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketInventoryConfiguration.html) // -// * ListBucketInventoryConfigurations +// * ListBucketInventoryConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketInventoryConfigurations.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -7050,7 +7059,7 @@ func (c *S3) PutBucketLifecycleRequest(input *PutBucketLifecycleInput) (req *req // PutBucketLifecycle API operation for Amazon Simple Storage Service. // // -// For an updated version of this API, see PutBucketLifecycleConfiguration. +// For an updated version of this API, see PutBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html). // This version has been deprecated. Existing lifecycle configurations will // work. For new lifecycle configurations, use the updated API. // @@ -7086,11 +7095,11 @@ func (c *S3) PutBucketLifecycleRequest(input *PutBucketLifecycleInput) (req *req // // Related Resources // -// * GetBucketLifecycle(Deprecated) +// * GetBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycle.html)(Deprecated) // -// * GetBucketLifecycleConfiguration +// * GetBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) // -// * +// * RestoreObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html) // // * By default, a resource owner—in this case, a bucket owner, which is // the AWS account that created the bucket—can perform any of the operations. @@ -7189,7 +7198,7 @@ func (c *S3) PutBucketLifecycleConfigurationRequest(input *PutBucketLifecycleCon // Accordingly, this section describes the latest API. The previous version // of the API supported filtering based only on an object key name prefix, which // is supported for backward compatibility. For the related API description, -// see PutBucketLifecycle. +// see PutBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycle.html). // // Rules // @@ -7240,9 +7249,9 @@ func (c *S3) PutBucketLifecycleConfigurationRequest(input *PutBucketLifecycleCon // // * Examples of Lifecycle Configuration (https://docs.aws.amazon.com/AmazonS3/latest/dev/lifecycle-configuration-examples.html) // -// * GetBucketLifecycleConfiguration +// * GetBucketLifecycleConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html) // -// * DeleteBucketLifecycle +// * DeleteBucketLifecycle (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -7355,18 +7364,19 @@ func (c *S3) PutBucketLoggingRequest(input *PutBucketLoggingInput) (req *request // For more information about server access logging, see Server Access Logging // (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerLogs.html). // -// For more information about creating a bucket, see CreateBucket. For more -// information about returning the logging status of a bucket, see GetBucketLogging. +// For more information about creating a bucket, see CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html). +// For more information about returning the logging status of a bucket, see +// GetBucketLogging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLogging.html). // // The following operations are related to PutBucketLogging: // -// * PutObject +// * PutObject (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) // -// * DeleteBucket +// * DeleteBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html) // -// * CreateBucket +// * CreateBucket (https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) // -// * GetBucketLogging +// * GetBucketLogging (https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLogging.html) // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -7458,11 +7468,11 @@ func (c *S3) PutBucketMetricsConfigurationRequest(input *PutBucketMetricsConfigu // // The following operations are related to PutBucketMetricsConfiguration: // -// * DeleteBucketMetricsConfiguration +// * DeleteBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketMetricsConfiguration.html) // -// * PutBucketMetricsConfiguration +// * PutBucketMetricsConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketMetricsConfiguration.html) // -// * ListBucketMetricsConfigurations +// * ListBucketMetricsConfigurations (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBucketMetricsConfigurations.html) // // GetBucketLifecycle has the following special error: // @@ -7552,7 +7562,8 @@ func (c *S3) PutBucketNotificationRequest(input *PutBucketNotificationInput) (re // PutBucketNotification API operation for Amazon Simple Storage Service. // -// No longer used, see the PutBucketNotificationConfiguration operation. +// No longer used, see the PutBucketNotificationConfiguration (https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketNotificationConfiguration.html) +// operation. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -7679,7 +7690,7 @@ func (c *S3) PutBucketNo ... [truncated]
service/s3/examples_test.go+92 −92 modified@@ -467,16 +467,16 @@ func ExampleS3_DeleteObject_shared01() { fmt.Println(result) } -// To remove tag set from an object version +// To remove tag set from an object // -// The following example removes tag set associated with the specified object version. -// The request specifies both the object key and object version. +// The following example removes tag set associated with the specified object. If the +// bucket is versioning enabled, the operation removes tag set from the latest object +// version. func ExampleS3_DeleteObjectTagging_shared00() { svc := s3.New(session.New()) input := &s3.DeleteObjectTaggingInput{ - Bucket: aws.String("examplebucket"), - Key: aws.String("HappyFace.jpg"), - VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"), + Bucket: aws.String("examplebucket"), + Key: aws.String("HappyFace.jpg"), } result, err := svc.DeleteObjectTagging(input) @@ -497,16 +497,16 @@ func ExampleS3_DeleteObjectTagging_shared00() { fmt.Println(result) } -// To remove tag set from an object +// To remove tag set from an object version // -// The following example removes tag set associated with the specified object. If the -// bucket is versioning enabled, the operation removes tag set from the latest object -// version. +// The following example removes tag set associated with the specified object version. +// The request specifies both the object key and object version. func ExampleS3_DeleteObjectTagging_shared01() { svc := s3.New(session.New()) input := &s3.DeleteObjectTaggingInput{ - Bucket: aws.String("examplebucket"), - Key: aws.String("HappyFace.jpg"), + Bucket: aws.String("examplebucket"), + Key: aws.String("HappyFace.jpg"), + VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"), } result, err := svc.DeleteObjectTagging(input) @@ -1197,17 +1197,13 @@ func ExampleS3_ListBuckets_shared00() { fmt.Println(result) } -// List next set of multipart uploads when previous result is truncated +// To list in-progress multipart uploads on a bucket // -// The following example specifies the upload-id-marker and key-marker from previous -// truncated response to retrieve next setup of multipart uploads. +// The following example lists in-progress multipart uploads on a specific bucket. func ExampleS3_ListMultipartUploads_shared00() { svc := s3.New(session.New()) input := &s3.ListMultipartUploadsInput{ - Bucket: aws.String("examplebucket"), - KeyMarker: aws.String("nextkeyfrompreviousresponse"), - MaxUploads: aws.Int64(2), - UploadIdMarker: aws.String("valuefrompreviousresponse"), + Bucket: aws.String("examplebucket"), } result, err := svc.ListMultipartUploads(input) @@ -1228,13 +1224,17 @@ func ExampleS3_ListMultipartUploads_shared00() { fmt.Println(result) } -// To list in-progress multipart uploads on a bucket +// List next set of multipart uploads when previous result is truncated // -// The following example lists in-progress multipart uploads on a specific bucket. +// The following example specifies the upload-id-marker and key-marker from previous +// truncated response to retrieve next setup of multipart uploads. func ExampleS3_ListMultipartUploads_shared01() { svc := s3.New(session.New()) input := &s3.ListMultipartUploadsInput{ - Bucket: aws.String("examplebucket"), + Bucket: aws.String("examplebucket"), + KeyMarker: aws.String("nextkeyfrompreviousresponse"), + MaxUploads: aws.Int64(2), + UploadIdMarker: aws.String("valuefrompreviousresponse"), } result, err := svc.ListMultipartUploads(input) @@ -1804,19 +1804,20 @@ func ExampleS3_PutBucketWebsite_shared00() { fmt.Println(result) } -// To upload an object and specify server-side encryption and object tags +// To upload object and specify user-defined metadata // -// The following example uploads and object. The request specifies the optional server-side -// encryption option. The request also specifies optional object tags. If the bucket -// is versioning enabled, S3 returns version ID in response. +// The following example creates an object. The request also specifies optional metadata. +// If the bucket is versioning enabled, S3 returns version ID in response. func ExampleS3_PutObject_shared00() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), - Bucket: aws.String("examplebucket"), - Key: aws.String("exampleobject"), - ServerSideEncryption: aws.String("AES256"), - Tagging: aws.String("key1=value1&key2=value2"), + Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), + Bucket: aws.String("examplebucket"), + Key: aws.String("exampleobject"), + Metadata: map[string]*string{ + "metadata1": aws.String("value1"), + "metadata2": aws.String("value2"), + }, } result, err := svc.PutObject(input) @@ -1837,16 +1838,18 @@ func ExampleS3_PutObject_shared00() { fmt.Println(result) } -// To create an object. +// To upload an object and specify canned ACL. // -// The following example creates an object. If the bucket is versioning enabled, S3 -// returns version ID in response. +// The following example uploads and object. The request specifies optional canned ACL +// (access control list) to all READ access to authenticated users. If the bucket is +// versioning enabled, S3 returns version ID in response. func ExampleS3_PutObject_shared01() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ + ACL: aws.String("authenticated-read"), Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), Bucket: aws.String("examplebucket"), - Key: aws.String("objectkey"), + Key: aws.String("exampleobject"), } result, err := svc.PutObject(input) @@ -1867,17 +1870,16 @@ func ExampleS3_PutObject_shared01() { fmt.Println(result) } -// To upload an object and specify optional tags +// To create an object. // -// The following example uploads an object. The request specifies optional object tags. -// The bucket is versioned, therefore S3 returns version ID of the newly created object. +// The following example creates an object. If the bucket is versioning enabled, S3 +// returns version ID in response. func ExampleS3_PutObject_shared02() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - Body: aws.ReadSeekCloser(strings.NewReader("c:\\HappyFace.jpg")), - Bucket: aws.String("examplebucket"), - Key: aws.String("HappyFace.jpg"), - Tagging: aws.String("key1=value1&key2=value2"), + Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), + Bucket: aws.String("examplebucket"), + Key: aws.String("objectkey"), } result, err := svc.PutObject(input) @@ -1898,18 +1900,17 @@ func ExampleS3_PutObject_shared02() { fmt.Println(result) } -// To upload an object (specify optional headers) +// To upload an object // -// The following example uploads an object. The request specifies optional request headers -// to directs S3 to use specific storage class and use server-side encryption. +// The following example uploads an object to a versioning-enabled bucket. The source +// file is specified using Windows file syntax. S3 returns VersionId of the newly created +// object. func ExampleS3_PutObject_shared03() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")), - Bucket: aws.String("examplebucket"), - Key: aws.String("HappyFace.jpg"), - ServerSideEncryption: aws.String("AES256"), - StorageClass: aws.String("STANDARD_IA"), + Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")), + Bucket: aws.String("examplebucket"), + Key: aws.String("HappyFace.jpg"), } result, err := svc.PutObject(input) @@ -1930,20 +1931,17 @@ func ExampleS3_PutObject_shared03() { fmt.Println(result) } -// To upload object and specify user-defined metadata +// To upload an object and specify optional tags // -// The following example creates an object. The request also specifies optional metadata. -// If the bucket is versioning enabled, S3 returns version ID in response. +// The following example uploads an object. The request specifies optional object tags. +// The bucket is versioned, therefore S3 returns version ID of the newly created object. func ExampleS3_PutObject_shared04() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), - Bucket: aws.String("examplebucket"), - Key: aws.String("exampleobject"), - Metadata: map[string]*string{ - "metadata1": aws.String("value1"), - "metadata2": aws.String("value2"), - }, + Body: aws.ReadSeekCloser(strings.NewReader("c:\\HappyFace.jpg")), + Bucket: aws.String("examplebucket"), + Key: aws.String("HappyFace.jpg"), + Tagging: aws.String("key1=value1&key2=value2"), } result, err := svc.PutObject(input) @@ -1964,18 +1962,19 @@ func ExampleS3_PutObject_shared04() { fmt.Println(result) } -// To upload an object and specify canned ACL. +// To upload an object and specify server-side encryption and object tags // -// The following example uploads and object. The request specifies optional canned ACL -// (access control list) to all READ access to authenticated users. If the bucket is -// versioning enabled, S3 returns version ID in response. +// The following example uploads and object. The request specifies the optional server-side +// encryption option. The request also specifies optional object tags. If the bucket +// is versioning enabled, S3 returns version ID in response. func ExampleS3_PutObject_shared05() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - ACL: aws.String("authenticated-read"), - Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), - Bucket: aws.String("examplebucket"), - Key: aws.String("exampleobject"), + Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")), + Bucket: aws.String("examplebucket"), + Key: aws.String("exampleobject"), + ServerSideEncryption: aws.String("AES256"), + Tagging: aws.String("key1=value1&key2=value2"), } result, err := svc.PutObject(input) @@ -1996,17 +1995,18 @@ func ExampleS3_PutObject_shared05() { fmt.Println(result) } -// To upload an object +// To upload an object (specify optional headers) // -// The following example uploads an object to a versioning-enabled bucket. The source -// file is specified using Windows file syntax. S3 returns VersionId of the newly created -// object. +// The following example uploads an object. The request specifies optional request headers +// to directs S3 to use specific storage class and use server-side encryption. func ExampleS3_PutObject_shared06() { svc := s3.New(session.New()) input := &s3.PutObjectInput{ - Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")), - Bucket: aws.String("examplebucket"), - Key: aws.String("HappyFace.jpg"), + Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")), + Bucket: aws.String("examplebucket"), + Key: aws.String("HappyFace.jpg"), + ServerSideEncryption: aws.String("AES256"), + StorageClass: aws.String("STANDARD_IA"), } result, err := svc.PutObject(input) @@ -2171,18 +2171,19 @@ func ExampleS3_UploadPart_shared00() { fmt.Println(result) } -// To upload a part by copying data from an existing object as data source +// To upload a part by copying byte range from an existing object as data source // -// The following example uploads a part of a multipart upload by copying data from an -// existing object as data source. +// The following example uploads a part of a multipart upload by copying a specified +// byte range from an existing object as data source. func ExampleS3_UploadPartCopy_shared00() { svc := s3.New(session.New()) input := &s3.UploadPartCopyInput{ - Bucket: aws.String("examplebucket"), - CopySource: aws.String("/bucketname/sourceobjectkey"), - Key: aws.String("examplelargeobject"), - PartNumber: aws.Int64(1), - UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"), + Bucket: aws.String("examplebucket"), + CopySource: aws.String("/bucketname/sourceobjectkey"), + CopySourceRange: aws.String("bytes=1-100000"), + Key: aws.String("examplelargeobject"), + PartNumber: aws.Int64(2), + UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"), } result, err := svc.UploadPartCopy(input) @@ -2203,19 +2204,18 @@ func ExampleS3_UploadPartCopy_shared00() { fmt.Println(result) } -// To upload a part by copying byte range from an existing object as data source +// To upload a part by copying data from an existing object as data source // -// The following example uploads a part of a multipart upload by copying a specified -// byte range from an existing object as data source. +// The following example uploads a part of a multipart upload by copying data from an +// existing object as data source. func ExampleS3_UploadPartCopy_shared01() { svc := s3.New(session.New()) input := &s3.UploadPartCopyInput{ - Bucket: aws.String("examplebucket"), - CopySource: aws.String("/bucketname/sourceobjectkey"), - CopySourceRange: aws.String("bytes=1-100000"), - Key: aws.String("examplelargeobject"), - PartNumber: aws.Int64(2), - UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"), + Bucket: aws.String("examplebucket"), + CopySource: aws.String("/bucketname/sourceobjectkey"), + Key: aws.String("examplelargeobject"), + PartNumber: aws.Int64(1), + UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"), } result, err := svc.UploadPartCopy(input)
service/s3/s3manager/upload_input.go+1 −1 modified@@ -111,7 +111,7 @@ type UploadInput struct { // Specifies the customer-provided encryption key for Amazon S3 to use in encrypting // data. This value is used to store the object and then it is discarded; Amazon // S3 does not store the encryption key. The key must be appropriate for use - // with the algorithm specified in the x-amz-server-side-encryption-customer-algorithm + // with the algorithm specified in the x-amz-server-side-encryption-customer-algorithm // header. SSECustomerKey *string `marshal-as:"blob" location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string" sensitive:"true"`
service/sms/api.go+2200 −247 modifiedservice/sms/doc.go+5 −13 modified@@ -3,21 +3,13 @@ // Package sms provides the client and types for making API // requests to AWS Server Migration Service. // -// This is the AWS Sever Migration Service API Reference. It provides descriptions, -// syntax, and usage examples for each of the actions and data types for the -// AWS Sever Migration Service (AWS SMS). The topic for each action shows the -// Query API request parameters and the XML response. You can also view the -// XML request elements in the WSDL. +// AWS Server Migration Service (AWS SMS) makes it easier and faster for you +// to migrate your on-premises workloads to AWS. To learn more about AWS SMS, +// see the following resources: // -// Alternatively, you can use one of the AWS SDKs to access an API that's tailored -// to the programming language or platform that you're using. For more information, -// see AWS SDKs (http://aws.amazon.com/tools/#SDKs). +// * AWS Server Migration Service product page (http://aws.amazon.com/server-migration-service/) // -// To learn more about the Server Migration Service, see the following resources: -// -// * AWS Sever Migration Service product page (https://aws.amazon.com/server-migration-service/) -// -// * AWS Sever Migration Service User Guide (https://docs.aws.amazon.com/server-migration-service/latest/userguide/server-migration.html) +// * AWS Server Migration Service User Guide (https://docs.aws.amazon.com/server-migration-service/latest/userguide/) // // See https://docs.aws.amazon.com/goto/WebAPI/sms-2016-10-24 for more information on this service. //
service/sms/errors.go+8 −0 modified@@ -8,6 +8,13 @@ import ( const ( + // ErrCodeDryRunOperationException for service response error code + // "DryRunOperationException". + // + // The user has the required permissions, so the request would have succeeded, + // but a dry run was performed. + ErrCodeDryRunOperationException = "DryRunOperationException" + // ErrCodeInternalError for service response error code // "InternalError". // @@ -78,6 +85,7 @@ const ( ) var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{ + "DryRunOperationException": newErrorDryRunOperationException, "InternalError": newErrorInternalError, "InvalidParameterException": newErrorInvalidParameterException, "MissingRequiredParameterException": newErrorMissingRequiredParameterException,
service/sms/smsiface/interface.go+28 −0 modified@@ -80,6 +80,10 @@ type SMSAPI interface { DeleteAppReplicationConfigurationWithContext(aws.Context, *sms.DeleteAppReplicationConfigurationInput, ...request.Option) (*sms.DeleteAppReplicationConfigurationOutput, error) DeleteAppReplicationConfigurationRequest(*sms.DeleteAppReplicationConfigurationInput) (*request.Request, *sms.DeleteAppReplicationConfigurationOutput) + DeleteAppValidationConfiguration(*sms.DeleteAppValidationConfigurationInput) (*sms.DeleteAppValidationConfigurationOutput, error) + DeleteAppValidationConfigurationWithContext(aws.Context, *sms.DeleteAppValidationConfigurationInput, ...request.Option) (*sms.DeleteAppValidationConfigurationOutput, error) + DeleteAppValidationConfigurationRequest(*sms.DeleteAppValidationConfigurationInput) (*request.Request, *sms.DeleteAppValidationConfigurationOutput) + DeleteReplicationJob(*sms.DeleteReplicationJobInput) (*sms.DeleteReplicationJobOutput, error) DeleteReplicationJobWithContext(aws.Context, *sms.DeleteReplicationJobInput, ...request.Option) (*sms.DeleteReplicationJobOutput, error) DeleteReplicationJobRequest(*sms.DeleteReplicationJobInput) (*request.Request, *sms.DeleteReplicationJobOutput) @@ -112,6 +116,14 @@ type SMSAPI interface { GetAppReplicationConfigurationWithContext(aws.Context, *sms.GetAppReplicationConfigurationInput, ...request.Option) (*sms.GetAppReplicationConfigurationOutput, error) GetAppReplicationConfigurationRequest(*sms.GetAppReplicationConfigurationInput) (*request.Request, *sms.GetAppReplicationConfigurationOutput) + GetAppValidationConfiguration(*sms.GetAppValidationConfigurationInput) (*sms.GetAppValidationConfigurationOutput, error) + GetAppValidationConfigurationWithContext(aws.Context, *sms.GetAppValidationConfigurationInput, ...request.Option) (*sms.GetAppValidationConfigurationOutput, error) + GetAppValidationConfigurationRequest(*sms.GetAppValidationConfigurationInput) (*request.Request, *sms.GetAppValidationConfigurationOutput) + + GetAppValidationOutput(*sms.GetAppValidationOutputInput) (*sms.GetAppValidationOutputOutput, error) + GetAppValidationOutputWithContext(aws.Context, *sms.GetAppValidationOutputInput, ...request.Option) (*sms.GetAppValidationOutputOutput, error) + GetAppValidationOutputRequest(*sms.GetAppValidationOutputInput) (*request.Request, *sms.GetAppValidationOutputOutput) + GetConnectors(*sms.GetConnectorsInput) (*sms.GetConnectorsOutput, error) GetConnectorsWithContext(aws.Context, *sms.GetConnectorsInput, ...request.Option) (*sms.GetConnectorsOutput, error) GetConnectorsRequest(*sms.GetConnectorsInput) (*request.Request, *sms.GetConnectorsOutput) @@ -140,6 +152,10 @@ type SMSAPI interface { GetServersPages(*sms.GetServersInput, func(*sms.GetServersOutput, bool) bool) error GetServersPagesWithContext(aws.Context, *sms.GetServersInput, func(*sms.GetServersOutput, bool) bool, ...request.Option) error + ImportAppCatalog(*sms.ImportAppCatalogInput) (*sms.ImportAppCatalogOutput, error) + ImportAppCatalogWithContext(aws.Context, *sms.ImportAppCatalogInput, ...request.Option) (*sms.ImportAppCatalogOutput, error) + ImportAppCatalogRequest(*sms.ImportAppCatalogInput) (*request.Request, *sms.ImportAppCatalogOutput) + ImportServerCatalog(*sms.ImportServerCatalogInput) (*sms.ImportServerCatalogOutput, error) ImportServerCatalogWithContext(aws.Context, *sms.ImportServerCatalogInput, ...request.Option) (*sms.ImportServerCatalogOutput, error) ImportServerCatalogRequest(*sms.ImportServerCatalogInput) (*request.Request, *sms.ImportServerCatalogOutput) @@ -152,6 +168,10 @@ type SMSAPI interface { ListAppsWithContext(aws.Context, *sms.ListAppsInput, ...request.Option) (*sms.ListAppsOutput, error) ListAppsRequest(*sms.ListAppsInput) (*request.Request, *sms.ListAppsOutput) + NotifyAppValidationOutput(*sms.NotifyAppValidationOutputInput) (*sms.NotifyAppValidationOutputOutput, error) + NotifyAppValidationOutputWithContext(aws.Context, *sms.NotifyAppValidationOutputInput, ...request.Option) (*sms.NotifyAppValidationOutputOutput, error) + NotifyAppValidationOutputRequest(*sms.NotifyAppValidationOutputInput) (*request.Request, *sms.NotifyAppValidationOutputOutput) + PutAppLaunchConfiguration(*sms.PutAppLaunchConfigurationInput) (*sms.PutAppLaunchConfigurationOutput, error) PutAppLaunchConfigurationWithContext(aws.Context, *sms.PutAppLaunchConfigurationInput, ...request.Option) (*sms.PutAppLaunchConfigurationOutput, error) PutAppLaunchConfigurationRequest(*sms.PutAppLaunchConfigurationInput) (*request.Request, *sms.PutAppLaunchConfigurationOutput) @@ -160,10 +180,18 @@ type SMSAPI interface { PutAppReplicationConfigurationWithContext(aws.Context, *sms.PutAppReplicationConfigurationInput, ...request.Option) (*sms.PutAppReplicationConfigurationOutput, error) PutAppReplicationConfigurationRequest(*sms.PutAppReplicationConfigurationInput) (*request.Request, *sms.PutAppReplicationConfigurationOutput) + PutAppValidationConfiguration(*sms.PutAppValidationConfigurationInput) (*sms.PutAppValidationConfigurationOutput, error) + PutAppValidationConfigurationWithContext(aws.Context, *sms.PutAppValidationConfigurationInput, ...request.Option) (*sms.PutAppValidationConfigurationOutput, error) + PutAppValidationConfigurationRequest(*sms.PutAppValidationConfigurationInput) (*request.Request, *sms.PutAppValidationConfigurationOutput) + StartAppReplication(*sms.StartAppReplicationInput) (*sms.StartAppReplicationOutput, error) StartAppReplicationWithContext(aws.Context, *sms.StartAppReplicationInput, ...request.Option) (*sms.StartAppReplicationOutput, error) StartAppReplicationRequest(*sms.StartAppReplicationInput) (*request.Request, *sms.StartAppReplicationOutput) + StartOnDemandAppReplication(*sms.StartOnDemandAppReplicationInput) (*sms.StartOnDemandAppReplicationOutput, error) + StartOnDemandAppReplicationWithContext(aws.Context, *sms.StartOnDemandAppReplicationInput, ...request.Option) (*sms.StartOnDemandAppReplicationOutput, error) + StartOnDemandAppReplicationRequest(*sms.StartOnDemandAppReplicationInput) (*request.Request, *sms.StartOnDemandAppReplicationOutput) + StartOnDemandReplicationRun(*sms.StartOnDemandReplicationRunInput) (*sms.StartOnDemandReplicationRunOutput, error) StartOnDemandReplicationRunWithContext(aws.Context, *sms.StartOnDemandReplicationRunInput, ...request.Option) (*sms.StartOnDemandReplicationRunOutput, error) StartOnDemandReplicationRunRequest(*sms.StartOnDemandReplicationRunInput) (*request.Request, *sms.StartOnDemandReplicationRunOutput)
1e84382fa1c0Merge commit '12ff57a16373dda5a0c22eafdf0fa1c4c224f7c4' into release
41 files changed · +2483 −537
aws/csm/reporter_test.go+1 −1 modified@@ -183,7 +183,7 @@ func TestReportingMetrics(t *testing.T) { { "Type": "ApiCallAttempt", "SdkException": request.ErrCodeRequestError, - "SdkExceptionMessage": request.ErrCodeRequestError+": sdk error", + "SdkExceptionMessage": request.ErrCodeRequestError + ": sdk error", "HttpStatusCode": float64(500), }, {
aws/ec2metadata/service.go+1 −1 modified@@ -41,7 +41,7 @@ const ( enableTokenProviderHandlerName = "enableTokenProviderHandler" // TTL constants - defaultTTL = 21600 * time.Second + defaultTTL = 21600 * time.Second ttlExpirationWindow = 30 * time.Second )
CHANGELOG_PENDING.md+1 −0 modified@@ -1,4 +1,5 @@ ### SDK Features +* `service/s3/s3crypto`: Updates to the Amazon S3 Encryption Client - This change includes fixes for issues that were reported by Sophie Schmieg from the Google ISE team, and for issues that were discovered by AWS Cryptography. ### SDK Enhancements
service/s3/doc_custom.go+0 −13 modified@@ -104,19 +104,6 @@ // content from S3. The Encryption and Decryption clients can be used concurrently // once the client is created. // -// sess := session.Must(session.NewSession()) -// -// // Create the decryption client. -// svc := s3crypto.NewDecryptionClient(sess) -// -// // The object will be downloaded from S3 and decrypted locally. By metadata -// // about the object's encryption will instruct the decryption client how -// // decrypt the content of the object. By default KMS is used for keys. -// result, err := svc.GetObject(&s3.GetObjectInput { -// Bucket: aws.String(myBucket), -// Key: aws.String(myKey), -// }) -// // See the s3crypto package documentation for more information. // https://docs.aws.amazon.com/sdk-for-go/api/service/s3/s3crypto/ //
service/s3/s3crypto/aes_cbc_content_cipher.go+48 −10 modified@@ -2,7 +2,6 @@ package s3crypto import ( "io" - "strings" ) const ( @@ -15,18 +14,38 @@ type cbcContentCipherBuilder struct { padder Padder } -func (cbcContentCipherBuilder) isUsingDeprecatedFeatures() error { - return errDeprecatedCipherBuilder -} - -// AESCBCContentCipherBuilder returns a new encryption only mode structure with a specific cipher -// for the master key +// AESCBCContentCipherBuilder returns a new encryption only AES/CBC mode structure using the provided padder. The provided cipher data generator +// will be used to provide keys for content encryption. // -// deprecated: This content cipher builder has been deprecated. Users should migrate to AESGCMContentCipherBuilder +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func AESCBCContentCipherBuilder(generator CipherDataGenerator, padder Padder) ContentCipherBuilder { return cbcContentCipherBuilder{generator: generator, padder: padder} } +// RegisterAESCBCContentCipher registers the AES/CBC cipher and padder with the provided CryptoRegistry. +// +// Example: +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterAESCBCContentCipher(cr, s3crypto.AESCBCPadder); err != nil { +// panic(err) // handle error +// } +// +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. +func RegisterAESCBCContentCipher(registry *CryptoRegistry, padder Padder) error { + if registry == nil { + return errNilCryptoRegistry + } + name := AESCBC + "/" + padder.Name() + err := registry.AddCEK(name, newAESCBCContentCipher) + if err != nil { + return err + } + if err := registry.AddPadder(name, padder); err != nil { + return err + } + return nil +} + func (builder cbcContentCipherBuilder) ContentCipher() (ContentCipher, error) { cd, err := builder.generator.GenerateCipherData(cbcKeySize, cbcNonceSize) if err != nil { @@ -37,11 +56,22 @@ func (builder cbcContentCipherBuilder) ContentCipher() (ContentCipher, error) { return newAESCBCContentCipher(cd) } +func (builder cbcContentCipherBuilder) isAWSFixture() bool { + return true +} + +func (cbcContentCipherBuilder) isEncryptionVersionCompatible(version clientVersion) error { + if version != v1ClientVersion { + return errDeprecatedIncompatibleCipherBuilder + } + return nil +} + // newAESCBCContentCipher will create a new aes cbc content cipher. If the cipher data's -// will set the CEK algorithm if it hasn't been set. +// will set the cek algorithm if it hasn't been set. func newAESCBCContentCipher(cd CipherData) (ContentCipher, error) { if len(cd.CEKAlgorithm) == 0 { - cd.CEKAlgorithm = strings.Join([]string{AESCBC, cd.Padder.Name()}, "/") + cd.CEKAlgorithm = AESCBC + "/" + cd.Padder.Name() } cipher, err := newAESCBC(cd, cd.Padder) if err != nil { @@ -77,3 +107,11 @@ func (cc *aesCBCContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser func (cc aesCBCContentCipher) GetCipherData() CipherData { return cc.CipherData } + +var ( + _ ContentCipherBuilder = (*cbcContentCipherBuilder)(nil) + _ compatibleEncryptionFixture = (*cbcContentCipherBuilder)(nil) + _ awsFixture = (*cbcContentCipherBuilder)(nil) + + _ ContentCipher = (*aesCBCContentCipher)(nil) +)
service/s3/s3crypto/aes_cbc_content_cipher_test.go+64 −4 modified@@ -1,14 +1,13 @@ -package s3crypto_test +package s3crypto import ( + "strings" "testing" - - "github.com/aws/aws-sdk-go/service/s3/s3crypto" ) func TestAESCBCBuilder(t *testing.T) { generator := mockGenerator{} - builder := s3crypto.AESCBCContentCipherBuilder(generator, s3crypto.NoPadder) + builder := AESCBCContentCipherBuilder(generator, NoPadder) if builder == nil { t.Fatal(builder) } @@ -18,3 +17,64 @@ func TestAESCBCBuilder(t *testing.T) { t.Fatal(err) } } + +func TestAesCBCContentCipher_isFixtureEncryptionCompatible(t *testing.T) { + generator := mockGenerator{} + builder := AESCBCContentCipherBuilder(generator, NoPadder) + if builder == nil { + t.Fatal("expected builder to not be nil") + } + + compatibility, ok := builder.(compatibleEncryptionFixture) + if !ok { + t.Fatal("expected builder to implement compatibleEncryptionFixture interface") + } + + if err := compatibility.isEncryptionVersionCompatible(v1ClientVersion); err != nil { + t.Errorf("expected builder to be compatible with v1 client") + } + + if err := compatibility.isEncryptionVersionCompatible(v2ClientVersion); err == nil { + t.Errorf("expected builder to not be compatible with v2 client") + } +} + +func TestRegisterAESCBCContentCipher(t *testing.T) { + cr := NewCryptoRegistry() + padder := AESCBCPadder + err := RegisterAESCBCContentCipher(cr, padder) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if v, ok := cr.GetCEK("AES/CBC/PKCS5Padding"); !ok { + t.Fatal("expected cek algorithm handler to registered") + } else if v == nil { + t.Fatal("expected non-nil cek handler to be registered") + } + + if v, ok := cr.GetPadder("AES/CBC/PKCS5Padding"); !ok { + t.Fatal("expected padder to be registered") + } else if v != padder { + t.Fatal("padder did not match provided value") + } + + // try to register padder again + err = RegisterAESCBCContentCipher(cr, padder) + if err == nil { + t.Fatal("expected error, got none") + } else if !strings.Contains(err.Error(), "duplicate cek registry entry") { + t.Errorf("expected duplicate cek entry, got %v", err) + } + + // try to regster padder with cek removed but padder entry still present + if _, ok := cr.RemoveCEK("AES/CBC/PKCS5Padding"); !ok { + t.Fatalf("expected value to be removed") + } + err = RegisterAESCBCContentCipher(cr, padder) + if err == nil { + t.Fatal("expected error, got none") + } else if !strings.Contains(err.Error(), "duplicate padder registry entry") { + t.Errorf("expected duplicate padder entry, got %v", err) + } +}
service/s3/s3crypto/aes_gcm_content_cipher.go+119 −13 modified@@ -1,6 +1,7 @@ package s3crypto import ( + "fmt" "io" "github.com/aws/aws-sdk-go/aws" @@ -11,21 +12,62 @@ const ( gcmNonceSize = 12 ) -type gcmContentCipherBuilder struct { - generator CipherDataGenerator +// AESGCMContentCipherBuilder returns a new encryption only AES/GCM mode structure with a specific cipher data generator +// that will provide keys to be used for content encryption. +// +// Note: This uses the Go stdlib AEAD implementation for AES/GCM. Due to this objects to be encrypted or decrypted +// will be fully loaded into memory before encryption or decryption can occur. Caution must be taken to avoid memory +// allocation failures. +// +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. +func AESGCMContentCipherBuilder(generator CipherDataGenerator) ContentCipherBuilder { + return gcmContentCipherBuilder{generator} +} + +// AESGCMContentCipherBuilderV2 returns a new encryption only AES/GCM mode structure with a specific cipher data generator +// that will provide keys to be used for content encryption. This type is compatible with the V2 encryption client. +// +// Note: This uses the Go stdlib AEAD implementation for AES/GCM. Due to this objects to be encrypted or decrypted +// will be fully loaded into memory before encryption or decryption can occur. Caution must be taken to avoid memory +// allocation failures. +func AESGCMContentCipherBuilderV2(generator CipherDataGeneratorWithCEKAlg) ContentCipherBuilder { + return gcmContentCipherBuilderV2{generator} } -func (builder gcmContentCipherBuilder) isUsingDeprecatedFeatures() error { - if feature, ok := builder.generator.(deprecatedFeatures); ok { - return feature.isUsingDeprecatedFeatures() +// RegisterAESGCMContentCipher registers the AES/GCM content cipher algorithm with the provided CryptoRegistry. +// +// Example: +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { +// panic(err) // handle error +// } +// +func RegisterAESGCMContentCipher(registry *CryptoRegistry) error { + if registry == nil { + return errNilCryptoRegistry + } + + err := registry.AddCEK(AESGCMNoPadding, newAESGCMContentCipher) + if err != nil { + return err + } + + // NoPadder is generic but required by this algorithm, so if it is already registered and is the expected implementation + // don't error. + padderName := NoPadder.Name() + if v, ok := registry.GetPadder(padderName); !ok { + if err := registry.AddPadder(padderName, NoPadder); err != nil { + return err + } + } else if _, ok := v.(noPadder); !ok { + return fmt.Errorf("%s is already registred but does not match expected type %T", padderName, NoPadder) } return nil } -// AESGCMContentCipherBuilder returns a new encryption only mode structure with a specific cipher -// for the master key -func AESGCMContentCipherBuilder(generator CipherDataGenerator) ContentCipherBuilder { - return gcmContentCipherBuilder{generator} +// gcmContentCipherBuilder is a AES/GCM content cipher to be used with the V1 client CipherDataGenerator interface +type gcmContentCipherBuilder struct { + generator CipherDataGenerator } func (builder gcmContentCipherBuilder) ContentCipher() (ContentCipher, error) { @@ -37,10 +79,6 @@ func (builder gcmContentCipherBuilder) ContentCipherWithContext(ctx aws.Context) var err error switch v := builder.generator.(type) { - case CipherDataGeneratorWithCEKAlgWithContext: - cd, err = v.GenerateCipherDataWithCEKAlgWithContext(ctx, gcmKeySize, gcmNonceSize, AESGCMNoPadding) - case CipherDataGeneratorWithCEKAlg: - cd, err = v.GenerateCipherDataWithCEKAlg(gcmKeySize, gcmNonceSize, AESGCMNoPadding) case CipherDataGeneratorWithContext: cd, err = v.GenerateCipherDataWithContext(ctx, gcmKeySize, gcmNonceSize) default: @@ -53,6 +91,52 @@ func (builder gcmContentCipherBuilder) ContentCipherWithContext(ctx aws.Context) return newAESGCMContentCipher(cd) } +// isFixtureEncryptionCompatible will ensure that this type may only be used with the V1 client +func (builder gcmContentCipherBuilder) isEncryptionVersionCompatible(version clientVersion) error { + if version != v1ClientVersion { + return errDeprecatedIncompatibleCipherBuilder + } + return nil +} + +func (builder gcmContentCipherBuilder) isAWSFixture() bool { + return true +} + +// gcmContentCipherBuilderV2 return a new builder for encryption content using AES/GCM/NoPadding. This type is meant +// to be used with key wrapping implementations that allow the cek algorithm to be provided when calling the +// cipher data generator. +type gcmContentCipherBuilderV2 struct { + generator CipherDataGeneratorWithCEKAlg +} + +func (builder gcmContentCipherBuilderV2) ContentCipher() (ContentCipher, error) { + return builder.ContentCipherWithContext(aws.BackgroundContext()) +} + +func (builder gcmContentCipherBuilderV2) ContentCipherWithContext(ctx aws.Context) (ContentCipher, error) { + cd, err := builder.generator.GenerateCipherDataWithCEKAlg(ctx, gcmKeySize, gcmNonceSize, AESGCMNoPadding) + if err != nil { + return nil, err + } + + return newAESGCMContentCipher(cd) +} + +// isFixtureEncryptionCompatible will ensure that this type may only be used with the V2 client +func (builder gcmContentCipherBuilderV2) isEncryptionVersionCompatible(version clientVersion) error { + if version != v2ClientVersion { + return errDeprecatedIncompatibleCipherBuilder + } + return nil +} + +// isAWSFixture will return whether this type was constructed with an AWS provided CipherDataGenerator +func (builder gcmContentCipherBuilderV2) isAWSFixture() bool { + v, ok := builder.generator.(awsFixture) + return ok && v.isAWSFixture() +} + func newAESGCMContentCipher(cd CipherData) (ContentCipher, error) { cd.CEKAlgorithm = AESGCMNoPadding cd.TagLength = "128" @@ -91,3 +175,25 @@ func (cc *aesGCMContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser func (cc aesGCMContentCipher) GetCipherData() CipherData { return cc.CipherData } + +// assert ContentCipherBuilder implementations +var ( + _ ContentCipherBuilder = (*gcmContentCipherBuilder)(nil) + _ ContentCipherBuilder = (*gcmContentCipherBuilderV2)(nil) +) + +// assert ContentCipherBuilderWithContext implementations +var ( + _ ContentCipherBuilderWithContext = (*gcmContentCipherBuilder)(nil) + _ ContentCipherBuilderWithContext = (*gcmContentCipherBuilderV2)(nil) +) + +// assert ContentCipher implementations +var ( + _ ContentCipher = (*aesGCMContentCipher)(nil) +) + +// assert awsFixture implementations +var ( + _ awsFixture = (*gcmContentCipherBuilderV2)(nil) +)
service/s3/s3crypto/aes_gcm_content_cipher_test.go+113 −7 modified@@ -1,22 +1,23 @@ -package s3crypto_test +package s3crypto import ( + "strings" "testing" - "github.com/aws/aws-sdk-go/service/kms/kmsiface" - "github.com/aws/aws-sdk-go/service/s3/s3crypto" + "github.com/aws/aws-sdk-go/awstesting/unit" + "github.com/aws/aws-sdk-go/service/kms" ) func TestAESGCMContentCipherBuilder(t *testing.T) { generator := mockGenerator{} - if builder := s3crypto.AESGCMContentCipherBuilder(generator); builder == nil { + if builder := AESGCMContentCipherBuilder(generator); builder == nil { t.Error("expected non-nil value") } } func TestAESGCMContentCipherNewEncryptor(t *testing.T) { generator := mockGenerator{} - builder := s3crypto.AESGCMContentCipherBuilder(generator) + builder := AESGCMContentCipherBuilder(generator) cipher, err := builder.ContentCipher() if err != nil { @@ -28,6 +29,111 @@ func TestAESGCMContentCipherNewEncryptor(t *testing.T) { } } -type mockKMS struct { - kmsiface.KMSAPI +func TestAESGCMContentCipherBuilderV2(t *testing.T) { + builder := AESGCMContentCipherBuilderV2(mockGeneratorV2{}) + cipher, err := builder.ContentCipher() + + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + if cipher == nil { + t.Errorf("expected non-nil vaue") + } +} + +func TestGcmContentCipherBuilder_isFixtureEncryptionCompatible(t *testing.T) { + builder := AESGCMContentCipherBuilder(NewKMSKeyGenerator(mockKMS{}, "cmkID")) + features, ok := builder.(compatibleEncryptionFixture) + if !ok { + t.Errorf("expected to implement compatibleEncryptionFixture interface") + } + + if err := features.isEncryptionVersionCompatible(v1ClientVersion); err != nil { + t.Errorf("expected to recieve no error, got %v", err) + } + + if err := features.isEncryptionVersionCompatible(v2ClientVersion); err == nil { + t.Errorf("expected to recieve error, got nil") + } +} + +func TestGcmContentCipherBuilderV2_isFixtureEncryptionCompatible(t *testing.T) { + builder := AESGCMContentCipherBuilderV2(NewKMSContextKeyGenerator(mockKMS{}, "cmkID", nil)) + features, ok := builder.(compatibleEncryptionFixture) + if !ok { + t.Errorf("expected to implement compatibleEncryptionFixture interface") + } + + if err := features.isEncryptionVersionCompatible(v1ClientVersion); err == nil { + t.Error("expected to receive error, got nil") + } + + if err := features.isEncryptionVersionCompatible(v2ClientVersion); err != nil { + t.Errorf("expected to recieve no error, got %v", err) + } +} + +func TestRegisterAESGCMContentCipher(t *testing.T) { + cr := NewCryptoRegistry() + err := RegisterAESGCMContentCipher(cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if v, ok := cr.GetCEK("AES/GCM/NoPadding"); !ok { + t.Fatal("expected cek handler to be registered") + } else if v == nil { + t.Fatal("expected non-nil cek handler") + } + + if v, ok := cr.GetPadder("NoPadding"); !ok { + t.Fatal("expected padder to be registered") + } else if v != NoPadder { + t.Fatal("padder did not match expected type") + } + + err = RegisterAESGCMContentCipher(cr) + if err == nil { + t.Fatal("expected error, got none") + } else if !strings.Contains(err.Error(), "duplicate cek registry entry") { + t.Errorf("expected duplicate entry, got %v", err) + } + + if _, ok := cr.RemoveCEK("AES/GCM/NoPadding"); !ok { + t.Error("expected value to be removed") + } + err = RegisterAESGCMContentCipher(cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if _, ok := cr.RemoveCEK("AES/GCM/NoPadding"); !ok { + t.Fatalf("expected value to be removed") + } + if _, ok := cr.RemovePadder("NoPadding"); !ok { + t.Fatalf("expected value to be removed") + } + if err := cr.AddPadder("NoPadding", mockPadder{}); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + err = RegisterAESGCMContentCipher(cr) + if err == nil { + t.Fatalf("expected error, got %v", err) + } else if !strings.Contains(err.Error(), "does not match expected type") { + t.Errorf("expected padder type error, got %v", err) + } +} + +func TestAESGCMContentCipherBuilderV2_isAWSFixture(t *testing.T) { + builder := AESGCMContentCipherBuilderV2(NewKMSContextKeyGenerator(kms.New(unit.Session.Copy()), "cmk", nil)) + if !builder.(awsFixture).isAWSFixture() { + t.Error("expected to be AWS ContentCipherBuilder constructed with a AWS CipherDataGenerator") + } + + builder = AESGCMContentCipherBuilderV2(mockGeneratorV2{}) + if builder.(awsFixture).isAWSFixture() { + t.Error("expected to return that this is not an AWS fixture") + } }
service/s3/s3crypto/cipher_builder.go+7 −0 modified@@ -39,3 +39,10 @@ type CipherData struct { Padder Padder } + +// Clone returns a new copy of CipherData +func (cd CipherData) Clone() (v CipherData) { + v = cd + v.MaterialDescription = cd.MaterialDescription.Clone() + return v +}
service/s3/s3crypto/cipher_util.go+2 −5 modified@@ -6,20 +6,18 @@ import ( ) // AESGCMNoPadding is the constant value that is used to specify -// the CEK algorithm consiting of AES GCM with no padding. +// the cek algorithm consiting of AES GCM with no padding. const AESGCMNoPadding = "AES/GCM/NoPadding" // AESCBC is the string constant that signifies the AES CBC algorithm cipher. const AESCBC = "AES/CBC" -func encodeMeta(reader hashReader, cd CipherData) (Envelope, error) { +func encodeMeta(reader lengthReader, cd CipherData) (Envelope, error) { iv := base64.StdEncoding.EncodeToString(cd.IV) key := base64.StdEncoding.EncodeToString(cd.EncryptedKey) - md5 := reader.GetValue() contentLength := reader.GetContentLength() - md5Str := base64.StdEncoding.EncodeToString(md5) matdesc, err := cd.MaterialDescription.encodeDescription() if err != nil { return Envelope{}, err @@ -32,7 +30,6 @@ func encodeMeta(reader hashReader, cd CipherData) (Envelope, error) { WrapAlg: cd.WrapAlgorithm, CEKAlg: cd.CEKAlgorithm, TagLen: cd.TagLength, - UnencryptedMD5: md5Str, UnencryptedContentLen: strconv.FormatInt(contentLength, 10), }, nil }
service/s3/s3crypto/cipher_util_test.go+33 −38 modified@@ -15,14 +15,13 @@ import ( func TestWrapFactory(t *testing.T) { o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ + CryptoRegistry: initCryptoRegistryFrom(map[string]WrapEntry{ KMSWrap: (kmsKeyHandler{ kms: kms.New(unit.Session), }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ + }, map[string]CEKEntry{ AESGCMNoPadding: newAESGCMContentCipher, - }, + }, map[string]Padder{}), } env := Envelope{ WrapAlg: KMSWrap, @@ -43,14 +42,13 @@ func TestWrapFactory(t *testing.T) { } func TestWrapFactoryErrorNoWrap(t *testing.T) { o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ + CryptoRegistry: initCryptoRegistryFrom(map[string]WrapEntry{ KMSWrap: (kmsKeyHandler{ kms: kms.New(unit.Session), }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ + }, map[string]CEKEntry{ AESGCMNoPadding: newAESGCMContentCipher, - }, + }, map[string]Padder{}), } env := Envelope{ WrapAlg: "none", @@ -68,14 +66,13 @@ func TestWrapFactoryErrorNoWrap(t *testing.T) { func TestWrapFactoryCustomEntry(t *testing.T) { o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ + CryptoRegistry: initCryptoRegistryFrom(map[string]WrapEntry{ "custom": (kmsKeyHandler{ kms: kms.New(unit.Session), }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ + }, map[string]CEKEntry{ AESGCMNoPadding: newAESGCMContentCipher, - }, + }, map[string]Padder{}), } env := Envelope{ WrapAlg: "custom", @@ -108,17 +105,15 @@ func TestCEKFactory(t *testing.T) { }) o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ + CryptoRegistry: initCryptoRegistryFrom(map[string]WrapEntry{ KMSWrap: (kmsKeyHandler{ kms: kms.New(sess), }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ + }, map[string]CEKEntry{ AESGCMNoPadding: newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{ + }, map[string]Padder{ NoPadder.Name(): NoPadder, - }, + }), } iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") if err != nil { @@ -167,17 +162,18 @@ func TestCEKFactoryNoCEK(t *testing.T) { }) o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(sess), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{ - NoPadder.Name(): NoPadder, - }, + CryptoRegistry: initCryptoRegistryFrom( + map[string]WrapEntry{ + KMSWrap: (kmsKeyHandler{ + kms: kms.New(sess), + }).decryptHandler, + }, + map[string]CEKEntry{ + AESGCMNoPadding: newAESGCMContentCipher, + }, + map[string]Padder{ + NoPadder.Name(): NoPadder, + }), } iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") if err != nil { @@ -226,15 +222,14 @@ func TestCEKFactoryCustomEntry(t *testing.T) { }) o := DecryptionClientOptions{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(sess), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - "custom": newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{}, + CryptoRegistry: initCryptoRegistryFrom( + map[string]WrapEntry{ + KMSWrap: (kmsKeyHandler{ + kms: kms.New(sess), + }).decryptHandler, + }, map[string]CEKEntry{ + "custom": newAESGCMContentCipher, + }, map[string]Padder{}), } iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") if err != nil {
service/s3/s3crypto/crypto_registry.go+158 −0 added@@ -0,0 +1,158 @@ +package s3crypto + +import ( + "fmt" +) + +// CryptoRegistry is a collection of registries for configuring a decryption client with different key wrapping algorithms, +// content encryption algorithms, and padders. +type CryptoRegistry struct { + wrap map[string]WrapEntry + cek map[string]CEKEntry + padder map[string]Padder +} + +// NewCryptoRegistry creates a new CryptoRegistry to which wrapping algorithms, content encryption ciphers, and +// padders can be registered for use with the DecryptionClientV2. +func NewCryptoRegistry() *CryptoRegistry { + return &CryptoRegistry{ + wrap: map[string]WrapEntry{}, + cek: map[string]CEKEntry{}, + padder: map[string]Padder{}, + } +} + +// initCryptoRegistryFrom creates a CryptoRegistry from prepopulated values, this is used for the V1 client +func initCryptoRegistryFrom(wrapRegistry map[string]WrapEntry, cekRegistry map[string]CEKEntry, padderRegistry map[string]Padder) *CryptoRegistry { + cr := &CryptoRegistry{ + wrap: wrapRegistry, + cek: cekRegistry, + padder: padderRegistry, + } + return cr +} + +// GetWrap returns the WrapEntry identified by the given name. Returns false if the entry is not registered. +func (c CryptoRegistry) GetWrap(name string) (WrapEntry, bool) { + if c.wrap == nil { + return nil, false + } + entry, ok := c.wrap[name] + return entry, ok +} + +// AddWrap registers the provided WrapEntry under the given name, returns an error if a WrapEntry is already present +// for the given name. +// +// This method should only be used if you need to register custom wrapping algorithms. Please see the following methods +// for helpers to register AWS provided algorithms: +// RegisterKMSContextWrapWithAnyCMK (kms+context) +// RegisterKMSContextWrapWithCMK (kms+context) +// RegisterKMSWrapWithAnyCMK (kms) +// RegisterKMSWrapWithCMK (kms) +func (c *CryptoRegistry) AddWrap(name string, entry WrapEntry) error { + if entry == nil { + return errNilWrapEntry + } + + if _, ok := c.wrap[name]; ok { + return newErrDuplicateWrapEntry(name) + } + c.wrap[name] = entry + return nil +} + +// RemoveWrap removes the WrapEntry identified by name. If the WrapEntry is not present returns false. +func (c *CryptoRegistry) RemoveWrap(name string) (WrapEntry, bool) { + if c.wrap == nil { + return nil, false + } + entry, ok := c.wrap[name] + if ok { + delete(c.wrap, name) + } + return entry, ok +} + +// GetCEK returns the CEKEntry identified by the given name. Returns false if the entry is not registered. +func (c CryptoRegistry) GetCEK(name string) (CEKEntry, bool) { + if c.cek == nil { + return nil, false + } + entry, ok := c.cek[name] + return entry, ok +} + +// AddCEK registers CEKEntry under the given name, returns an error if a CEKEntry is already present for the given name. +// +// This method should only be used if you need to register custom content encryption algorithms. Please see the following methods +// for helpers to register AWS provided algorithms: +// RegisterAESGCMContentCipher (AES/GCM) +// RegisterAESCBCContentCipher (AES/CBC) +func (c *CryptoRegistry) AddCEK(name string, entry CEKEntry) error { + if entry == nil { + return errNilCEKEntry + } + if _, ok := c.cek[name]; ok { + return newErrDuplicateCEKEntry(name) + } + c.cek[name] = entry + return nil +} + +// RemoveCEK removes the CEKEntry identified by name. If the entry is not present returns false. +func (c *CryptoRegistry) RemoveCEK(name string) (CEKEntry, bool) { + if c.cek == nil { + return nil, false + } + entry, ok := c.cek[name] + if ok { + delete(c.cek, name) + } + return entry, ok +} + +// GetPadder returns the Padder identified by name. If the Padder is not present, returns false. +func (c *CryptoRegistry) GetPadder(name string) (Padder, bool) { + if c.padder == nil { + return nil, false + } + entry, ok := c.padder[name] + return entry, ok +} + +// AddPadder registers Padder under the given name, returns an error if a Padder is already present for the given name. +// +// This method should only be used to register custom padder implementations not provided by AWS. +func (c *CryptoRegistry) AddPadder(name string, padder Padder) error { + if padder == nil { + return errNilPadder + } + if _, ok := c.padder[name]; ok { + return newErrDuplicatePadderEntry(name) + } + c.padder[name] = padder + return nil +} + +// RemovePadder removes the Padder identified by name. If the entry is not present returns false. +func (c *CryptoRegistry) RemovePadder(name string) (Padder, bool) { + if c.padder == nil { + return nil, false + } + padder, ok := c.padder[name] + if ok { + delete(c.padder, name) + } + return padder, ok +} + +func (c CryptoRegistry) valid() error { + if len(c.wrap) == 0 { + return fmt.Errorf("at least one key wrapping algorithms must be provided") + } + if len(c.cek) == 0 { + return fmt.Errorf("at least one content decryption algorithms must be provided") + } + return nil +}
service/s3/s3crypto/crypto_registry_test.go+150 −0 added@@ -0,0 +1,150 @@ +package s3crypto + +import ( + "strings" + "testing" +) + +func TestCryptoRegistry_Wrap(t *testing.T) { + cr := NewCryptoRegistry() + + mockWrap := WrapEntry(func(envelope Envelope) (CipherDataDecrypter, error) { + return nil, nil + }) + + if _, ok := cr.GetWrap("foo"); ok { + t.Errorf("expected wrapper to not be present") + } + + if _, ok := cr.RemoveWrap("foo"); ok { + t.Errorf("expected wrapped to not have been removed") + } + + if err := cr.AddWrap("foo", nil); err == nil { + t.Errorf("expected error, got none") + } + + if err := cr.AddWrap("foo", mockWrap); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if err := cr.AddWrap("foo", mockWrap); err == nil { + t.Error("expected error, got none") + } + + if v, ok := cr.GetWrap("foo"); !ok || v == nil { + t.Error("expected wrapper to be present and not nil") + } + + if v, ok := cr.RemoveWrap("foo"); !ok || v == nil { + t.Error("expected wrapper to have been removed and not nil") + } + + if _, ok := cr.GetWrap("foo"); ok { + t.Error("expected wrapper to have been removed and not nil") + } +} + +func TestCryptoRegistry_CEK(t *testing.T) { + cr := NewCryptoRegistry() + + mockEntry := CEKEntry(func(data CipherData) (ContentCipher, error) { + return nil, nil + }) + + if _, ok := cr.GetCEK("foo"); ok { + t.Errorf("expected wrapper to not be present") + } + + if _, ok := cr.RemoveCEK("foo"); ok { + t.Errorf("expected wrapped to not have been removed") + } + + if err := cr.AddCEK("foo", nil); err == nil { + t.Errorf("expected error, got none") + } + + if err := cr.AddCEK("foo", mockEntry); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if err := cr.AddCEK("foo", mockEntry); err == nil { + t.Error("expected error, got none") + } + + if v, ok := cr.GetCEK("foo"); !ok || v == nil { + t.Error("expected wrapper to be present and not nil") + } + + if v, ok := cr.RemoveCEK("foo"); !ok || v == nil { + t.Error("expected wrapper to have been removed and not nil") + } + + if _, ok := cr.GetCEK("foo"); ok { + t.Error("expected wrapper to have been removed and not nil") + } +} + +func TestCryptoRegistry_Padder(t *testing.T) { + cr := NewCryptoRegistry() + + padder := &mockPadder{} + + if _, ok := cr.GetPadder("foo"); ok { + t.Errorf("expected wrapper to not be present") + } + + if _, ok := cr.RemovePadder("foo"); ok { + t.Errorf("expected wrapped to not have been removed") + } + + if err := cr.AddPadder("foo", nil); err == nil { + t.Errorf("expected error, got none") + } + + if err := cr.AddPadder("foo", padder); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if err := cr.AddPadder("foo", padder); err == nil { + t.Error("expected error, got none") + } + + if v, ok := cr.GetPadder("foo"); !ok || v == nil { + t.Error("expected wrapper to be present and not nil") + } + + if v, ok := cr.RemovePadder("foo"); !ok || v == nil { + t.Error("expected wrapper to have been removed and not nil") + } +} + +func TestCryptoRegistry_valid(t *testing.T) { + cr := NewCryptoRegistry() + + if err := cr.valid(); err == nil { + t.Errorf("expected error, got none") + } else if e, a := "at least one key wrapping algorithms must be provided", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + + if err := cr.AddWrap("foo", func(envelope Envelope) (CipherDataDecrypter, error) { + return nil, nil + }); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := cr.valid(); err == nil { + t.Fatalf("expected error, got none") + } else if e, a := "least one content decryption algorithms must be provided", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + + if err := cr.AddCEK("foo", func(data CipherData) (ContentCipher, error) { + return nil, nil + }); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := cr.valid(); err != nil { + t.Fatalf("expected no error, got %v", err) + } +}
service/s3/s3crypto/decryption_client.go+16 −20 modified@@ -1,8 +1,6 @@ package s3crypto import ( - "strings" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/request" @@ -14,7 +12,7 @@ import ( // WrapEntry is builder that return a proper key decrypter and error type WrapEntry func(Envelope) (CipherDataDecrypter, error) -// CEKEntry is a builder thatn returns a proper content decrypter and error +// CEKEntry is a builder that returns a proper content decrypter and error type CEKEntry func(CipherData) (ContentCipher, error) // DecryptionClient is an S3 crypto client. The decryption client @@ -26,7 +24,7 @@ type CEKEntry func(CipherData) (ContentCipher, error) // * AES/GCM // * AES/CBC // -// deprecated: See DecryptionClientV2 +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. type DecryptionClient struct { S3Client s3iface.S3API // LoadStrategy is used to load the metadata either from the metadata of the object @@ -43,36 +41,36 @@ type DecryptionClient struct { // NewDecryptionClient instantiates a new S3 crypto client // // Example: -// sess := session.New() +// sess := session.Must(session.NewSession()) // svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient{ // // Custom client options here // })) // -// deprecated: see NewDecryptionClientV2 +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func NewDecryptionClient(prov client.ConfigProvider, options ...func(*DecryptionClient)) *DecryptionClient { s3client := s3.New(prov) s3client.Handlers.Build.PushBack(func(r *request.Request) { - request.AddToUserAgent(r, "S3Crypto") + request.AddToUserAgent(r, "S3CryptoV1n") }) + kmsClient := kms.New(prov) client := &DecryptionClient{ S3Client: s3client, LoadStrategy: defaultV2LoadStrategy{ client: s3client, }, WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(prov), - }).decryptHandler, + KMSWrap: NewKMSWrapEntry(kmsClient), + KMSContextWrap: newKMSContextWrapEntryWithAnyCMK(kmsClient), }, CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): newAESCBCContentCipher, + AESGCMNoPadding: newAESGCMContentCipher, + AESCBC + "/" + AESCBCPadder.Name(): newAESCBCContentCipher, }, PadderRegistry: map[string]Padder{ - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): AESCBCPadder, - "NoPadding": NoPadder, + AESCBC + "/" + AESCBCPadder.Name(): AESCBCPadder, + NoPadder.Name(): NoPadder, }, } for _, option := range options { @@ -94,14 +92,14 @@ func NewDecryptionClient(prov client.ConfigProvider, options ...func(*Decryption // }) // err := req.Send() // -// deprecated: see DecryptionClientV2.GetObjectRequest +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *DecryptionClient) GetObjectRequest(input *s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) { return getObjectRequest(c.getClientOptions(), input) } // GetObject is a wrapper for GetObjectRequest // -// deprecated: see DecryptionClientV2.GetObject +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *DecryptionClient) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { return getObject(c.getClientOptions(), input) } @@ -114,7 +112,7 @@ func (c *DecryptionClient) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOut // cause a panic. Use the Context to add deadlining, timeouts, etc. In the future // this may create sub-contexts for individual underlying requests. // -// deprecated: see DecryptionClientV2.GetObjectWithContext +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *DecryptionClient) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) { return getObjectWithContext(c.getClientOptions(), ctx, input, opts...) } @@ -123,8 +121,6 @@ func (c *DecryptionClient) getClientOptions() DecryptionClientOptions { return DecryptionClientOptions{ S3Client: c.S3Client, LoadStrategy: c.LoadStrategy, - WrapRegistry: c.WrapRegistry, - CEKRegistry: c.CEKRegistry, - PadderRegistry: c.PadderRegistry, + CryptoRegistry: initCryptoRegistryFrom(c.WrapRegistry, c.CEKRegistry, c.PadderRegistry), } }
service/s3/s3crypto/decryption_client_test.go+53 −6 modified@@ -70,8 +70,6 @@ func TestGetObjectGCM(t *testing.T) { }, Body: ioutil.NopCloser(bytes.NewBuffer(b)), } - out.Metadata = make(map[string]*string) - out.Metadata["x-amz-wrap-alg"] = aws.String(s3crypto.KMSWrap) }) err := req.Send() if err != nil { @@ -139,8 +137,6 @@ func TestGetObjectCBC(t *testing.T) { }, Body: ioutil.NopCloser(bytes.NewBuffer(b)), } - out.Metadata = make(map[string]*string) - out.Metadata["x-amz-wrap-alg"] = aws.String(s3crypto.KMSWrap) }) err := req.Send() if err != nil { @@ -204,8 +200,6 @@ func TestGetObjectCBC2(t *testing.T) { }, Body: ioutil.NopCloser(bytes.NewBuffer(b)), } - out.Metadata = make(map[string]*string) - out.Metadata["x-amz-wrap-alg"] = aws.String(s3crypto.KMSWrap) }) err := req.Send() if err != nil { @@ -248,3 +242,56 @@ func TestGetObjectWithContext(t *testing.T) { t.Errorf("expected error message to contain %q, but did not %q", e, a) } } + +func TestDecryptionClient_GetObject_V2Artifact(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "hJUv7S6K2cHF64boS9ixHX0TZAjBZLT4ZpEO4XxkGnY=", `"}`)) + })) + defer ts.Close() + + c := s3crypto.NewDecryptionClient(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) + + input := &s3.GetObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + } + + req, out := c.GetObjectRequest(input) + req.Handlers.Send.Clear() + req.Handlers.Send.PushBack(func(r *request.Request) { + b, err := hex.DecodeString("6b134eb7a353131de92faff64f594b2794e3544e31776cca26fe3bbeeffc68742d1007234f11c6670522602326868e29f37e9d2678f1614ec1a2418009b9772100929aadbed9a21a") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: http.Header{ + http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"PsuclPnlo2O0MQoov6kL1TBlaZG6oyNwWuAqmAgq7g8b9ZeeORi3VTMg624FU9jx"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"dqqlq2dRVSQ5hFRb"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"aws:x-amz-cek-alg": "AES/GCM/NoPadding"}`}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSContextWrap}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(b)), + } + }) + err := req.Send() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + actual, err := ioutil.ReadAll(out.Body) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected, err := hex.DecodeString("af150d7156bf5b3f5c461e5c6ac820acc5a33aab7085d920666c250ff251209d5a4029b3bd78250fab6e11aed52fae948d407056a9519b68") + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if bytes.Compare(expected, actual) != 0 { + t.Fatalf("expected content to match but it did not") + } +}
service/s3/s3crypto/decryption_client_v2.go+27 −27 modified@@ -1,12 +1,9 @@ package s3crypto import ( - "strings" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" ) @@ -33,58 +30,61 @@ type DecryptionClientOptions struct { // Defaults to our default load strategy. LoadStrategy LoadStrategy - WrapRegistry map[string]WrapEntry - CEKRegistry map[string]CEKEntry - PadderRegistry map[string]Padder + CryptoRegistry *CryptoRegistry } -// NewDecryptionClientV2 instantiates a new V2 S3 crypto client. The returned DecryptionClientV2 will be able to decrypt -// object encrypted by both the V1 and V2 clients. +// NewDecryptionClientV2 instantiates a new DecryptionClientV2. The NewDecryptionClientV2 must be configured with the +// desired key wrapping and content encryption algorithms that are required to be read by the client. These algorithms +// are registered by providing the client a CryptoRegistry that has been constructed with the desired configuration. +// NewDecryptionClientV2 will return an error if no key wrapping or content encryption algorithms have been provided. // // Example: // sess := session.Must(session.NewSession()) -// svc := s3crypto.NewDecryptionClientV2(sess, func(svc *s3crypto.DecryptionClientOptions{ +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(sess)); err != nil { +// panic(err) // handle error +// } +// if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { +// panic(err) // handle error +// } +// svc, err := s3crypto.NewDecryptionClientV2(sess, cr, func(o *s3crypto.DecryptionClientOptions) { // // Custom client options here -// })) -func NewDecryptionClientV2(prov client.ConfigProvider, options ...func(clientOptions *DecryptionClientOptions)) *DecryptionClientV2 { +// }) +// if err != nil { +// panic(err) // handle error +// } +func NewDecryptionClientV2( + prov client.ConfigProvider, cryptoRegistry *CryptoRegistry, + options ...func(clientOptions *DecryptionClientOptions), +) (*DecryptionClientV2, error) { s3client := s3.New(prov) s3client.Handlers.Build.PushBack(func(r *request.Request) { request.AddToUserAgent(r, "S3CryptoV2") }) - kmsClient := kms.New(prov) clientOptions := &DecryptionClientOptions{ S3Client: s3client, LoadStrategy: defaultV2LoadStrategy{ client: s3client, }, - WrapRegistry: map[string]WrapEntry{ - KMSWrap: NewKMSWrapEntry(kmsClient), - KMSContextWrap: NewKMSContextWrapEntry(kmsClient), - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): newAESCBCContentCipher, - }, - PadderRegistry: map[string]Padder{ - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): AESCBCPadder, - "NoPadding": NoPadder, - }, + CryptoRegistry: cryptoRegistry, } for _, option := range options { option(clientOptions) } - return &DecryptionClientV2{options: *clientOptions} + if err := cryptoRegistry.valid(); err != nil { + return nil, err + } + + return &DecryptionClientV2{options: *clientOptions}, nil } // GetObjectRequest will make a request to s3 and retrieve the object. In this process // decryption will be done. The SDK only supports V2 reads of KMS and GCM. // // Example: -// sess := session.Must(session.NewSession()) -// svc := s3crypto.NewDecryptionClientV2(sess) // req, out := svc.GetObjectRequest(&s3.GetObjectInput { // Key: aws.String("testKey"), // Bucket: aws.String("testBucket"),
service/s3/s3crypto/decryption_client_v2_test.go+300 −5 modified@@ -1,16 +1,28 @@ +// +build go1.7 + package s3crypto_test import ( + "bytes" + "encoding/hex" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/awstesting/unit" "github.com/aws/aws-sdk-go/service/kms" + "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3crypto" ) func TestDecryptionClientV2_CheckDeprecatedFeatures(t *testing.T) { // AES/GCM/NoPadding with kms+context => allowed - builder := s3crypto.AESGCMContentCipherBuilder(s3crypto.NewKMSContextKeyGenerator(kms.New(unit.Session), "cmkID")) + builder := s3crypto.AESGCMContentCipherBuilderV2(s3crypto.NewKMSContextKeyGenerator(kms.New(unit.Session), "cmkID", s3crypto.MaterialDescription{})) _, err := s3crypto.NewEncryptionClientV2(unit.Session, builder) if err != nil { t.Errorf("expected no error, got %v", err) @@ -29,11 +41,294 @@ func TestDecryptionClientV2_CheckDeprecatedFeatures(t *testing.T) { if err == nil { t.Error("expected error, but got nil") } +} - // AES/CBC/PKCS5Padding with kms+context => not allowed - builder = s3crypto.AESCBCContentCipherBuilder(s3crypto.NewKMSContextKeyGenerator(kms.New(unit.Session), "cmkID"), s3crypto.NewPKCS7Padder(128)) - _, err = s3crypto.NewEncryptionClientV2(unit.Session, builder) +func TestDecryptionClientV2_GetObject(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "hJUv7S6K2cHF64boS9ixHX0TZAjBZLT4ZpEO4XxkGnY=", `"}`)) + })) + defer ts.Close() + + kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) + + cr := s3crypto.NewCryptoRegistry() + if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kmsClient); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + input := &s3.GetObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + } + + req, out := c.GetObjectRequest(input) + req.Handlers.Send.Clear() + req.Handlers.Send.PushBack(func(r *request.Request) { + b, err := hex.DecodeString("6b134eb7a353131de92faff64f594b2794e3544e31776cca26fe3bbeeffc68742d1007234f11c6670522602326868e29f37e9d2678f1614ec1a2418009b9772100929aadbed9a21a") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: http.Header{ + http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"PsuclPnlo2O0MQoov6kL1TBlaZG6oyNwWuAqmAgq7g8b9ZeeORi3VTMg624FU9jx"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"dqqlq2dRVSQ5hFRb"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"aws:x-amz-cek-alg":"AES/GCM/NoPadding"}`}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSContextWrap}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(b)), + } + }) + err = req.Send() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + actual, err := ioutil.ReadAll(out.Body) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected, err := hex.DecodeString("af150d7156bf5b3f5c461e5c6ac820acc5a33aab7085d920666c250ff251209d5a4029b3bd78250fab6e11aed52fae948d407056a9519b68") + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if bytes.Compare(expected, actual) != 0 { + t.Fatalf("expected content to match but it did not") + } +} + +func TestDecryptionClientV2_GetObject_V1Interop_KMS_AESCBC(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "7ItX9CTGNWWegC62RlaNu6EJ3+J9yGO7yAqDNU4CdeA=", `"}`)) + })) + defer ts.Close() + + kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) + + cr := s3crypto.NewCryptoRegistry() + if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kmsClient); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := s3crypto.RegisterAESCBCContentCipher(cr, s3crypto.AESCBCPadder); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + input := &s3.GetObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + } + + req, out := c.GetObjectRequest(input) + req.Handlers.Send.Clear() + req.Handlers.Send.PushBack(func(r *request.Request) { + b, err := hex.DecodeString("6f4f413a357a3c3a12289442fb835c5e4ecc8db1d86d3d1eab906ce07e1ad772180b2e9ec49c3fc667d8aceea8c46da6bb9738251a8e36241a473ad820f99c701906bac1f48578d5392e928889bbb1d9") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: http.Header{ + http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"/nJlgMtxMNk2ErKLLrLp3H7A7aQyJcJOClE2ldAIIFNZU4OhUMc1mMCHdIEC8fby"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"adO9U7pcEHxUTaguIkho9g=="}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/CBC/PKCS5Padding"}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(b)), + } + }) + err = req.Send() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + actual, err := ioutil.ReadAll(out.Body) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected, err := hex.DecodeString("a716e018ffecf4bb94d4352082af4662612d9c225efed6f389bf1f6f0447a9bce80cc712d7e66ee5e1c086af38e607ead351fd2c1a0247878e693ada73bd580b") + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if bytes.Compare(expected, actual) != 0 { + t.Fatalf("expected content to match but it did not") + } +} + +func TestDecryptionClientV2_GetObject_V1Interop_KMS_AESGCM(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "Hrjrkkt/vQwMYtqvK6+MiXh3xiMvviL1Ks7w2mgsJgU=", `"}`)) + })) + defer ts.Close() + + kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) + + cr := s3crypto.NewCryptoRegistry() + if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kmsClient); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + input := &s3.GetObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + } + + req, out := c.GetObjectRequest(input) + req.Handlers.Send.Clear() + req.Handlers.Send.PushBack(func(r *request.Request) { + b, err := hex.DecodeString("6370a90b9a118301c2160c23a90d96146761276acdcfa92e6cbcb783abdc2e1813891506d6850754ef87ed2ac3bf570dd5c9da9492b7769ae1e639d073d688bd284815404ce2648a") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: http.Header{ + http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"/7tu/RFXZU1UFwRzzf11IdF3b1wBxBZhnUMjVYHKKr5DjAHS602GvXt4zYcx/MJo"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"8Rlvyy8AoYj8v579"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(b)), + } + }) + err = req.Send() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + actual, err := ioutil.ReadAll(out.Body) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected, err := hex.DecodeString("75f6805afa7d7be4f56c5906adc27a5959158bf4af6e7c7e12bda3458300f6b1c8daaf9a5949f7a6bdbb8a9c072de05bf0541633421f42f8") + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if bytes.Compare(expected, actual) != 0 { + t.Fatalf("expected content to match but it did not") + } +} + +func TestDecryptionClientV2_GetObject_OnlyDecryptsRegisteredAlgorithms(t *testing.T) { + dataHandler := func(r *request.Request) { + b, err := hex.DecodeString("1bd0271b25951fdef3dbe51a9b7af85f66b311e091aa10a346655068f657b9da9acc0843ea0522b0d1ae4a25a31b13605dd1ac5d002db8965d9d4652fd602693") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + r.HTTPResponse = &http.Response{ + StatusCode: 200, + Header: http.Header{ + http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"gNuYjzkLTzfhOcIX9h1l8jApWcAAQqzlryOE166kdDojaHH/+7cCqR5HU8Bpxmij"}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"Vmauu+TMEgaXa26ObqpARA=="}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, + http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/CBC/PKCS5Padding"}, + }, + Body: ioutil.NopCloser(bytes.NewBuffer(b)), + } + } + + cases := map[string]struct { + Client *s3crypto.DecryptionClientV2 + WantErr string + }{ + "unsupported wrap": { + Client: func() *s3crypto.DecryptionClientV2 { + cr := s3crypto.NewCryptoRegistry() + if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(unit.Session.Copy())); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + return c + }(), + WantErr: "wrap algorithm isn't supported, kms", + }, + "unsupported cek": { + Client: func() *s3crypto.DecryptionClientV2 { + cr := s3crypto.NewCryptoRegistry() + if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kms.New(unit.Session.Copy())); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + return c + }(), + WantErr: "cek algorithm isn't supported, AES/CBC/PKCS5Padding", + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + input := &s3.GetObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + } + req, _ := tt.Client.GetObjectRequest(input) + req.Handlers.Send.Clear() + req.Handlers.Send.PushBack(dataHandler) + err := req.Send() + if err == nil { + t.Fatalf("expected error, got none") + } + if e, a := tt.WantErr, err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + }) + } +} + +func TestDecryptionClientV2_CheckValidCryptoRegistry(t *testing.T) { + cr := s3crypto.NewCryptoRegistry() + _, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) if err == nil { - t.Error("expected error, but got nil") + t.Fatal("expected error, got none") + } + if e, a := "at least one key wrapping algorithms must be provided", err.Error(); !strings.Contains(a, e) { + t.Fatalf("expected %v, got %v", e, a) } }
service/s3/s3crypto/deprecations.go+0 −10 removed@@ -1,10 +0,0 @@ -package s3crypto - -import "fmt" - -var errDeprecatedCipherBuilder = fmt.Errorf("attempted to use deprecated cipher builder") -var errDeprecatedCipherDataGenerator = fmt.Errorf("attempted to use deprecated cipher data generator") - -type deprecatedFeatures interface { - isUsingDeprecatedFeatures() error -}
service/s3/s3crypto/deprecations_test.go+0 −51 removed@@ -1,51 +0,0 @@ -package s3crypto - -import ( - "testing" - - "github.com/aws/aws-sdk-go/service/kms/kmsiface" -) - -type mockKMS struct { - kmsiface.KMSAPI -} - -func TestAESGCMContentCipherBuilder_isUsingDeprecatedFeatures(t *testing.T) { - builder := AESGCMContentCipherBuilder(NewKMSKeyGenerator(mockKMS{}, "cmkID")) - - features, ok := builder.(deprecatedFeatures) - if !ok { - t.Errorf("expected to implement deprecatedFeatures interface") - } - - err := features.isUsingDeprecatedFeatures() - if err == nil { - t.Errorf("expected to recieve error for using deprecated features") - } - - builder = AESGCMContentCipherBuilder(NewKMSContextKeyGenerator(mockKMS{}, "cmkID")) - - features, ok = builder.(deprecatedFeatures) - if !ok { - t.Errorf("expected to implement deprecatedFeatures interface") - } - - err = features.isUsingDeprecatedFeatures() - if err != nil { - t.Errorf("expected no error, got %v", err) - } -} - -func TestAESCBCContentCipherBuilder_isUsingDeprecatedFeatures(t *testing.T) { - builder := AESCBCContentCipherBuilder(nil, nil) - - features, ok := builder.(deprecatedFeatures) - if !ok { - t.Errorf("expected to implement deprecatedFeatures interface") - } - - err := features.isUsingDeprecatedFeatures() - if err == nil { - t.Errorf("expected to recieve error for using deprecated features") - } -}
service/s3/s3crypto/doc.go+64 −21 modified@@ -16,20 +16,41 @@ Creating an S3 cryptography client cmkID := "<some key ID>" sess := session.Must(session.NewSession()) + kmsClient := kms.New(sess) // Create the KeyProvider - handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID) + var matdesc s3crypto.MaterialDescription + handler := s3crypto.NewKMSContextKeyGenerator(kmsClient, cmkID, matdesc) // Create an encryption and decryption client // We need to pass the session here so S3 can use it. In addition, any decryption that // occurs will use the KMS client. - svc := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilder(handler)) - svc := s3crypto.NewDecryptionClientV2(sess) + svc, err := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilderV2(handler)) + if err != nil { + panic(err) // handle error + } + + // Create a CryptoRegistry and register the algorithms you wish to use for decryption + cr := s3crypto.NewCryptoRegistry() + + if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + panic(err) // handle error + } + + if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kmsClient); err != nil { + panic(err) // handle error + } + + // Create a decryption client to decrypt artifacts + svc, err := s3crypto.NewDecryptionClientV2(sess, cr) + if err != nil { + panic(err) // handle error + } Configuration of the S3 cryptography client sess := session.Must(session.NewSession()) - handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID) - svc := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilder(handler), func (o *s3crypto.EncryptionClientOptions) { + handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID, s3crypto.MaterialDescription{}) + svc, err := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilderV2(handler), func (o *s3crypto.EncryptionClientOptions) { // Save instruction files to separate objects o.SaveStrategy = NewS3SaveStrategy(sess, "") @@ -43,20 +64,39 @@ Configuration of the S3 cryptography client // instead of writing the contents to a temp file. o.MinFileSize = int64(1024 * 1024 * 1024) }) + if err != nil { + panic(err) // handle error + } -The default SaveStrategy is to the object's header. +Object Metadata SaveStrategy -The InstructionFileSuffix defaults to .instruction. Careful here though, if you do this, be sure you know -what that suffix is in grabbing data. All requests will look for fooKey.example instead of fooKey.instruction. -This suffix only affects gets and not puts. Put uses the keyprovider's suffix. +The default SaveStrategy is to save metadata to an object's headers. An alternative SaveStrategy can be provided to the EncryptionClientV2. +For example, the S3SaveStrategy can be used to save the encryption metadata to a instruction file that is stored in S3 +using the objects KeyName+InstructionFileSuffix. The InstructionFileSuffix defaults to .instruction. If using this strategy you will need to +configure the DecryptionClientV2 to use the matching S3LoadStrategy LoadStrategy in order to decrypt object using this save strategy. -Registration of new wrap or cek algorithms are also supported by the SDK. Let's say we want to support `AES Wrap` -and `AES CTR`. Let's assume we have already defined the functionality. +Custom Key Wrappers and Custom Content Encryption Algorithms - svc := s3crypto.NewDecryptionClientV2(sess, func(o *s3crypto.DecryptionClientOptions) { - o.WrapRegistry["CustomWrap"] = NewCustomWrap - o.CEKRegistry["CustomCEK"] = NewCustomCEK - }) +Registration of custom key wrapping or content encryption algorithms not provided by AWS is allowed by the SDK, but +security and compatibility with custom types can not be guaranteed. For example if you want to support `CustomWrap` +key wrapping algorithm and `CustomCEK` content encryption algorithm. You can use the CryptoRegistry to register these types. + + cr := s3crypto.NewCryptoRegistry() + + // Register a custom key wrap algorithm to the CryptoRegistry + if err := cr.AddWrap("CustomWrap", NewCustomWrapEntry); err != nil { + panic(err) // handle error + } + + // Register a custom content encryption algorithm to the CryptoRegistry + if err := cr.AddCEK("CustomCEK", NewCustomCEKEntry); err != nil { + panic(err) // handle error + } + + svc, err := s3crypto.NewDecryptionClientV2(sess, cr) + if err != nil { + panic(err) // handle error + } We have now registered these new algorithms to the decryption client. When the client calls `GetObject` and sees the wrap as `CustomWrap` then it'll use that wrap algorithm. This is also true for `CustomCEK`. @@ -69,27 +109,30 @@ defined ciphers. // Our content cipher builder, NewCustomCEKContentBuilder svc := s3crypto.NewEncryptionClientV2(sess, NewCustomCEKContentBuilder(handler)) -Deprecations +Maintenance Mode Notification for V1 Clients -The EncryptionClient and DecryptionClient types and their associated constructor functions have been deprecated. -Users of these clients should migrate to EncryptionClientV2 and DecryptionClientV2 types and constructor functions. +The EncryptionClient and DecryptionClient are in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. +Users of these clients should migrate to EncryptionClientV2 and DecryptionClientV2. EncryptionClientV2 removes encryption support of the following features - * AES/CBC/PKCS5Padding (content cipher) + * AES/CBC (content cipher) * kms (key wrap algorithm) Attempting to construct an EncryptionClientV2 with deprecated features will result in an error returned back to the calling application during construction of the client. -Users of `AES/CBC/PKCS5Padding` will need to migrate usage to `AES/GCM/NoPadding`. +Users of `AES/CBC` will need to migrate usage to `AES/GCM`. Users of `kms` key provider will need to migrate `kms+context`. DecryptionClientV2 client adds support for the `kms+context` key provider and maintains backwards comparability with -objects encrypted with the deprecated EncryptionClient. +objects encrypted with the V1 EncryptionClient. Migrating from V1 to V2 Clients Examples of how to migrate usage of the V1 clients to the V2 equivalents have been documented as usage examples of the NewEncryptionClientV2 and NewDecryptionClientV2 functions. + +Please see the AWS SDK for Go Developer Guide for additional migration steps https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-encryption-migration.html + */ package s3crypto
service/s3/s3crypto/encryption_client.go+44 −8 modified@@ -2,6 +2,7 @@ package s3crypto import ( "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/s3" @@ -17,7 +18,7 @@ const DefaultMinFileSize = 1024 * 512 * 5 // AES GCM will load all data into memory. However, the rest of the content algorithms // do not load the entire contents into memory. // -// deprecated: See EncryptionClientV2 +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. type EncryptionClient struct { S3Client s3iface.S3API ContentCipherBuilder ContentCipherBuilder @@ -33,6 +34,19 @@ type EncryptionClient struct { MinFileSize int64 } +func validateV1EncryptionClientConstruction(c *EncryptionClient) error { + builder, ok := c.ContentCipherBuilder.(compatibleEncryptionFixture) + if !ok { + return nil + } + + err := builder.isEncryptionVersionCompatible(v1ClientVersion) + if err != nil { + return awserr.New(clientConstructionErrorCode, "invalid client configuration", err) + } + return nil +} + // NewEncryptionClient instantiates a new S3 crypto client // // Example: @@ -41,12 +55,12 @@ type EncryptionClient struct { // handler := s3crypto.NewKMSKeyGenerator(kms.New(sess), cmkID) // svc := s3crypto.NewEncryptionClient(sess, s3crypto.AESGCMContentCipherBuilder(handler)) // -// deprecated: See NewEncryptionClientV2 +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func NewEncryptionClient(prov client.ConfigProvider, builder ContentCipherBuilder, options ...func(*EncryptionClient)) *EncryptionClient { s3client := s3.New(prov) s3client.Handlers.Build.PushBack(func(r *request.Request) { - request.AddToUserAgent(r, "S3Crypto") + request.AddToUserAgent(r, "S3CryptoV1n") }) client := &EncryptionClient{ @@ -67,23 +81,42 @@ func NewEncryptionClient(prov client.ConfigProvider, builder ContentCipherBuilde // that data to S3. // // Example: -// svc := s3crypto.New(session.New(), s3crypto.AESGCMContentCipherBuilder(handler)) +// svc := s3crypto.NewEncryptionClient(session.Must(session.NewSession()), s3crypto.AESGCMContentCipherBuilder(handler)) // req, out := svc.PutObjectRequest(&s3.PutObjectInput { // Key: aws.String("testKey"), // Bucket: aws.String("testBucket"), // Body: strings.NewReader("test data"), // }) // err := req.Send() // -// deprecated: See EncryptionClientV2.PutObjectRequest +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *EncryptionClient) PutObjectRequest(input *s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) { - return putObjectRequest(c.getClientOptions(), input) + req, out := putObjectRequest(c.getClientOptions(), input) + if err := validateV1EncryptionClientConstruction(c); err != nil { + errHandler := setReqError(err) + req.Error = err + req.Handlers.Build.Clear() + req.Handlers.Send.Clear() + req.Handlers.Validate.PushFront(errHandler) + req.Handlers.Build.PushFront(errHandler) + req.Handlers.Send.PushFront(errHandler) + } + return req, out +} + +func setReqError(err error) func(*request.Request) { + return func(r *request.Request) { + r.Error = err + } } // PutObject is a wrapper for PutObjectRequest // -// deprecated: See EncryptionClientV2.PutObject +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *EncryptionClient) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error) { + if err := validateV1EncryptionClientConstruction(c); err != nil { + return nil, err + } return putObject(c.getClientOptions(), input) } @@ -96,8 +129,11 @@ func (c *EncryptionClient) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOut // this may create sub-contexts for individual underlying requests. // PutObject is a wrapper for PutObjectRequest // -// deprecated: See EncryptionClientV2.PutObjectWithContext +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func (c *EncryptionClient) PutObjectWithContext(ctx aws.Context, input *s3.PutObjectInput, opts ...request.Option) (*s3.PutObjectOutput, error) { + if err := validateV1EncryptionClientConstruction(c); err != nil { + return nil, err + } return putObjectWithContext(c.getClientOptions(), ctx, input, opts...) }
service/s3/s3crypto/encryption_client_test.go+53 −6 modified@@ -1,4 +1,4 @@ -package s3crypto_test +package s3crypto import ( "bytes" @@ -16,7 +16,6 @@ import ( "github.com/aws/aws-sdk-go/awstesting/unit" "github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3crypto" ) func TestDefaultConfigValues(t *testing.T) { @@ -26,9 +25,9 @@ func TestDefaultConfigValues(t *testing.T) { Region: aws.String("us-west-2"), }) svc := kms.New(sess) - handler := s3crypto.NewKMSKeyGenerator(svc, "testid") + handler := NewKMSKeyGenerator(svc, "testid") - c := s3crypto.NewEncryptionClient(sess, s3crypto.AESGCMContentCipherBuilder(handler)) + c := NewEncryptionClient(sess, AESGCMContentCipherBuilder(handler)) if c == nil { t.Error("expected non-vil client value") @@ -52,7 +51,7 @@ func TestPutObject(t *testing.T) { S3ForcePathStyle: aws.Bool(true), Region: aws.String("us-west-2"), }) - c := s3crypto.NewEncryptionClient(sess, cb) + c := NewEncryptionClient(sess, cb) if c == nil { t.Error("expected non-vil client value") } @@ -86,7 +85,7 @@ func TestPutObjectWithContext(t *testing.T) { generator := mockGenerator{} cb := mockCipherBuilder{generator} - c := s3crypto.NewEncryptionClient(unit.Session, cb) + c := NewEncryptionClient(unit.Session, cb) ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})} ctx.Error = fmt.Errorf("context canceled") @@ -109,3 +108,51 @@ func TestPutObjectWithContext(t *testing.T) { t.Errorf("expected error message to contain %q, but did not %q", e, a) } } + +func TestEncryptionClient_PutObject_InvalidClientConstruction(t *testing.T) { + generator := mockGeneratorV2{} + cb := mockCipherBuilderV2{generator: generator} + + c := NewEncryptionClient(unit.Session, cb) + if c == nil { + t.Errorf("expected client not to be nil") + } + + input := s3.PutObjectInput{ + Bucket: aws.String("test"), + Key: aws.String("test"), + Body: bytes.NewReader([]byte{}), + } + _, err := c.PutObject(&input) + if err == nil { + t.Fatalf("expected error, did not get one") + } + if e, a := "invalid client configuration", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + + _, err = c.PutObjectWithContext(aws.BackgroundContext(), &input) + if err == nil { + t.Fatalf("expected error, did not get one") + } + if e, a := "invalid client configuration", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + + _, err = c.PutObjectWithContext(aws.BackgroundContext(), &input) + if err == nil { + t.Fatalf("expected error, did not get one") + } + if e, a := "invalid client configuration", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } + + req, _ := c.PutObjectRequest(&input) + err = req.Send() + if err == nil { + t.Fatalf("expected error, did not get one") + } + if e, a := "invalid client configuration", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected %v, got %v", e, a) + } +}
service/s3/s3crypto/encryption_client_v2.go+17 −10 modified@@ -8,6 +8,8 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3iface" ) +const customTypeWarningMessage = "WARNING: The S3 Encryption Client is configured to write encrypted objects using types not provided by AWS. Security and compatibility with these types can not be guaranteed." + // EncryptionClientV2 is an S3 crypto client. By default the SDK will use Authentication mode which // will use KMS for key wrapping and AES GCM for content encryption. // AES GCM will load all data into memory. However, the rest of the content algorithms @@ -33,19 +35,19 @@ type EncryptionClientOptions struct { } // NewEncryptionClientV2 instantiates a new S3 crypto client. An error will be returned to the caller if the provided -// contentCipherBuilder has been deprecated, or if it uses other deprecated components. +// contentCipherBuilder has been deprecated or was constructed with a deprecated component. // // Example: // cmkID := "arn:aws:kms:region:000000000000:key/00000000-0000-0000-0000-000000000000" // sess := session.Must(session.NewSession()) -// handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID) -// svc := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilder(handler)) +// var matdesc s3crypto.MaterialDescription +// handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID, matdesc) +// svc := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilderV2(handler)) func NewEncryptionClientV2(prov client.ConfigProvider, contentCipherBuilder ContentCipherBuilder, options ...func(clientOptions *EncryptionClientOptions), ) ( client *EncryptionClientV2, err error, ) { s3client := s3.New(prov) - s3client.Handlers.Build.PushBack(func(r *request.Request) { request.AddToUserAgent(r, "S3CryptoV2") }) @@ -56,17 +58,25 @@ func NewEncryptionClientV2(prov client.ConfigProvider, contentCipherBuilder Cont SaveStrategy: HeaderV2SaveStrategy{}, MinFileSize: DefaultMinFileSize, } - for _, option := range options { option(clientOptions) } - if feature, ok := contentCipherBuilder.(deprecatedFeatures); ok { - if err := feature.isUsingDeprecatedFeatures(); err != nil { + // Check that the configured client uses a compatible ContentCipherBuilder. + // User provided types will not implement this method + if fixture, ok := contentCipherBuilder.(compatibleEncryptionFixture); ok { + if err := fixture.isEncryptionVersionCompatible(v2ClientVersion); err != nil { return nil, err } } + // Check if the passed in type is an fixture, if not log a warning message to the user + if fixture, ok := contentCipherBuilder.(awsFixture); !ok || !fixture.isAWSFixture() { + if s3client.Config.Logger != nil { + s3client.Config.Logger.Log(customTypeWarningMessage) + } + } + client = &EncryptionClientV2{ *clientOptions, } @@ -78,9 +88,6 @@ func NewEncryptionClientV2(prov client.ConfigProvider, contentCipherBuilder Cont // that data to S3. // // Example: -// sess := session.Must(session.NewSession()) -// handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), "cmkID") -// svc := s3crypto.NewEncryptionClientV2(sess, s3crypto.AESGCMContentCipherBuilder(handler)) // req, out := svc.PutObjectRequest(&s3.PutObjectInput { // Key: aws.String("testKey"), // Bucket: aws.String("testBucket"),
service/s3/s3crypto/encryption_client_v2_test.go+211 −0 added@@ -0,0 +1,211 @@ +// +build go1.7 + +package s3crypto + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/awstesting/unit" + "github.com/aws/aws-sdk-go/service/kms" + "github.com/aws/aws-sdk-go/service/s3" +) + +func sessionWithLogCheck(message string) (*session.Session, *bool) { + gotWarning := false + + u := unit.Session.Copy(&aws.Config{Logger: aws.LoggerFunc(func(i ...interface{}) { + if len(i) == 0 { + return + } + s, ok := i[0].(string) + if !ok { + return + } + if s == message { + gotWarning = true + } + })}) + + return u, &gotWarning +} + +func TestNewEncryptionClientV2(t *testing.T) { + tUnit, gotWarning := sessionWithLogCheck(customTypeWarningMessage) + + mcb := AESGCMContentCipherBuilderV2(NewKMSContextKeyGenerator(nil, "id", nil)) + v2, err := NewEncryptionClientV2(tUnit, mcb) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if v2 == nil { + t.Fatal("expected client to not be nil") + } + + if *gotWarning { + t.Errorf("expected no warning for aws provided custom cipher builder") + } + + if !reflect.DeepEqual(mcb, v2.options.ContentCipherBuilder) { + t.Errorf("content cipher builder did not match provided value") + } + + _, ok := v2.options.SaveStrategy.(HeaderV2SaveStrategy) + if !ok { + t.Errorf("expected default save strategy to be s3 header strategy") + } + + if v2.options.S3Client == nil { + t.Errorf("expected s3 client not be nil") + } + + if e, a := DefaultMinFileSize, v2.options.MinFileSize; int64(e) != a { + t.Errorf("expected %v, got %v", e, a) + } + + if e, a := "", v2.options.TempFolderPath; e != a { + t.Errorf("expected %v, got %v", e, a) + } +} + +func TestNewEncryptionClientV2_NonDefaults(t *testing.T) { + tUnit, gotWarning := sessionWithLogCheck(customTypeWarningMessage) + + s3Client := s3.New(tUnit) + + mcb := mockCipherBuilderV2{} + v2, err := NewEncryptionClientV2(tUnit, nil, func(clientOptions *EncryptionClientOptions) { + clientOptions.S3Client = s3Client + clientOptions.ContentCipherBuilder = mcb + clientOptions.TempFolderPath = "/mock/path" + clientOptions.MinFileSize = 42 + clientOptions.SaveStrategy = S3SaveStrategy{} + }) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if v2 == nil { + t.Fatal("expected client to not be nil") + } + + if !*gotWarning { + t.Errorf("expected warning for custom provided content cipher builder") + } + + if !reflect.DeepEqual(mcb, v2.options.ContentCipherBuilder) { + t.Errorf("content cipher builder did not match provided value") + } + + _, ok := v2.options.SaveStrategy.(S3SaveStrategy) + if !ok { + t.Errorf("expected default save strategy to be s3 header strategy") + } + + if v2.options.S3Client != s3Client { + t.Errorf("expected s3 client not be nil") + } + + if e, a := 42, v2.options.MinFileSize; int64(e) != a { + t.Errorf("expected %v, got %v", e, a) + } + + if e, a := "/mock/path", v2.options.TempFolderPath; e != a { + t.Errorf("expected %v, got %v", e, a) + } +} + +// cdgWithStaticTestIV is a test structure that wraps a CipherDataGeneratorWithCEKAlg and stubs in a static IV +// so that encryption tests can be guranteed to be consistent. +type cdgWithStaticTestIV struct { + IV []byte + CipherDataGeneratorWithCEKAlg +} + +// isAWSFixture will avoid the warning log message when doing tests that need to mock the IV +func (k cdgWithStaticTestIV) isAWSFixture() bool { + return true +} + +func (k cdgWithStaticTestIV) GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize, ivSize int, cekAlg string) (CipherData, error) { + cipherData, err := k.CipherDataGeneratorWithCEKAlg.GenerateCipherDataWithCEKAlg(ctx, keySize, ivSize, cekAlg) + if err == nil { + cipherData.IV = k.IV + } + return cipherData, err +} + +func TestEncryptionClientV2_PutObject_KMSCONTEXT_AESGCM(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + fmt.Fprintln(writer, `{"CiphertextBlob":"8gSzlk7giyfFbLPUVgoVjvQebI1827jp8lDkO+n2chsiSoegx1sjm8NdPk0Bl70I","KeyId":"test-key-id","Plaintext":"lP6AbIQTmptyb/+WQq+ubDw+w7na0T1LGSByZGuaono="}`) + })) + + sess := unit.Session.Copy() + kmsClient := kms.New(sess.Copy(&aws.Config{Endpoint: &ts.URL})) + + var md MaterialDescription + iv, _ := hex.DecodeString("ae325acae2bfd5b9c3d0b813") + kmsWithStaticIV := cdgWithStaticTestIV{ + IV: iv, + CipherDataGeneratorWithCEKAlg: NewKMSContextKeyGenerator(kmsClient, "test-key-id", md), + } + contentCipherBuilderV2 := AESGCMContentCipherBuilderV2(kmsWithStaticIV) + client, err := NewEncryptionClientV2(sess, contentCipherBuilderV2) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + req, _ := client.PutObjectRequest(&s3.PutObjectInput{ + Bucket: aws.String("test-bucket"), + Key: aws.String("test-key"), + Body: func() io.ReadSeeker { + content, _ := hex.DecodeString("8f2c59c6dbfcacf356f3da40788cbde67ca38161a4702cbcf757af663e1c24a600001b2f500417dbf5a050f57db6737422b2ed6a44c75e0d") + return bytes.NewReader(content) + }(), + }) + + req.Handlers.Send.Clear() + req.Handlers.Send.PushFront(func(r *request.Request) { + all, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected, _ := hex.DecodeString("4cd8e95a1c9b8b19640e02838b02c8c09e66250703a602956695afbc23cbb8647d51645955ab63b89733d0766f9a264adb88571b1d467b734ff72eb73d31de9a83670d59688c54ea") + + if !bytes.Equal(all, expected) { + t.Error("encrypted bytes did not match expected") + } + + req.HTTPResponse = &http.Response{ + Status: http.StatusText(200), + StatusCode: http.StatusOK, + Body: aws.ReadSeekCloser(bytes.NewReader([]byte{})), + } + }) + err = req.Send() + if err != nil { + t.Errorf("expected no error, got %v", err) + } +} + +func TestNewEncryptionClientV2_FailsOnIncompatibleFixtures(t *testing.T) { + sess := unit.Session.Copy() + _, err := NewEncryptionClientV2(sess, AESGCMContentCipherBuilder(NewKMSKeyGenerator(kms.New(sess), "cmkId"))) + if err == nil { + t.Fatal("expected to fail, but got nil") + } + if !strings.Contains(err.Error(), "attempted to use deprecated or incompatible cipher builder") { + t.Errorf("expected to get error for using dperecated cipher builder") + } +}
service/s3/s3crypto/envelope.go+8 −5 modified@@ -34,11 +34,14 @@ type Envelope struct { // CipherKey is the randomly generated cipher key. CipherKey string `json:"x-amz-key-v2"` // MaterialDesc is a description to distinguish from other envelopes. - MatDesc string `json:"x-amz-matdesc"` - WrapAlg string `json:"x-amz-wrap-alg"` - CEKAlg string `json:"x-amz-cek-alg"` - TagLen string `json:"x-amz-tag-len"` - UnencryptedMD5 string `json:"-"` + MatDesc string `json:"x-amz-matdesc"` + WrapAlg string `json:"x-amz-wrap-alg"` + CEKAlg string `json:"x-amz-cek-alg"` + TagLen string `json:"x-amz-tag-len"` + + // deprecated: This MD5 hash is no longer populated + UnencryptedMD5 string `json:"-"` + UnencryptedContentLen string `json:"x-amz-unencrypted-content-length"` }
service/s3/s3crypto/errors.go+24 −0 added@@ -0,0 +1,24 @@ +package s3crypto + +import "fmt" + +var errNilCryptoRegistry = fmt.Errorf("provided CryptoRegistry must not be nil") +var errNilWrapEntry = fmt.Errorf("wrap entry must not be nil") +var errNilCEKEntry = fmt.Errorf("cek entry must not be nil") +var errNilPadder = fmt.Errorf("padder must not be nil") + +func newErrDuplicateWrapEntry(name string) error { + return newErrDuplicateRegistryEntry("wrap", name) +} + +func newErrDuplicateCEKEntry(name string) error { + return newErrDuplicateRegistryEntry("cek", name) +} + +func newErrDuplicatePadderEntry(name string) error { + return newErrDuplicateRegistryEntry("padder", name) +} + +func newErrDuplicateRegistryEntry(registry, key string) error { + return fmt.Errorf("duplicate %v registry entry, %v", registry, key) +}
service/s3/s3crypto/fixture.go+27 −0 added@@ -0,0 +1,27 @@ +package s3crypto + +import "fmt" + +type clientVersion int + +const ( + v1ClientVersion clientVersion = 1 + iota + v2ClientVersion +) + +var errDeprecatedIncompatibleCipherBuilder = fmt.Errorf("attempted to use deprecated or incompatible cipher builder") + +// compatibleEncryptionFixture is an unexported interface to expose whether a given fixture is compatible for encryption +// given the provided client version. +type compatibleEncryptionFixture interface { + isEncryptionVersionCompatible(clientVersion) error +} + +// awsFixture is an unexported interface to expose whether a given fixture is an aws provided fixture, and whether that +// fixtures dependencies were constructed using aws types. +// +// This interface is used in v2 clients to warn users if they are using custom implementations of ContentCipherBuilder +// or CipherDataGenerator. +type awsFixture interface { + isAWSFixture() bool +}
service/s3/s3crypto/hash_io.go+24 −33 modified@@ -1,19 +1,38 @@ package s3crypto import ( - "crypto/md5" "crypto/sha256" "hash" "io" ) -// hashReader is used for calculating SHA256 when following the sigv4 specification. -// Additionally this used for calculating the unencrypted MD5. -type hashReader interface { - GetValue() []byte +// lengthReader returns the content length +type lengthReader interface { GetContentLength() int64 } +type contentLengthReader struct { + contentLength int64 + body io.Reader +} + +func newContentLengthReader(f io.Reader) *contentLengthReader { + return &contentLengthReader{body: f} +} + +func (r *contentLengthReader) Read(b []byte) (int, error) { + n, err := r.body.Read(b) + if err != nil && err != io.EOF { + return n, err + } + r.contentLength += int64(n) + return n, err +} + +func (r *contentLengthReader) GetContentLength() int64 { + return r.contentLength +} + type sha256Writer struct { sha256 []byte hash hash.Hash @@ -31,31 +50,3 @@ func (r *sha256Writer) Write(b []byte) (int, error) { func (r *sha256Writer) GetValue() []byte { return r.hash.Sum(nil) } - -type md5Reader struct { - contentLength int64 - hash hash.Hash - body io.Reader -} - -func newMD5Reader(body io.Reader) *md5Reader { - return &md5Reader{hash: md5.New(), body: body} -} - -func (w *md5Reader) Read(b []byte) (int, error) { - n, err := w.body.Read(b) - if err != nil && err != io.EOF { - return n, err - } - w.contentLength += int64(n) - w.hash.Write(b[:n]) - return n, err -} - -func (w *md5Reader) GetValue() []byte { - return w.hash.Sum(nil) -} - -func (w *md5Reader) GetContentLength() int64 { - return w.contentLength -}
service/s3/s3crypto/hash_io_test.go+53 −0 modified@@ -3,6 +3,10 @@ package s3crypto import ( "bytes" "encoding/hex" + "fmt" + "io" + "io/ioutil" + "strings" "testing" ) @@ -27,3 +31,52 @@ func TestSHA256_Case2(t *testing.T) { t.Errorf("expected equivalent sha values, but received otherwise") } } + +type mockReader struct { + err error +} + +func (m mockReader) Read(p []byte) (int, error) { + return len(p), m.err +} + +func TestContentLengthReader(t *testing.T) { + cases := []struct { + reader io.Reader + expected int64 + expectedErr string + }{ + { + reader: bytes.NewReader([]byte("foo bar baz")), + expected: 11, + }, + { + reader: bytes.NewReader(nil), + expected: 0, + }, + { + reader: mockReader{err: fmt.Errorf("not an EOF error")}, + expectedErr: "not an EOF error", + }, + } + + for _, tt := range cases { + reader := newContentLengthReader(tt.reader) + _, err := ioutil.ReadAll(reader) + if err != nil { + if len(tt.expectedErr) == 0 { + t.Errorf("expected no error, got %v", err) + } else if !strings.Contains(err.Error(), tt.expectedErr) { + t.Errorf("expected error %v, got %v", tt.expectedErr, err.Error()) + } + continue + } else if len(tt.expectedErr) > 0 { + t.Error("expected error, got none") + continue + } + actual := reader.GetContentLength() + if tt.expected != actual { + t.Errorf("expected %v, got %v", tt.expected, actual) + } + } +}
service/s3/s3crypto/integration/main_test.go+44 −8 modified@@ -77,9 +77,27 @@ func TestEncryptionV1_WithV2Interop(t *testing.T) { v1DC := s3crypto.NewDecryptionClient(config.Session, func(client *s3crypto.DecryptionClient) { client.S3Client = config.Clients.S3 }) - v2DC := s3crypto.NewDecryptionClientV2(config.Session, func(options *s3crypto.DecryptionClientOptions) { + + cr := s3crypto.NewCryptoRegistry() + if err = s3crypto.RegisterKMSWrapWithAnyCMK(cr, config.Clients.KMS); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err = s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, config.Clients.KMS); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err = s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err = s3crypto.RegisterAESCBCContentCipher(cr, s3crypto.AESCBCPadder); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + v2DC, err := s3crypto.NewDecryptionClientV2(config.Session, cr, func(options *s3crypto.DecryptionClientOptions) { options.S3Client = config.Clients.S3 }) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } cases := map[string]s3crypto.ContentCipherBuilder{ "AES/GCM/NoPadding": s3crypto.AESGCMContentCipherBuilder(kmsKeyGenerator), @@ -102,20 +120,35 @@ func TestEncryptionV1_WithV2Interop(t *testing.T) { } } -func TestEncryptionV2(t *testing.T) { - kmsKeyGenerator := s3crypto.NewKMSContextKeyGenerator(config.Clients.KMS, config.KMSKeyID) - gcmContentCipherBuilder := s3crypto.AESGCMContentCipherBuilder(kmsKeyGenerator) +func TestEncryptionV2_WithV1Interop(t *testing.T) { + kmsKeyGenerator := s3crypto.NewKMSContextKeyGenerator(config.Clients.KMS, config.KMSKeyID, s3crypto.MaterialDescription{}) + gcmContentCipherBuilder := s3crypto.AESGCMContentCipherBuilderV2(kmsKeyGenerator) ec, err := s3crypto.NewEncryptionClientV2(config.Session, gcmContentCipherBuilder, func(options *s3crypto.EncryptionClientOptions) { options.S3Client = config.Clients.S3 }) if err != nil { - t.Fatalf("failed to construct encryption client: %v", err) + t.Fatalf("failed to construct encryption decryptionClient: %v", err) } - dc := s3crypto.NewDecryptionClientV2(config.Session, func(options *s3crypto.DecryptionClientOptions) { + decryptionClient := s3crypto.NewDecryptionClient(config.Session, func(client *s3crypto.DecryptionClient) { + client.S3Client = config.Clients.S3 + }) + + cr := s3crypto.NewCryptoRegistry() + if err = s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, config.Clients.KMS); err != nil { + t.Fatalf("expected no error, got %v", err) + } + if err = s3crypto.RegisterAESGCMContentCipher(cr); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + decryptionClientV2, err := s3crypto.NewDecryptionClientV2(config.Session, cr, func(options *s3crypto.DecryptionClientOptions) { options.S3Client = config.Clients.S3 }) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } // 1020 is chosen here as it is not cleanly divisible by the AES-256 block size testData := make([]byte, 1020) @@ -129,8 +162,11 @@ func TestEncryptionV2(t *testing.T) { // Upload V2 Objects with Encryption Client putObject(t, ec, keyId, bytes.NewReader(testData)) - // Verify V2 Object with Decryption Client - getObjectAndCompare(t, dc, keyId, testData) + // Verify V2 Object with V2 Decryption Client + getObjectAndCompare(t, decryptionClientV2, keyId, testData) + + // Verify V2 Object with V1 Decryption Client + getObjectAndCompare(t, decryptionClient, keyId, testData) } type Encryptor interface {
service/s3/s3crypto/key_handler.go+1 −10 modified@@ -24,16 +24,7 @@ type CipherDataGeneratorWithContext interface { // content cipher. CipherDataGenerator will also encrypt the key and store it in // the CipherData. type CipherDataGeneratorWithCEKAlg interface { - GenerateCipherDataWithCEKAlg(int, int, string) (CipherData, error) - - CipherDataGenerator // backwards comparability to plug into older interface -} - -// CipherDataGeneratorWithCEKAlgWithContext handles generating proper key and IVs of -// proper size for the content cipher. CipherDataGenerator will also encrypt -// the key and store it in the CipherData. -type CipherDataGeneratorWithCEKAlgWithContext interface { - GenerateCipherDataWithCEKAlgWithContext(aws.Context, int, int, string) (CipherData, error) + GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize, ivSize int, cekAlgorithm string) (CipherData, error) } // CipherDataDecrypter is a handler to decrypt keys from the envelope.
service/s3/s3crypto/kms_context_key_handler.go+199 −0 added@@ -0,0 +1,199 @@ +package s3crypto + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kms" + "github.com/aws/aws-sdk-go/service/kms/kmsiface" +) + +const ( + // KMSContextWrap is a constant used during decryption to build a kms+context key handler + KMSContextWrap = "kms+context" + kmsAWSCEKContextKey = "aws:" + cekAlgorithmHeader + + kmsReservedKeyConflictErrMsg = "conflict in reserved KMS Encryption Context key %s. This value is reserved for the S3 Encryption Client and cannot be set by the user" + kmsMismatchCEKAlg = "the content encryption algorithm used at encryption time does not match the algorithm stored for decryption time. The object may be altered or corrupted" +) + +// NewKMSContextKeyGenerator builds a new kms+context key provider using the customer key ID and material +// description. +// +// Example: +// sess := session.Must(session.NewSession()) +// cmkID := "KMS Key ARN" +// var matdesc s3crypto.MaterialDescription +// handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID, matdesc) +func NewKMSContextKeyGenerator(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGeneratorWithCEKAlg { + return newKMSContextKeyHandler(client, cmkID, matdesc) +} + +// RegisterKMSContextWrapWithCMK registers the kms+context wrapping algorithm to the given WrapRegistry. The wrapper +// will be configured to only call KMS Decrypt using the provided CMK. +// +// Example: +// cr := s3crypto.NewCryptoRegistry() +// if err := RegisterKMSContextWrapWithCMK(); err != nil { +// panic(err) // handle error +// } +func RegisterKMSContextWrapWithCMK(registry *CryptoRegistry, client kmsiface.KMSAPI, cmkID string) error { + if registry == nil { + return errNilCryptoRegistry + } + return registry.AddWrap(KMSContextWrap, newKMSContextWrapEntryWithCMK(client, cmkID)) +} + +// RegisterKMSContextWrapWithAnyCMK registers the kms+context wrapping algorithm to the given WrapRegistry. The wrapper +// will be configured to call KMS decrypt without providing a CMK. +// +// Example: +// sess := session.Must(session.NewSession()) +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(sess)); err != nil { +// panic(err) // handle error +// } +func RegisterKMSContextWrapWithAnyCMK(registry *CryptoRegistry, client kmsiface.KMSAPI) error { + if registry == nil { + return errNilCryptoRegistry + } + return registry.AddWrap(KMSContextWrap, newKMSContextWrapEntryWithAnyCMK(client)) +} + +// newKMSContextWrapEntryWithCMK builds returns a new kms+context key provider and its decrypt handler. +// The returned handler will be configured to calls KMS Decrypt API without specifying a specific KMS CMK. +func newKMSContextWrapEntryWithCMK(kmsClient kmsiface.KMSAPI, cmkID string) WrapEntry { + // These values are read only making them thread safe + kp := &kmsContextKeyHandler{ + kms: kmsClient, + cmkID: &cmkID, + } + + return kp.decryptHandler +} + +// newKMSContextWrapEntryWithAnyCMK builds returns a new kms+context key provider and its decrypt handler. +// The returned handler will be configured to calls KMS Decrypt API without specifying a specific KMS CMK. +func newKMSContextWrapEntryWithAnyCMK(kmsClient kmsiface.KMSAPI) WrapEntry { + // These values are read only making them thread safe + kp := &kmsContextKeyHandler{ + kms: kmsClient, + } + + return kp.decryptHandler +} + +// kmsContextKeyHandler wraps the kmsKeyHandler to explicitly make this type incompatible with the v1 client +// by not exposing the old interface implementations. +type kmsContextKeyHandler struct { + kms kmsiface.KMSAPI + cmkID *string + + CipherData +} + +func (kp *kmsContextKeyHandler) isAWSFixture() bool { + return true +} + +func newKMSContextKeyHandler(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) *kmsContextKeyHandler { + kp := &kmsContextKeyHandler{ + kms: client, + cmkID: &cmkID, + } + + if matdesc == nil { + matdesc = MaterialDescription{} + } + + kp.CipherData.WrapAlgorithm = KMSContextWrap + kp.CipherData.MaterialDescription = matdesc + + return kp +} + +func (kp *kmsContextKeyHandler) GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize int, ivSize int, cekAlgorithm string) (CipherData, error) { + cd := kp.CipherData.Clone() + + if len(cekAlgorithm) == 0 { + return CipherData{}, fmt.Errorf("cek algorithm identifier must not be empty") + } + + if _, ok := cd.MaterialDescription[kmsAWSCEKContextKey]; ok { + return CipherData{}, fmt.Errorf(kmsReservedKeyConflictErrMsg, kmsAWSCEKContextKey) + } + cd.MaterialDescription[kmsAWSCEKContextKey] = &cekAlgorithm + + out, err := kp.kms.GenerateDataKeyWithContext(ctx, + &kms.GenerateDataKeyInput{ + EncryptionContext: cd.MaterialDescription, + KeyId: kp.cmkID, + KeySpec: aws.String("AES_256"), + }) + if err != nil { + return CipherData{}, err + } + + iv, err := generateBytes(ivSize) + if err != nil { + return CipherData{}, err + } + + cd.Key = out.Plaintext + cd.IV = iv + cd.EncryptedKey = out.CiphertextBlob + + return cd, nil +} + +// decryptHandler initializes a KMS keyprovider with a material description. This +// is used with Decrypting kms content, due to the cmkID being in the material description. +func (kp kmsContextKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) { + if env.WrapAlg != KMSContextWrap { + return nil, fmt.Errorf("%s value `%s` did not match the expected algorithm `%s` for this handler", cekAlgorithmHeader, env.WrapAlg, KMSContextWrap) + } + + m := MaterialDescription{} + err := m.decodeDescription([]byte(env.MatDesc)) + if err != nil { + return nil, err + } + + if v, ok := m[kmsAWSCEKContextKey]; !ok { + return nil, fmt.Errorf("required key %v is missing from encryption context", kmsAWSCEKContextKey) + } else if v == nil || *v != env.CEKAlg { + return nil, fmt.Errorf(kmsMismatchCEKAlg) + } + + kp.MaterialDescription = m + kp.WrapAlgorithm = KMSContextWrap + + return &kp, nil +} + +// DecryptKey makes a call to KMS to decrypt the key. +func (kp *kmsContextKeyHandler) DecryptKey(key []byte) ([]byte, error) { + return kp.DecryptKeyWithContext(aws.BackgroundContext(), key) +} + +// DecryptKeyWithContext makes a call to KMS to decrypt the key with request context. +func (kp *kmsContextKeyHandler) DecryptKeyWithContext(ctx aws.Context, key []byte) ([]byte, error) { + out, err := kp.kms.DecryptWithContext(ctx, + &kms.DecryptInput{ + KeyId: kp.cmkID, // will be nil and not serialized if created with the AnyCMK constructor + EncryptionContext: kp.MaterialDescription, + CiphertextBlob: key, + GrantTokens: []*string{}, + }) + if err != nil { + return nil, err + } + return out.Plaintext, nil +} + +var ( + _ CipherDataGeneratorWithCEKAlg = (*kmsContextKeyHandler)(nil) + _ CipherDataDecrypter = (*kmsContextKeyHandler)(nil) + _ CipherDataDecrypterWithContext = (*kmsContextKeyHandler)(nil) + _ awsFixture = (*kmsContextKeyHandler)(nil) +)
service/s3/s3crypto/kms_context_key_handler_test.go+265 −0 added@@ -0,0 +1,265 @@ +package s3crypto + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/awstesting/unit" + "github.com/aws/aws-sdk-go/service/kms" +) + +func TestKmsContextKeyHandler_GenerateCipherDataWithCEKAlg(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + bodyBytes, err := ioutil.ReadAll(r.Body) + if err != nil { + w.WriteHeader(500) + return + } + var body map[string]interface{} + err = json.Unmarshal(bodyBytes, &body) + if err != nil { + w.WriteHeader(500) + return + } + + md, ok := body["EncryptionContext"].(map[string]interface{}) + if !ok { + w.WriteHeader(500) + return + } + + exEncContext := map[string]interface{}{ + "aws:" + cekAlgorithmHeader: "cekAlgValue", + } + + if e, a := exEncContext, md; !reflect.DeepEqual(e, a) { + w.WriteHeader(500) + t.Errorf("expected %v, got %v", e, a) + return + } + + fmt.Fprintln(w, `{"CiphertextBlob":"AQEDAHhqBCCY1MSimw8gOGcUma79cn4ANvTtQyv9iuBdbcEF1QAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDJ6IcN5E4wVbk38MNAIBEIA7oF1E3lS7FY9DkoxPc/UmJsEwHzL82zMqoLwXIvi8LQHr8If4Lv6zKqY8u0+JRgSVoqCvZDx3p8Cn6nM=","KeyId":"arn:aws:kms:us-west-2:042062605278:key/c80a5cdb-8d09-4f9f-89ee-df01b2e3870a","Plaintext":"6tmyz9JLBE2yIuU7iXpArqpDVle172WSmxjcO6GNT7E="}`) + })) + defer ts.Close() + + sess := unit.Session.Copy(&aws.Config{ + MaxRetries: aws.Int(0), + Endpoint: aws.String(ts.URL), + DisableSSL: aws.Bool(true), + S3ForcePathStyle: aws.Bool(true), + Region: aws.String("us-west-2"), + }) + + svc := kms.New(sess) + handler := NewKMSContextKeyGenerator(svc, "testid", nil) + + keySize := 32 + ivSize := 16 + + cd, err := handler.GenerateCipherDataWithCEKAlg(aws.BackgroundContext(), keySize, ivSize, "cekAlgValue") + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + if keySize != len(cd.Key) { + t.Errorf("expected %d, but received %d", keySize, len(cd.Key)) + } + if ivSize != len(cd.IV) { + t.Errorf("expected %d, but received %d", ivSize, len(cd.IV)) + } +} + +func TestKmsContextKeyHandler_GenerateCipherDataWithCEKAlg_ReservedKeyConflict(t *testing.T) { + svc := kms.New(unit.Session.Copy()) + handler := NewKMSContextKeyGenerator(svc, "testid", MaterialDescription{ + "aws:x-amz-cek-alg": aws.String("something unexpected"), + }) + + _, err := handler.GenerateCipherDataWithCEKAlg(aws.BackgroundContext(), 32, 16, "cekAlgValue") + if err == nil { + t.Errorf("expected error, but none") + } else if !strings.Contains(err.Error(), "conflict in reserved KMS Encryption Context key aws:x-amz-cek-alg") { + t.Errorf("expected reserved key error, got %v", err) + } +} + +func TestKmsContextKeyHandler_DecryptKey(t *testing.T) { + key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") + keyB64 := base64.URLEncoding.EncodeToString(key) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + bodyBytes, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("expected no error, got %v", err) + w.WriteHeader(500) + return + } + + var body map[string]interface{} + err = json.Unmarshal(bodyBytes, &body) + if err != nil { + w.WriteHeader(500) + return + } + + if _, ok := body["KeyId"]; ok { + t.Errorf("expected CMK to not be sent") + } + + md, ok := body["EncryptionContext"].(map[string]interface{}) + if !ok { + w.WriteHeader(500) + return + } + + exEncContext := map[string]interface{}{ + "aws:" + cekAlgorithmHeader: "AES/GCM/NoPadding", + } + + if e, a := exEncContext, md; !reflect.DeepEqual(e, a) { + w.WriteHeader(500) + t.Errorf("expected %v, got %v", e, a) + return + } + + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) + })) + defer ts.Close() + + sess := unit.Session.Copy(&aws.Config{ + MaxRetries: aws.Int(0), + Endpoint: aws.String(ts.URL), + DisableSSL: aws.Bool(true), + S3ForcePathStyle: aws.Bool(true), + Region: aws.String("us-west-2"), + }) + handler, err := newKMSContextWrapEntryWithAnyCMK(kms.New(sess))(Envelope{WrapAlg: KMSContextWrap, CEKAlg: "AES/GCM/NoPadding", MatDesc: `{"aws:x-amz-cek-alg": "AES/GCM/NoPadding"}`}) + if err != nil { + t.Fatalf("expected no error, but received %v", err) + } + + plaintextKey, err := handler.DecryptKey([]byte{1, 2, 3, 4}) + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + if !bytes.Equal(key, plaintextKey) { + t.Errorf("expected %v, but received %v", key, plaintextKey) + } +} + +func TestKmsContextKeyHandler_decryptHandler_MismatchCEK(t *testing.T) { + _, err := newKMSContextWrapEntryWithAnyCMK(kms.New(unit.Session.Copy()))(Envelope{WrapAlg: KMSContextWrap, CEKAlg: "MismatchCEKValue", MatDesc: `{"aws:x-amz-cek-alg": "AES/GCM/NoPadding"}`}) + if err == nil { + t.Fatal("expected error, but got none") + } + + if e, a := "algorithm used at encryption time does not match the algorithm stored", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected error to contain %v, got %v", e, a) + } +} + +func TestKmsContextKeyHandler_decryptHandler_MissingContextKey(t *testing.T) { + _, err := newKMSContextWrapEntryWithAnyCMK(kms.New(unit.Session.Copy()))(Envelope{WrapAlg: KMSContextWrap, CEKAlg: "AES/GCM/NoPadding", MatDesc: `{}`}) + if err == nil { + t.Fatal("expected error, but got none") + } + + if e, a := "missing from encryption context", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected error to contain %v, got %v", e, a) + } +} + +func TestKmsContextKeyHandler_decryptHandler_MismatchWrap(t *testing.T) { + _, err := newKMSContextWrapEntryWithAnyCMK(kms.New(unit.Session.Copy()))(Envelope{WrapAlg: KMSWrap, CEKAlg: "AES/GCM/NoPadding", MatDesc: `{}`}) + if err == nil { + t.Fatal("expected error, but got none") + } + + if e, a := "x-amz-cek-alg value `kms` did not match the expected algorithm `kms+context` for this handler", err.Error(); !strings.Contains(a, e) { + t.Errorf("expected error to contain %v, got %v", e, a) + } +} + +func TestKmsContextKeyHandler_DecryptKey_WithCMK(t *testing.T) { + key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") + keyB64 := base64.URLEncoding.EncodeToString(key) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("expected no error, got %v", err) + w.WriteHeader(500) + return + } + + if !bytes.Contains(body, []byte(`"KeyId":"thisKey"`)) { + t.Errorf("expected CMK to be sent") + } + + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"thisKey","Plaintext":"`, keyB64, `"}`)) + })) + defer ts.Close() + + sess := unit.Session.Copy(&aws.Config{ + MaxRetries: aws.Int(0), + Endpoint: aws.String(ts.URL), + DisableSSL: aws.Bool(true), + S3ForcePathStyle: aws.Bool(true), + Region: aws.String("us-west-2"), + }) + handler, err := newKMSContextWrapEntryWithCMK(kms.New(sess), "thisKey")(Envelope{WrapAlg: KMSContextWrap, CEKAlg: "AES/GCM/NoPadding", MatDesc: `{"aws:x-amz-cek-alg": "AES/GCM/NoPadding"}`}) + if err != nil { + t.Errorf("expected no error, but received %v", err) + } + + _, err = handler.DecryptKey([]byte{1, 2, 3, 4}) + if err != nil { + t.Errorf("expected no error, but received %v", err) + } +} + +func TestRegisterKMSContextWrapWithAnyCMK(t *testing.T) { + kmsClient := kms.New(unit.Session.Copy()) + + cr := NewCryptoRegistry() + if err := RegisterKMSContextWrapWithAnyCMK(cr, kmsClient); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if wrap, ok := cr.GetWrap(KMSContextWrap); !ok { + t.Errorf("expected wrapped to be present") + } else if wrap == nil { + t.Errorf("expected wrap to not be nil") + } + + if err := RegisterKMSContextWrapWithCMK(cr, kmsClient, "test-key-id"); err == nil { + t.Error("expected error, got none") + } +} + +func TestRegisterKMSContextWrapWithCMK(t *testing.T) { + kmsClient := kms.New(unit.Session.Copy()) + + cr := NewCryptoRegistry() + if err := RegisterKMSContextWrapWithCMK(cr, kmsClient, "cmkId"); err != nil { + t.Errorf("expected no error, got %v", err) + } + + if wrap, ok := cr.GetWrap(KMSContextWrap); !ok { + t.Errorf("expected wrapped to be present") + } else if wrap == nil { + t.Errorf("expected wrap to not be nil") + } + + if err := RegisterKMSContextWrapWithAnyCMK(cr, kmsClient); err == nil { + t.Error("expected error, got none") + } +}
service/s3/s3crypto/kms_key_handler.go+96 −109 modified@@ -1,8 +1,6 @@ package s3crypto import ( - "fmt" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/kms" @@ -12,16 +10,15 @@ import ( const ( // KMSWrap is a constant used during decryption to build a KMS key handler. KMSWrap = "kms" - - // KMSContextWrap is a constant used during decryption to build a kms+context key handler - KMSContextWrap = "kms+context" ) // kmsKeyHandler will make calls to KMS to get the masterkey type kmsKeyHandler struct { - kms kmsiface.KMSAPI - cmkID *string - withContext bool + kms kmsiface.KMSAPI + cmkID *string + + // useProvidedCMK is toggled when using `kms` key wrapper with V2 client + useProvidedCMK bool CipherData } @@ -30,116 +27,118 @@ type kmsKeyHandler struct { // description. // // Example: -// sess := session.New(&aws.Config{}) +// sess := session.Must(session.NewSession()) // cmkID := "arn to key" // matdesc := s3crypto.MaterialDescription{} // handler := s3crypto.NewKMSKeyGenerator(kms.New(sess), cmkID) // -// deprecated: See NewKMSContextKeyGenerator +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func NewKMSKeyGenerator(kmsClient kmsiface.KMSAPI, cmkID string) CipherDataGenerator { return NewKMSKeyGeneratorWithMatDesc(kmsClient, cmkID, MaterialDescription{}) } -// NewKMSContextKeyGenerator builds a new kms+context key provider using the customer key ID and material -// description. -// -// Example: -// sess := session.New(&aws.Config{}) -// cmkID := "arn to key" -// matdesc := s3crypto.MaterialDescription{} -// handler := s3crypto.NewKMSContextKeyGenerator(kms.New(sess), cmkID) -func NewKMSContextKeyGenerator(client kmsiface.KMSAPI, cmkID string) CipherDataGeneratorWithCEKAlg { - return NewKMSContextKeyGeneratorWithMatDesc(client, cmkID, MaterialDescription{}) -} - -func newKMSKeyHandler(client kmsiface.KMSAPI, cmkID string, withContext bool, matdesc MaterialDescription) *kmsKeyHandler { +func newKMSKeyHandler(client kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) *kmsKeyHandler { // These values are read only making them thread safe kp := &kmsKeyHandler{ - kms: client, - cmkID: &cmkID, - withContext: withContext, + kms: client, + cmkID: &cmkID, } if matdesc == nil { matdesc = MaterialDescription{} } - // These values are read only making them thread safe - if kp.withContext { - kp.CipherData.WrapAlgorithm = KMSContextWrap - } else { - matdesc["kms_cmk_id"] = &cmkID - kp.CipherData.WrapAlgorithm = KMSWrap - } + matdesc["kms_cmk_id"] = &cmkID + + kp.CipherData.WrapAlgorithm = KMSWrap kp.CipherData.MaterialDescription = matdesc + return kp } // NewKMSKeyGeneratorWithMatDesc builds a new KMS key provider using the customer key ID and material // description. // // Example: -// sess := session.New(&aws.Config{}) +// sess := session.Must(session.NewSession()) // cmkID := "arn to key" // matdesc := s3crypto.MaterialDescription{} // handler := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc) // -// deprecated: See NewKMSContextKeyGeneratorWithMatDesc +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGenerator { - return newKMSKeyHandler(kmsClient, cmkID, false, matdesc) -} - -// NewKMSContextKeyGeneratorWithMatDesc builds a new kms+context key provider using the customer key ID and material -// description. -// -// Example: -// sess := session.New(&aws.Config{}) -// cmkID := "arn to key" -// matdesc := s3crypto.MaterialDescription{} -// handler := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc) -func NewKMSContextKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGeneratorWithCEKAlg { - return newKMSKeyHandler(kmsClient, cmkID, true, matdesc) + return newKMSKeyHandler(kmsClient, cmkID, matdesc) } // NewKMSWrapEntry builds returns a new KMS key provider and its decrypt handler. // // Example: -// sess := session.New(&aws.Config{}) +// sess := session.Must(session.NewSession()) // customKMSClient := kms.New(sess) // decryptHandler := s3crypto.NewKMSWrapEntry(customKMSClient) // // svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient) { // svc.WrapRegistry[s3crypto.KMSWrap] = decryptHandler // })) // -// deprecated: See NewKMSContextWrapEntry +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. func NewKMSWrapEntry(kmsClient kmsiface.KMSAPI) WrapEntry { - // These values are read only making them thread safe - kp := &kmsKeyHandler{ - kms: kmsClient, - } - + kp := newKMSWrapEntry(kmsClient) return kp.decryptHandler } -// NewKMSContextWrapEntry builds returns a new KMS key provider and its decrypt handler. +// RegisterKMSWrapWithCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be +// configured to call KMS Decrypt with the provided CMK. // // Example: -// sess := session.New(&aws.Config{}) -// customKMSClient := kms.New(sess) -// decryptHandler := s3crypto.NewKMSContextWrapEntry(customKMSClient) +// sess := session.Must(session.NewSession()) +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterKMSWrapWithCMK(cr, kms.New(sess), "cmkId"); err != nil { +// panic(err) // handle error +// } // -// svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient) { -// svc.WrapRegistry[s3crypto.KMSContextWrap] = decryptHandler -// })) -func NewKMSContextWrapEntry(kmsClient kmsiface.KMSAPI) WrapEntry { +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. +func RegisterKMSWrapWithCMK(registry *CryptoRegistry, client kmsiface.KMSAPI, cmkID string) error { + if registry == nil { + return errNilCryptoRegistry + } + return registry.AddWrap(KMSWrap, newKMSWrapEntryWithCMK(client, cmkID)) +} + +// RegisterKMSWrapWithAnyCMK registers the `kms` wrapping algorithm to the given WrapRegistry. The wrapper will be +// configured to call KMS Decrypt without providing a CMK. +// +// Example: +// sess := session.Must(session.NewSession()) +// cr := s3crypto.NewCryptoRegistry() +// if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kms.New(sess)); err != nil { +// panic(err) // handle error +// } +// +// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information. +func RegisterKMSWrapWithAnyCMK(registry *CryptoRegistry, client kmsiface.KMSAPI) error { + if registry == nil { + return errNilCryptoRegistry + } + return registry.AddWrap(KMSWrap, NewKMSWrapEntry(client)) +} + +// newKMSWrapEntryWithCMK builds returns a new KMS key provider and its decrypt handler. The wrap entry will be configured +// to only attempt to decrypt the data key using the provided CMK. +func newKMSWrapEntryWithCMK(kmsClient kmsiface.KMSAPI, cmkID string) WrapEntry { + kp := newKMSWrapEntry(kmsClient) + kp.useProvidedCMK = true + kp.cmkID = &cmkID + return kp.decryptHandler +} + +func newKMSWrapEntry(kmsClient kmsiface.KMSAPI) *kmsKeyHandler { // These values are read only making them thread safe kp := &kmsKeyHandler{ - kms: kmsClient, - withContext: true, + kms: kmsClient, } - return kp.decryptHandler + return kp } // decryptHandler initializes a KMS keyprovider with a material description. This @@ -151,17 +150,14 @@ func (kp kmsKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error return nil, err } - cmkID, ok := m["kms_cmk_id"] - if !kp.withContext && !ok { + _, ok := m["kms_cmk_id"] + if !ok { return nil, awserr.New("MissingCMKIDError", "Material description is missing CMK ID", nil) } kp.CipherData.MaterialDescription = m - kp.cmkID = cmkID kp.WrapAlgorithm = KMSWrap - if kp.withContext { - kp.WrapAlgorithm = KMSContextWrap - } + return &kp, nil } @@ -172,12 +168,18 @@ func (kp *kmsKeyHandler) DecryptKey(key []byte) ([]byte, error) { // DecryptKeyWithContext makes a call to KMS to decrypt the key with request context. func (kp *kmsKeyHandler) DecryptKeyWithContext(ctx aws.Context, key []byte) ([]byte, error) { - out, err := kp.kms.DecryptWithContext(ctx, - &kms.DecryptInput{ - EncryptionContext: kp.CipherData.MaterialDescription, - CiphertextBlob: key, - GrantTokens: []*string{}, - }) + in := &kms.DecryptInput{ + EncryptionContext: kp.MaterialDescription, + CiphertextBlob: key, + GrantTokens: []*string{}, + } + + // useProvidedCMK will be true if a constructor was used with the new V2 client + if kp.useProvidedCMK { + in.KeyId = kp.cmkID + } + + out, err := kp.kms.DecryptWithContext(ctx, in) if err != nil { return nil, err } @@ -190,31 +192,14 @@ func (kp *kmsKeyHandler) GenerateCipherData(keySize, ivSize int) (CipherData, er return kp.GenerateCipherDataWithContext(aws.BackgroundContext(), keySize, ivSize) } -func (kp kmsKeyHandler) GenerateCipherDataWithCEKAlg(keySize, ivSize int, cekAlgorithm string) (CipherData, error) { - return kp.GenerateCipherDataWithCEKAlgWithContext(aws.BackgroundContext(), keySize, ivSize, cekAlgorithm) -} - // GenerateCipherDataWithContext makes a call to KMS to generate a data key, // Upon making the call, it also sets the encrypted key. func (kp *kmsKeyHandler) GenerateCipherDataWithContext(ctx aws.Context, keySize, ivSize int) (CipherData, error) { - return kp.GenerateCipherDataWithCEKAlgWithContext(ctx, keySize, ivSize, "") -} - -func (kp kmsKeyHandler) GenerateCipherDataWithCEKAlgWithContext(ctx aws.Context, keySize int, ivSize int, cekAlgorithm string) (CipherData, error) { - md := kp.CipherData.MaterialDescription - - wrapAlgorithm := KMSWrap - if kp.withContext { - wrapAlgorithm = KMSContextWrap - if len(cekAlgorithm) == 0 { - return CipherData{}, fmt.Errorf("CEK algorithm identifier must not be empty") - } - md["aws:"+cekAlgorithmHeader] = &cekAlgorithm - } + cd := kp.CipherData.Clone() out, err := kp.kms.GenerateDataKeyWithContext(ctx, &kms.GenerateDataKeyInput{ - EncryptionContext: md, + EncryptionContext: cd.MaterialDescription, KeyId: kp.cmkID, KeySpec: aws.String("AES_256"), }) @@ -227,19 +212,21 @@ func (kp kmsKeyHandler) GenerateCipherDataWithCEKAlgWithContext(ctx aws.Context, return CipherData{}, err } - cd := CipherData{ - Key: out.Plaintext, - IV: iv, - WrapAlgorithm: wrapAlgorithm, - MaterialDescription: md, - EncryptedKey: out.CiphertextBlob, - } + cd.Key = out.Plaintext + cd.IV = iv + cd.EncryptedKey = out.CiphertextBlob + return cd, nil } -func (kp *kmsKeyHandler) isUsingDeprecatedFeatures() error { - if !kp.withContext { - return errDeprecatedCipherDataGenerator - } - return nil +func (kp kmsKeyHandler) isAWSFixture() bool { + return true } + +var ( + _ CipherDataGenerator = (*kmsKeyHandler)(nil) + _ CipherDataGeneratorWithContext = (*kmsKeyHandler)(nil) + _ CipherDataDecrypter = (*kmsKeyHandler)(nil) + _ CipherDataDecrypterWithContext = (*kmsKeyHandler)(nil) + _ awsFixture = (*kmsKeyHandler)(nil) +)
service/s3/s3crypto/kms_key_handler_test.go+55 −87 modified@@ -4,7 +4,6 @@ import ( "bytes" "encoding/base64" "encoding/hex" - "encoding/json" "fmt" "io/ioutil" "net/http" @@ -17,15 +16,15 @@ import ( "github.com/aws/aws-sdk-go/service/kms" ) -func TestBuildKMSEncryptHandler(t *testing.T) { +func TestNewKMSKeyGenerator(t *testing.T) { svc := kms.New(unit.Session) handler := NewKMSKeyGenerator(svc, "testid") if handler == nil { t.Error("expected non-nil handler") } } -func TestBuildKMSEncryptHandlerWithMatDesc(t *testing.T) { +func TestNewKMSKeyGeneratorWithMatDesc(t *testing.T) { svc := kms.New(unit.Session) handler := NewKMSKeyGeneratorWithMatDesc(svc, "testid", MaterialDescription{ "Testing": aws.String("123"), @@ -45,7 +44,7 @@ func TestBuildKMSEncryptHandlerWithMatDesc(t *testing.T) { } } -func TestKMSGenerateCipherData(t *testing.T) { +func TestKmsKeyHandler_GenerateCipherData(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, `{"CiphertextBlob":"AQEDAHhqBCCY1MSimw8gOGcUma79cn4ANvTtQyv9iuBdbcEF1QAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDJ6IcN5E4wVbk38MNAIBEIA7oF1E3lS7FY9DkoxPc/UmJsEwHzL82zMqoLwXIvi8LQHr8If4Lv6zKqY8u0+JRgSVoqCvZDx3p8Cn6nM=","KeyId":"arn:aws:kms:us-west-2:042062605278:key/c80a5cdb-8d09-4f9f-89ee-df01b2e3870a","Plaintext":"6tmyz9JLBE2yIuU7iXpArqpDVle172WSmxjcO6GNT7E="}`) })) @@ -77,10 +76,19 @@ func TestKMSGenerateCipherData(t *testing.T) { } } -func TestKMSDecrypt(t *testing.T) { +func TestKmsKeyHandler_DecryptKey(t *testing.T) { key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") keyB64 := base64.URLEncoding.EncodeToString(key) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("expected no error, got %v", err) + w.WriteHeader(500) + return + } + if bytes.Contains(body, []byte(`"KeyId":"test"`)) { + t.Errorf("expected CMK to not be sent") + } fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) })) defer ts.Close() @@ -92,7 +100,7 @@ func TestKMSDecrypt(t *testing.T) { S3ForcePathStyle: aws.Bool(true), Region: aws.String("us-west-2"), }) - handler, err := (kmsKeyHandler{kms: kms.New(sess)}).decryptHandler(Envelope{MatDesc: `{"kms_cmk_id":"test"}`}) + handler, err := (kmsKeyHandler{kms: kms.New(sess)}).decryptHandler(Envelope{WrapAlg: KMSWrap, MatDesc: `{"kms_cmk_id":"test"}`}) if err != nil { t.Errorf("expected no error, but received %v", err) } @@ -107,37 +115,22 @@ func TestKMSDecrypt(t *testing.T) { } } -func TestKMSContextGenerateCipherData(t *testing.T) { +func TestKmsKeyHandler_DecryptKey_WithCMK(t *testing.T) { + key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") + keyB64 := base64.URLEncoding.EncodeToString(key) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bodyBytes, err := ioutil.ReadAll(r.Body) + body, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(500) - return - } - var body map[string]interface{} - err = json.Unmarshal(bodyBytes, &body) - if err != nil { - w.WriteHeader(500) - return - } - - md, ok := body["EncryptionContext"].(map[string]interface{}) - if !ok { + t.Errorf("expected no error, got %v", err) w.WriteHeader(500) return } - exEncContext := map[string]interface{}{ - "aws:" + cekAlgorithmHeader: "cekAlgValue", - } - - if e, a := exEncContext, md; !reflect.DeepEqual(e, a) { - w.WriteHeader(500) - t.Errorf("expected %v, got %v", e, a) - return + if !bytes.Contains(body, []byte(`"KeyId":"thisKey"`)) { + t.Errorf("expected CMK to be sent") } - fmt.Fprintln(w, `{"CiphertextBlob":"AQEDAHhqBCCY1MSimw8gOGcUma79cn4ANvTtQyv9iuBdbcEF1QAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDJ6IcN5E4wVbk38MNAIBEIA7oF1E3lS7FY9DkoxPc/UmJsEwHzL82zMqoLwXIvi8LQHr8If4Lv6zKqY8u0+JRgSVoqCvZDx3p8Cn6nM=","KeyId":"arn:aws:kms:us-west-2:042062605278:key/c80a5cdb-8d09-4f9f-89ee-df01b2e3870a","Plaintext":"6tmyz9JLBE2yIuU7iXpArqpDVle172WSmxjcO6GNT7E="}`) + fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) })) defer ts.Close() @@ -148,79 +141,54 @@ func TestKMSContextGenerateCipherData(t *testing.T) { S3ForcePathStyle: aws.Bool(true), Region: aws.String("us-west-2"), }) - - svc := kms.New(sess) - handler := NewKMSContextKeyGenerator(svc, "testid") - - keySize := 32 - ivSize := 16 - - cd, err := handler.GenerateCipherDataWithCEKAlg(keySize, ivSize, "cekAlgValue") + handler, err := newKMSWrapEntryWithCMK(kms.New(sess), "thisKey")(Envelope{WrapAlg: KMSWrap, MatDesc: `{"kms_cmk_id":"test"}`}) if err != nil { t.Errorf("expected no error, but received %v", err) } - if keySize != len(cd.Key) { - t.Errorf("expected %d, but received %d", keySize, len(cd.Key)) + + plaintextKey, err := handler.DecryptKey([]byte{1, 2, 3, 4}) + if err != nil { + t.Errorf("expected no error, but received %v", err) } - if ivSize != len(cd.IV) { - t.Errorf("expected %d, but received %d", ivSize, len(cd.IV)) + if !bytes.Equal(key, plaintextKey) { + t.Errorf("expected %v, but received %v", key, plaintextKey) } } -func TestKMSContextDecrypt(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bodyBytes, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(500) - return - } - var body map[string]interface{} - err = json.Unmarshal(bodyBytes, &body) - if err != nil { - w.WriteHeader(500) - return - } +func TestRegisterKMSWrapWithAnyCMK(t *testing.T) { + kmsClient := kms.New(unit.Session.Copy()) - md, ok := body["EncryptionContext"].(map[string]interface{}) - if !ok { - w.WriteHeader(500) - return - } + cr := NewCryptoRegistry() + if err := RegisterKMSWrapWithAnyCMK(cr, kmsClient); err != nil { + t.Errorf("expected no error, got %v", err) + } - exEncContext := map[string]interface{}{ - "aws:" + cekAlgorithmHeader: "cekAlgValue", - } + if wrap, ok := cr.GetWrap(KMSWrap); !ok { + t.Errorf("expected wrapped to be present") + } else if wrap == nil { + t.Errorf("expected wrap to not be nil") + } - if e, a := exEncContext, md; !reflect.DeepEqual(e, a) { - w.WriteHeader(500) - t.Errorf("expected %v, got %v", e, a) - return - } + if err := RegisterKMSWrapWithCMK(cr, kmsClient, "test-key-id"); err == nil { + t.Error("expected error, got none") + } +} - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() +func TestRegisterKMSWrapWithCMK(t *testing.T) { + kmsClient := kms.New(unit.Session.Copy()) - sess := unit.Session.Copy(&aws.Config{ - MaxRetries: aws.Int(0), - Endpoint: aws.String(ts.URL), - DisableSSL: aws.Bool(true), - S3ForcePathStyle: aws.Bool(true), - Region: aws.String("us-west-2"), - }) - handler, err := NewKMSContextWrapEntry(kms.New(sess))(Envelope{MatDesc: `{"aws:x-amz-cek-alg": "cekAlgValue"}`}) - if err != nil { - t.Errorf("expected no error, but received %v", err) + cr := NewCryptoRegistry() + if err := RegisterKMSWrapWithCMK(cr, kmsClient, "cmkId"); err != nil { + t.Errorf("expected no error, got %v", err) } - plaintextKey, err := handler.DecryptKey([]byte{1, 2, 3, 4}) - if err != nil { - t.Errorf("expected no error, but received %v", err) + if wrap, ok := cr.GetWrap(KMSWrap); !ok { + t.Errorf("expected wrapped to be present") + } else if wrap == nil { + t.Errorf("expected wrap to not be nil") } - if !bytes.Equal(key, plaintextKey) { - t.Errorf("expected %v, but received %v", key, plaintextKey) + if err := RegisterKMSWrapWithAnyCMK(cr, kmsClient); err == nil { + t.Error("expected error, got none") } }
service/s3/s3crypto/mat_desc.go+20 −0 modified@@ -8,6 +8,18 @@ import ( // key has been used. type MaterialDescription map[string]*string +// Clone returns a copy of the MaterialDescription +func (md MaterialDescription) Clone() (clone MaterialDescription) { + if md == nil { + return nil + } + clone = make(MaterialDescription, len(md)) + for k, v := range md { + clone[k] = copyPtrString(v) + } + return clone +} + func (md *MaterialDescription) encodeDescription() ([]byte, error) { v, err := json.Marshal(&md) return v, err @@ -16,3 +28,11 @@ func (md *MaterialDescription) encodeDescription() ([]byte, error) { func (md *MaterialDescription) decodeDescription(b []byte) error { return json.Unmarshal(b, &md) } + +func copyPtrString(v *string) *string { + if v == nil { + return nil + } + ns := *v + return &ns +}
service/s3/s3crypto/mat_desc_test.go+31 −0 modified@@ -1,3 +1,5 @@ +// +build go1.7 + package s3crypto import ( @@ -33,3 +35,32 @@ func TestDecodeMaterialDescription(t *testing.T) { t.Error("expected material description to be equivalent, but received otherwise") } } + +func TestMaterialDescription_Clone(t *testing.T) { + tests := map[string]struct { + md MaterialDescription + wantClone MaterialDescription + }{ + "it handles nil": { + md: nil, + wantClone: nil, + }, + "it copies all values": { + md: MaterialDescription{ + "key1": aws.String("value1"), + "key2": aws.String("value2"), + }, + wantClone: MaterialDescription{ + "key1": aws.String("value1"), + "key2": aws.String("value2"), + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if gotClone := tt.md.Clone(); !reflect.DeepEqual(gotClone, tt.wantClone) { + t.Errorf("Clone() = %v, want %v", gotClone, tt.wantClone) + } + }) + } +}
service/s3/s3crypto/migrations_test.go+63 −6 modified@@ -24,12 +24,12 @@ func ExampleNewEncryptionClientV2_migration00() { // Usage of NewKMSKeyGenerator (kms) key wrapping algorithm must be migrated to NewKMSContextKeyGenerator (kms+context) key wrapping algorithm // // cipherDataGenerator := s3crypto.NewKMSKeyGenerator(kmsClient, cmkID) - cipherDataGenerator := s3crypto.NewKMSContextKeyGenerator(kmsClient, cmkID) + cipherDataGenerator := s3crypto.NewKMSContextKeyGenerator(kmsClient, cmkID, s3crypto.MaterialDescription{}) // Usage of AESCBCContentCipherBuilder (AES/CBC/PKCS5Padding) must be migrated to AESGCMContentCipherBuilder (AES/GCM/NoPadding) // // contentCipherBuilder := s3crypto.AESCBCContentCipherBuilder(cipherDataGenerator, s3crypto.AESCBCPadder) - contentCipherBuilder := s3crypto.AESGCMContentCipherBuilder(cipherDataGenerator) + contentCipherBuilder := s3crypto.AESGCMContentCipherBuilderV2(cipherDataGenerator) // Construction of an encryption client should be done using NewEncryptionClientV2 // @@ -59,9 +59,9 @@ func ExampleNewEncryptionClientV2_migration01() { kmsClient := kms.New(sess) cmkID := "1234abcd-12ab-34cd-56ef-1234567890ab" - cipherDataGenerator := s3crypto.NewKMSContextKeyGenerator(kmsClient, cmkID) + cipherDataGenerator := s3crypto.NewKMSContextKeyGenerator(kmsClient, cmkID, s3crypto.MaterialDescription{}) - contentCipherBuilder := s3crypto.AESGCMContentCipherBuilder(cipherDataGenerator) + contentCipherBuilder := s3crypto.AESGCMContentCipherBuilderV2(cipherDataGenerator) // Overriding of the encryption client options is possible by passing in functional arguments that override the // provided EncryptionClientOptions. @@ -98,7 +98,46 @@ func ExampleNewDecryptionClientV2_migration00() { // The V2 decryption client is able to decrypt object encrypted by the V1 client. // // decryptionClient := s3crypto.NewDecryptionClient(sess) - decryptionClient := s3crypto.NewDecryptionClientV2(sess) + + // The V2 decryption client requires you to explicitly register the key wrap algorithms and content encryption algorithms + // that you want to explicitly support decryption for. + registry := s3crypto.NewCryptoRegistry() + + kmsClient := kms.New(sess) + + // If you need support for unwrapping data keys wrapped using the `kms` wrap algorithm you can use RegisterKMSWrapWithAnyCMK. + // Alternatively you may use RegisterKMSWrapWithCMK if you wish to limit KMS decrypt calls to a specific CMK. + if err := s3crypto.RegisterKMSWrapWithAnyCMK(registry, kmsClient); err != nil { + fmt.Printf("error: %v", err) + return + } + + // For unwrapping data keys wrapped using the new `kms+context` key wrap algorithm you can use RegisterKMSContextWrapWithAnyCMK. + // Alternatively you may use RegisterKMSWrapWithCMK if you wish to limit KMS decrypt calls to a specific CMK. + if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(registry, kmsClient); err != nil { + fmt.Printf("error: %v", err) + return + } + + // If you need to decrypt objects encrypted using the V1 AES/CBC/PCKS5Padding cipher you can do so with RegisterAESCBCContentCipher + if err := s3crypto.RegisterAESCBCContentCipher(registry, s3crypto.AESCBCPadder); err != nil { + fmt.Printf("error: %v", err) + return + } + + // For decrypting objects encrypted in V1 or V2 using AES/GCM/NoPadding cipher you can do so with RegisterAESGCMContentCipher. + if err := s3crypto.RegisterAESGCMContentCipher(registry); err != nil { + fmt.Printf("error: %v", err) + return + } + + // Instantiate a new decryption client, and provided the Wrap, cek, and Padder that have been registered + // with your desired algorithms. + decryptionClient, err := s3crypto.NewDecryptionClientV2(sess, registry) + if err != nil { + fmt.Printf("error: %v", err) + return + } getObject, err := decryptionClient.GetObject(&s3.GetObjectInput{ Bucket: aws.String("your_bucket"), @@ -127,9 +166,27 @@ func ExampleNewDecryptionClientV2_migration01() { // decryptionClient := s3crypto.NewDecryptionClient(sess, func(o *s3crypto.DecryptionClient) { // o.S3Client = s3.New(sess, &aws.Config{Region: aws.String("us-west-2")}) //}) - decryptionClient := s3crypto.NewDecryptionClientV2(sess, func(o *s3crypto.DecryptionClientOptions) { + registry := s3crypto.NewCryptoRegistry() + + kmsClient := kms.New(sess) + if err := s3crypto.RegisterKMSWrapWithAnyCMK(registry, kmsClient); err != nil { + fmt.Printf("error: %v", err) + return + } + + // If you need to decrypt objects encrypted using AES/GCM/NoPadding cipher you can do so with RegisterAESGCMContentCipher + if err := s3crypto.RegisterAESGCMContentCipher(registry); err != nil { + fmt.Printf("error: %v", err) + return + } + + decryptionClient, err := s3crypto.NewDecryptionClientV2(sess, registry, func(o *s3crypto.DecryptionClientOptions) { o.S3Client = s3.New(sess, &aws.Config{Region: aws.String("us-west-2")}) }) + if err != nil { + fmt.Printf("error: %v", err) + return + } getObject, err := decryptionClient.GetObject(&s3.GetObjectInput{ Bucket: aws.String("your_bucket"),
service/s3/s3crypto/mock_test.go+75 −13 modified@@ -1,50 +1,93 @@ -package s3crypto_test +package s3crypto import ( "bytes" + "fmt" "io" "io/ioutil" - "github.com/aws/aws-sdk-go/service/s3/s3crypto" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/kms/kmsiface" ) type mockGenerator struct{} -func (m mockGenerator) GenerateCipherData(keySize, ivSize int) (s3crypto.CipherData, error) { - cd := s3crypto.CipherData{ +func (m mockGenerator) GenerateCipherData(keySize, ivSize int) (CipherData, error) { + cd := CipherData{ Key: make([]byte, keySize), IV: make([]byte, ivSize), } return cd, nil } -func (m mockGenerator) EncryptKey(key []byte) ([]byte, error) { - size := len(key) - b := bytes.Repeat([]byte{1}, size) - return b, nil +func (m mockGenerator) DecryptKey(key []byte) ([]byte, error) { + return make([]byte, 16), nil } -func (m mockGenerator) DecryptKey(key []byte) ([]byte, error) { +type mockGeneratorV2 struct{} + +func (m mockGeneratorV2) GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize int, ivSize int, cekAlg string) (CipherData, error) { + cd := CipherData{ + Key: make([]byte, keySize), + IV: make([]byte, ivSize), + } + return cd, nil +} + +func (m mockGeneratorV2) DecryptKey(key []byte) ([]byte, error) { return make([]byte, 16), nil } +func (m mockGeneratorV2) isEncryptionVersionCompatible(version clientVersion) error { + if version != v2ClientVersion { + return fmt.Errorf("mock error about version") + } + return nil +} + type mockCipherBuilder struct { - generator s3crypto.CipherDataGenerator + generator CipherDataGenerator } -func (builder mockCipherBuilder) ContentCipher() (s3crypto.ContentCipher, error) { +func (builder mockCipherBuilder) isEncryptionVersionCompatible(version clientVersion) error { + if version != v1ClientVersion { + return fmt.Errorf("mock error about version") + } + return nil +} + +func (builder mockCipherBuilder) ContentCipher() (ContentCipher, error) { cd, err := builder.generator.GenerateCipherData(32, 16) if err != nil { return nil, err } return &mockContentCipher{cd}, nil } +type mockCipherBuilderV2 struct { + generator CipherDataGeneratorWithCEKAlg +} + +func (builder mockCipherBuilderV2) isEncryptionVersionCompatible(version clientVersion) error { + if version != v2ClientVersion { + return fmt.Errorf("mock error about version") + } + return nil +} + +func (builder mockCipherBuilderV2) ContentCipher() (ContentCipher, error) { + cd, err := builder.generator.GenerateCipherDataWithCEKAlg(aws.BackgroundContext(), 32, 16, "mock-cek-alg") + if err != nil { + return nil, err + } + return &mockContentCipher{cd}, nil +} + type mockContentCipher struct { - cd s3crypto.CipherData + cd CipherData } -func (cipher *mockContentCipher) GetCipherData() s3crypto.CipherData { +func (cipher *mockContentCipher) GetCipherData() CipherData { return cipher.cd } @@ -66,3 +109,22 @@ func (cipher *mockContentCipher) DecryptContents(src io.ReadCloser) (io.ReadClos size := len(b) return ioutil.NopCloser(bytes.NewReader(make([]byte, size))), nil } + +type mockKMS struct { + kmsiface.KMSAPI +} + +type mockPadder struct { +} + +func (m mockPadder) Pad(i []byte, i2 int) ([]byte, error) { + return i, nil +} + +func (m mockPadder) Unpad(i []byte) ([]byte, error) { + return i, nil +} + +func (m mockPadder) Name() string { + return "mockPadder" +}
service/s3/s3crypto/shared_client.go+16 −9 modified@@ -13,6 +13,12 @@ import ( "github.com/aws/aws-sdk-go/service/s3" ) +// clientConstructionErrorCode is used for operations that can't be completed due to invalid client construction +const clientConstructionErrorCode = "ClientConstructionError" + +// mismatchWrapError is an error returned if a wrapping handler receives an unexpected envelope +var mismatchWrapError = awserr.New(clientConstructionErrorCode, "wrap algorithm provided did not match handler", nil) + func putObjectRequest(c EncryptionClientOptions, input *s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) { req, out := c.S3Client.PutObjectRequest(input) @@ -45,9 +51,9 @@ func putObjectRequest(c EncryptionClientOptions, input *s3.PutObjectInput) (*req return } - md5 := newMD5Reader(input.Body) + lengthReader := newContentLengthReader(input.Body) sha := newSHA256Writer(dst) - reader, err := encryptor.EncryptContents(md5) + reader, err := encryptor.EncryptContents(lengthReader) if err != nil { r.Error = err return @@ -60,7 +66,7 @@ func putObjectRequest(c EncryptionClientOptions, input *s3.PutObjectInput) (*req } data := encryptor.GetCipherData() - env, err := encodeMeta(md5, data) + env, err := encodeMeta(lengthReader, data) if err != nil { r.Error = err return @@ -101,7 +107,7 @@ func getObjectRequest(options DecryptionClientOptions, input *s3.GetObjectInput) return } - // If KMS should return the correct CEK algorithm with the proper + // If KMS should return the correct cek algorithm with the proper // KMS key provider cipher, err := contentCipherFromEnvelope(options, r.Context(), env) if err != nil { @@ -143,8 +149,9 @@ func contentCipherFromEnvelope(options DecryptionClientOptions, ctx aws.Context, } func wrapFromEnvelope(options DecryptionClientOptions, env Envelope) (CipherDataDecrypter, error) { - f, ok := options.WrapRegistry[env.WrapAlg] + f, ok := options.CryptoRegistry.GetWrap(env.WrapAlg) if !ok || f == nil { + return nil, awserr.New( "InvalidWrapAlgorithmError", "wrap algorithm isn't supported, "+env.WrapAlg, @@ -155,7 +162,7 @@ func wrapFromEnvelope(options DecryptionClientOptions, env Envelope) (CipherData } func cekFromEnvelope(options DecryptionClientOptions, ctx aws.Context, env Envelope, decrypter CipherDataDecrypter) (ContentCipher, error) { - f, ok := options.CEKRegistry[env.CEKAlg] + f, ok := options.CryptoRegistry.GetCEK(env.CEKAlg) if !ok || f == nil { return nil, awserr.New( "InvalidCEKAlgorithmError", @@ -197,11 +204,11 @@ func cekFromEnvelope(options DecryptionClientOptions, ctx aws.Context, env Envel // If there wasn't a cek algorithm specific padder, we check the padder itself. // We return a no unpadder, if no unpadder was found. This means any customization // either contained padding within the cipher implementation, and to maintain -// backwards compatility we will simply not unpad anything. +// backwards compatibility we will simply not unpad anything. func getPadder(options DecryptionClientOptions, cekAlg string) Padder { - padder, ok := options.PadderRegistry[cekAlg] + padder, ok := options.CryptoRegistry.GetPadder(cekAlg) if !ok { - padder, ok = options.PadderRegistry[cekAlg[strings.LastIndex(cekAlg, "/")+1:]] + padder, ok = options.CryptoRegistry.GetPadder(cekAlg[strings.LastIndex(cekAlg, "/")+1:]) if !ok { return NoPadder }
service/s3/s3crypto/strategy.go+0 −1 modified@@ -118,7 +118,6 @@ func (load HeaderV2LoadStrategy) Load(req *request.Request) (Envelope, error) { env.WrapAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, wrapAlgorithmHeader}, "-")) env.CEKAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, cekAlgorithmHeader}, "-")) env.TagLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, tagLengthHeader}, "-")) - env.UnencryptedMD5 = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedMD5Header}, "-")) env.UnencryptedContentLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedContentLengthHeader}, "-")) return env, nil }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-f5pg-7wfw-84q9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-8911ghsaADVISORY
- aws.amazon.com/blogs/developer/updates-to-the-amazon-s3-encryption-client/ghsax_refsource_CONFIRMWEB
- bugzilla.redhat.com/show_bug.cgighsaWEB
- github.com/aws/aws-sdk-go/commit/1e84382fa1c0086362b5a4b68e068d4f8518d40eghsaWEB
- github.com/aws/aws-sdk-go/commit/ae9b9fd92af132cfd8d879809d8611825ba135f4ghsaWEB
- github.com/aws/aws-sdk-go/pull/3403ghsaWEB
- github.com/google/security-research/security/advisories/GHSA-f5pg-7wfw-84q9ghsax_refsource_CONFIRMWEB
- github.com/sophieschmieg/exploits/tree/master/aws_s3_crypto_pocghsaWEB
- pkg.go.dev/vuln/GO-2022-0646ghsaWEB
News mentions
0No linked articles in our index yet.