Module: Aws::S3::EncryptionV3

Defined in:
lib/aws-sdk-s3/encryptionV3/client.rb,
lib/aws-sdk-s3/encryption_v3.rb,
lib/aws-sdk-s3/encryptionV3/utils.rb,
lib/aws-sdk-s3/encryptionV3/errors.rb,
lib/aws-sdk-s3/encryptionV3/materials.rb,
lib/aws-sdk-s3/encryptionV3/decryption.rb,
lib/aws-sdk-s3/encryptionV3/io_decrypter.rb,
lib/aws-sdk-s3/encryptionV3/io_encrypter.rb,
lib/aws-sdk-s3/encryptionV3/key_provider.rb,
lib/aws-sdk-s3/encryptionV3/decrypt_handler.rb,
lib/aws-sdk-s3/encryptionV3/encrypt_handler.rb,
lib/aws-sdk-s3/encryptionV3/io_auth_decrypter.rb,
lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb,
lib/aws-sdk-s3/encryptionV3/default_key_provider.rb,
lib/aws-sdk-s3/encryptionV3/default_cipher_provider.rb

Overview

Provides an encryption client that encrypts and decrypts data client-side, storing the encrypted data in Amazon S3. The ‘EncryptionV3::Client` (V3 Client) provides improved security over the `EncryptionV2::Client` (V2 Client) through key commitment. You can use the V3 Client to continue decrypting objects encrypted by V2 by setting security_profile: :v3_and_legacy. The latest V2 Client also supports reading and decrypting objects encrypted by the V3 Client.

This client uses a process called “envelope encryption”. Your private encryption keys and your data’s plain-text are never sent to Amazon S3. **If you lose you encryption keys, you will not be able to decrypt your data.**

## Key Commitment

Key commitment (also known as robustness) is a security property that guarantees that each ciphertext can be decrypted to only a single plaintext. This prevents sophisticated attacks where a ciphertext could theoretically decrypt to different plaintexts under different keys.

The V3 client encrypts with key commitment by default using the ‘:alg_aes_256_gcm_hkdf_sha512_commit_key` algorithm. Key commitment adds approximately 32 bytes to each encrypted object and slightly increases processing time, but significantly enhances security.

## Envelope Encryption Overview

The goal of envelope encryption is to combine the performance of fast symmetric encryption while maintaining the secure key management that asymmetric keys provide.

A one-time-use symmetric key (envelope key) is generated client-side. This is used to encrypt the data client-side. This key is then encrypted by your master key and stored alongside your data in Amazon S3.

When accessing your encrypted data with the encryption client, the encrypted envelope key is retrieved and decrypted client-side with your master key. The envelope key is then used to decrypt the data client-side.

One of the benefits of envelope encryption is that if your master key is compromised, you have the option of just re-encrypting the stored envelope symmetric keys, instead of re-encrypting all of the data in your account.

## Basic Usage

The encryption client requires an Client. If you do not provide a ‘:client`, then a client will be constructed for you.

require 'openssl'
key = OpenSSL::PKey::RSA.new(1024)

# encryption client
s3 = Aws::S3::EncryptionV3::Client.new(
  encryption_key: key,
  key_wrap_schema: :rsa_oaep_sha1 # the key_wrap_schema must be rsa_oaep_sha1 for asymmetric keys
)

# round-trip an object, encrypted/decrypted locally
s3.put_object(bucket:'aws-sdk', key:'secret', body:'handshake')
s3.get_object(bucket:'aws-sdk', key:'secret').body.read
#=> 'handshake'

# reading encrypted object without the encryption client
# results in the getting the cipher text
Aws::S3::Client.new.get_object(bucket:'aws-sdk', key:'secret').body.read
#=> "... cipher text ..."

## Required Configuration

You must configure the following:

  • a key or key provider - See the Keys section below. The key provided determines the key wrapping schema(s) supported for both encryption and decryption.

  • ‘key_wrap_schema` - The key wrapping schema. It must match the type of key configured.

The following have defaults and are optional:

  • ‘content_encryption_schema` - Defaults to `:alg_aes_256_gcm_hkdf_sha512_commit_key`

  • ‘security_profile` - Defaults to `:v3`. Set to `:v3_and_legacy` to read V2-encrypted objects.

  • ‘commitment_policy` - Defaults to `:require_encrypt_require_decrypt` (most secure)

## Keys

For client-side encryption to work, you must provide one of the following:

  • An encryption key

  • A KeyProvider

  • A KMS encryption key id

Additionally, the key wrapping schema must agree with the type of the key:

  • :aes_gcm: An AES encryption key or a key provider.

  • :rsa_oaep_sha1: An RSA encryption key or key provider.

  • :kms_context: A KMS encryption key id

### An Encryption Key

You can pass a single encryption key. This is used as a master key encrypting and decrypting all object keys.

key = OpenSSL::Cipher.new("AES-256-ECB").random_key # symmetric key - used with `key_wrap_schema: :aes_gcm`
key = OpenSSL::PKey::RSA.new(1024) # asymmetric key pair - used with `key_wrap_schema: :rsa_oaep_sha1`

s3 = Aws::S3::EncryptionV3::Client.new(
  encryption_key: key,
  key_wrap_schema: :aes_gcm # or :rsa_oaep_sha1 if using RSA
)

### Key Provider

Alternatively, you can use a KeyProvider. A key provider makes it easy to work with multiple keys and simplifies key rotation.

### KMS Encryption Key Id

If you pass the id of an AWS Key Management Service (KMS) key and use :kms_content for the key_wrap_schema, then KMS will be used to generate, encrypt and decrypt object keys.

# keep track of the kms key id
kms = Aws::KMS::Client.new
key_id = kms.create_key..key_id

Aws::S3::EncryptionV3::Client.new(
  kms_key_id: key_id,
  kms_client: kms,
  key_wrap_schema: :kms_context
)

## Custom Key Providers

A KeyProvider is any object that responds to:

  • ‘#encryption_materials`

  • ‘#key_for(materials_description)`

Here is a trivial implementation of an in-memory key provider. This is provided as a demonstration of the key provider interface, and should not be used in production:

class KeyProvider

  def initialize(default_key_name, keys)
    @keys = keys
    @encryption_materials = Aws::S3::EncryptionV3::Materials.new(
      key: @keys[default_key_name],
      description: JSON.dump(key: default_key_name),
    )
  end

  attr_reader :encryption_materials

  def key_for(matdesc)
    key_name = JSON.parse(matdesc)['key']
    if key = @keys[key_name]
      key
    else
      raise "encryption key not found for: #{matdesc.inspect}"
    end
  end
end

Given the above key provider, you can create an encryption client that chooses the key to use based on the materials description stored with the encrypted object. This makes it possible to use multiple keys and simplifies key rotation.

# uses "new-key" for encrypting objects, uses either for decrypting
keys = KeyProvider.new('new-key', {
  "old-key" => Base64.decode64("kM5UVbhE/4rtMZJfsadYEdm2vaKFsmV2f5+URSeUCV4="),
  "new-key" => Base64.decode64("w1WLio3agRWRTSJK/Ouh8NHoqRQ6fn5WbSXDTHjXMSo="),
}),

# chooses the key based on the materials description stored
# with the encrypted object
s3 = Aws::S3::EncryptionV3::Client.new(
  key_provider: keys,
  key_wrap_schema: :aes_gcm # or :rsa_oaep_sha1 for RSA keys
)

## Materials Description

A materials description is JSON document string that is stored in the metadata (or instruction file) of an encrypted object. The DefaultKeyProvider uses the empty JSON document ‘“{}”`.

When building a key provider, you are free to store whatever information you need to identify the master key that was used to encrypt the object.

## Envelope Location

By default, the encryption client store the encryption envelope with the object, as metadata. You can choose to have the envelope stored in a separate “instruction file”. An instruction file is an object, with the key of the encrypted object, suffixed with ‘“.instruction”`.

Specify the ‘:envelope_location` option as `:instruction_file` to use an instruction file for storing the envelope.

# default behavior
s3 = Aws::S3::EncryptionV3::Client.new(
  encryption_key: your_key,
  key_wrap_schema: :aes_gcm,
  envelope_location: :metadata
)

# store envelope in a separate object
s3 = Aws::S3::EncryptionV3::Client.new(
  encryption_key: your_key,
  key_wrap_schema: :aes_gcm,
  envelope_location: :instruction_file,
  instruction_file_suffix: '.instruction' # default
)

When using an instruction file, multiple requests are made when putting and getting the object. **This may cause issues if you are issuing concurrent PUT and GET requests to an encrypted object.**

## Commitment Policies Explained

  • ‘:forbid_encrypt_allow_decrypt` - Encrypts without key commitment (for backward compatibility with systems that have not been updated), but can decrypt objects with or without commitment. Use if you are not sure that all readers can decrypt objects encrypted with key commitment.

  • ‘:require_encrypt_allow_decrypt` - Encrypts with key commitment, can decrypt objects with or without commitment. Use once all readers can decrypt objects encrypted with key commitment.

  • ‘:require_encrypt_require_decrypt` - Encrypts with key commitment, can only decrypt objects with key commitment. **Recommended for new applications and after migrations are complete.** This is the default.

Defined Under Namespace

Modules: Errors, KeyProvider, Utils Classes: Client, DecryptHandler, Decryption, DefaultCipherProvider, DefaultKeyProvider, EncryptHandler, IOAuthDecrypter, IODecrypter, IOEncrypter, KmsCipherProvider, Materials

Constant Summary collapse

AES_GCM_TAG_LEN_BYTES =
16
EC_USER_AGENT =
'S3CryptoV3'