123456789_123456789_123456789_123456789_123456789_

Class: Mongo::ClientEncryption

Relationships & Source Files
Inherits: Object
Defined in: lib/mongo/client_encryption.rb

Overview

ClientEncryption encapsulates explicit operations on a key vault collection that cannot be done directly on a MongoClient. It provides an API for explicitly encrypting and decrypting values, and creating data keys.

Class Method Summary

Instance Method Summary

Constructor Details

.new(key_vault_client, options = {}) ⇒ ClientEncryption

Create a new ClientEncryption object with the provided options.

Parameters:

  • key_vault_client (Mongo::Client)

    A Mongo::Client that is connected to the MongoDB instance where the key vault collection is stored.

  • options (Hash) (defaults to: {})

    The ClientEncryption options.

Options Hash (options):

  • :key_vault_namespace (String)

    The name of the key vault collection in the format "database.collection".

  • :kms_providers (Hash)

    A hash of key management service configuration information. @see Mongo::Crypt::KMS::Credentials for list of options for every supported provider. @note There may be more than one KMS provider specified.

  • :kms_tls_options (Hash)

    TLS options to connect to KMS providers. Keys of the hash should be KSM provider names; values should be hashes of TLS connection options. The options are equivalent to TLS connection options of Client. @see Mongo::Client#initialize for list of TLS options.

  • :timeout_ms (Integer)

    The operation timeout in milliseconds. Must be a non-negative integer. An explicit value of 0 means infinite. The default value is unset which means the feature is disabled.

  • :key_expiration_ms (Integer)

    The lifetime of the data encryption key cache, in milliseconds. Must be a non-negative integer. An explicit value of 0 means the cache never expires. The default is 60000.

Raises:

  • (ArgumentError)

    If required options are missing or incorrectly formatted.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 52

def initialize(key_vault_client, options = {})
  @encrypter = Crypt::ExplicitEncrypter.new(
    key_vault_client,
    options[:key_vault_namespace],
    Crypt::KMS::Credentials.new(options[:kms_providers]),
    Crypt::KMS::Validations.validate_tls_options(options[:kms_tls_options]),
    options[:timeout_ms],
    options[:key_expiration_ms]
  )
end

Instance Method Details

#add_key_alt_name(id, key_alt_name) ⇒ BSON::Document | nil

Adds a key_alt_name for the key in the key vault collection with the given id.

Parameters:

  • id (BSON::Binary)

    Id of the key to add new key alt name.

  • key_alt_name (String)

    New key alt name to add.

Returns:

  • (BSON::Document | nil)

    Document describing the identified key before adding the key alt name, or nil if no such key.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 195

def add_key_alt_name(id, key_alt_name)
  @encrypter.add_key_alt_name(id, key_alt_name)
end

#create_data_key(kms_provider, options = {}) ⇒ BSON::Binary

Generates a data key used for encryption/decryption and stores that key in the KMS collection. The generated key is encrypted with the KMS master key.

Parameters:

  • kms_provider (String)

    The KMS provider to use. Valid values are "aws" and "local".

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :master_key (Hash)

    Information about the AWS master key. Required if kms_provider is "aws".

    • :region [ String ] The The AWS region of the master key (required).
    • :key [ String ] The Amazon Resource Name (ARN) of the master key (required).
    • :endpoint [ String ] An alternate host to send KMS requests to (optional). endpoint should be a host name with an optional port number separated by a colon (e.g. "kms.us-east-1.amazonaws.com" or "kms.us-east-1.amazonaws.com:443"). An endpoint in any other format will not be properly parsed.
  • :key_alt_names (Array<String>)

    An optional array of strings specifying alternate names for the new data key.

  • :key_material (String | nil)

    Optional 96 bytes to use as custom key material for the data key being created. If :key_material option is given, the custom key material is used for encrypting and decrypting data.

Returns:

  • (BSON::Binary)

    The 16-byte UUID of the new data key as a BSON::Binary object with type :uuid.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 89

def create_data_key(kms_provider, options = {})
  key_document = Crypt::KMS::MasterKeyDocument.new(kms_provider, options)

  key_alt_names = options[:key_alt_names]
  key_material = options[:key_material]
  @encrypter.create_and_insert_data_key(key_document, key_alt_names, key_material)
end

#create_data_keys(encrypted_fields, kms_provider, master_key) ⇒ Hash (private)

Create data keys for fields in encrypted_fields that has :keyId key, but the value is nil.

Parameters:

  • encrypted_fields (Hash)

    Encrypted fields map.

  • kms_provider (String)

    KMS provider to encrypt fields.

  • master_key (Hash | nil)

    Document describing master key to encrypt fields.

Returns:

  • (Hash)

    Encrypted fields map with keyIds for fields that did not have one.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 306

def create_data_keys(encrypted_fields, kms_provider, master_key)
  encrypted_fields = encrypted_fields.dup
  # We must return the partially formed encrypted_fields hash if an error
  # occurs - https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#create-encrypted-collection-helper
  # Thefore, we do this in a loop instead of using #map.
  encrypted_fields[:fields].size.times do |i|
    field = encrypted_fields[:fields][i]
    next unless field.is_a?(Hash) && field.fetch(:keyId, false).nil?

    begin
      encrypted_fields[:fields][i][:keyId] = create_data_key(kms_provider, master_key: master_key)
    rescue Error::CryptError => e
      raise Error::CryptError, "Error creating data key for field #{field[:path]} \
          with encrypted fields #{encrypted_fields}: #{e.class}: #{e.message}"
    end
  end
  encrypted_fields
end

#create_encrypted_collection(database, coll_name, coll_opts, kms_provider, master_key) ⇒ Array<Operation::Result, Hash>

Note:

This method does not update the :encrypted_fields_map in the client's :auto_encryption_options. Therefore, in order to use the collection created by this method with automatic encryption, the user must create a new client after calling this function with the :encrypted_fields returned.

Create collection with encrypted fields.

If :encryption_fields contains a keyId with a null value, a data key will be automatically generated and assigned to keyId value.

Parameters:

  • database (Mongo::Database)

    Database to create collection in.

  • coll_name (String)

    Name of collection to create.

  • coll_opts (Hash)

    Options for collection to create.

  • kms_provider (String)

    KMS provider to encrypt fields.

  • master_key (Hash | nil)

    Document describing master key to encrypt fields.

Returns:

  • (Array<Operation::Result, Hash>)

    The result of the create collection operation and the encrypted fields map used to create the collection.

Raises:

  • (ArgumentError)
[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 282

def create_encrypted_collection(database, coll_name, coll_opts, kms_provider, master_key)
  raise ArgumentError, 'coll_opts must contain :encrypted_fields' unless coll_opts[:encrypted_fields]

  encrypted_fields = create_data_keys(coll_opts[:encrypted_fields], kms_provider, master_key)
  begin
    new_coll_opts = coll_opts.dup.merge(encrypted_fields: encrypted_fields)
    [ database[coll_name].create(new_coll_opts), encrypted_fields ]
  rescue Mongo::Error => e
    raise Error::CryptError, "Error creating collection with encrypted fields \
          #{encrypted_fields}: #{e.class}: #{e.message}"
  end
end

#decrypt(value) ⇒ Object

Decrypts a value that has already been encrypted.

Parameters:

  • value (BSON::Binary)

    A BSON Binary object of subtype 6 (ciphertext) that will be decrypted.

Returns:

  • (Object)

    The decrypted value.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 184

def decrypt(value)
  @encrypter.decrypt(value)
end

#delete_key(id) ⇒ Operation::Result

Removes the key with the given id from the key vault collection.

Parameters:

  • id (BSON::Binary)

    Id of the key to delete.

Returns:

  • (Operation::Result)

    The response from the database for the delete_one operation that deletes the key.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 205

def delete_key(id)
  @encrypter.delete_key(id)
end

#encrypt(value, options = {}) ⇒ BSON::Binary

Note:

The "substring" query type is unstable and subject to backwards breaking changes.

Note:

The :key_id and :key_alt_name options are mutually exclusive. Only one is required to perform explicit encryption.

Encrypts a value using the specified encryption key and algorithm.

Parameters:

  • value (Object)

    The value to encrypt.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :key_id (BSON::Binary)

    A BSON::Binary object of type :uuid representing the UUID of the encryption key as it is stored in the key vault collection.

  • :key_alt_name (String)

    The alternate name for the encryption key.

  • :algorithm (String)

    The algorithm used to encrypt the value. Valid algorithms are "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "String".

  • :contention_factor (Integer | nil)

    Contention factor to be applied if encryption algorithm is set to "Indexed" or "String". If not provided, it defaults to a value of 0. Contention factor should be set only if encryption algorithm is set to "Indexed" or "String".

  • query_type (String | nil)

    Query type to be applied if encryption algorithm is set to "Indexed" or "String". Allowed values are "equality" (for "Indexed") and "prefix", "suffix", "substring" (for "String").

  • :string_opts (Hash | nil)

    Specifies index options for a Queryable Encryption field supporting "prefix", "suffix", or "substring" queries. Required when algorithm is "String". Allowed options are :case_sensitive, :diacritic_sensitive, :prefix, :suffix, :substring.

Returns:

  • (BSON::Binary)

    A BSON Binary object of subtype 6 (ciphertext) representing the encrypted value.

Raises:

  • (ArgumentError)

    if either contention_factor or query_type is set, and algorithm is not "Indexed".

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 134

def encrypt(value, options = {})
  @encrypter.encrypt(value, options)
end

#encrypt_expression(expression, options = {}) ⇒ BSON::Binary

Note:

The :key_id and :key_alt_name options are mutually exclusive. Only one is required to perform explicit encryption.

Encrypts a Match Expression or Aggregate Expression to query a range index.

Only supported when queryType is "range" and algorithm is "Range". @note: The Range algorithm is experimental only. It is not intended for public use. It is subject to breaking changes.

@param [ Hash ] options

Examples:

Encrypt Match Expression.

encryption.encrypt_expression(
  {'$and' =>  [{'field' => {'$gt' => 10}}, {'field' =>  {'$lt' => 20 }}]}
)

Encrypt Aggregate Expression.

encryption.encrypt_expression(
  {'$and' =>  [{'$gt' => ['$field', 10]}, {'$lt' => ['$field', 20]}}
)
{$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]

Parameters:

  • expression (Hash)

    Expression to encrypt.

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :key_id (BSON::Binary)

    A BSON::Binary object of type :uuid representing the UUID of the encryption key as it is stored in the key vault collection.

  • :key_alt_name (String)

    The alternate name for the encryption key.

  • :algorithm (String)

    The algorithm used to encrypt the expression. The only allowed value is "Range"

  • :contention_factor (Integer | nil)

    Contention factor to be applied If not provided, it defaults to a value of 0.

  • query_type (String | nil)

    Query type to be applied. The only allowed value is "range".

Returns:

  • (BSON::Binary)

    A BSON Binary object of subtype 6 (ciphertext) representing the encrypted expression.

Raises:

  • (ArgumentError)

    if disallowed values in options are set.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 174

def encrypt_expression(expression, options = {})
  @encrypter.encrypt_expression(expression, options)
end

#get_key(id) ⇒ BSON::Document | nil

Finds a single key with the given id.

Parameters:

  • id (BSON::Binary)

    Id of the key to get.

Returns:

  • (BSON::Document | nil)

    The found key document or nil if not found.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 215

def get_key(id)
  @encrypter.get_key(id)
end

#get_key_by_alt_name(key_alt_name) ⇒ BSON::Document | nil

Returns a key in the key vault collection with the given key_alt_name.

Parameters:

  • key_alt_name (String)

    Key alt name to find a key.

Returns:

  • (BSON::Document | nil)

    The found key document or nil if not found.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 225

def get_key_by_alt_name(key_alt_name)
  @encrypter.get_key_by_alt_name(key_alt_name)
end

#get_keysCollection::View Also known as: #keys

Returns all keys in the key vault collection.

Returns:

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 232

def get_keys
  @encrypter.get_keys
end

#keys

Alias for #get_keys.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 235

alias keys get_keys

#remove_key_alt_name(id, key_alt_name) ⇒ BSON::Document | nil

Removes a key_alt_name from a key in the key vault collection with the given id.

Parameters:

  • id (BSON::Binary)

    Id of the key to remove key alt name.

  • key_alt_name (String)

    Key alt name to remove.

Returns:

  • (BSON::Document | nil)

    Document describing the identified key before removing the key alt name, or nil if no such key.

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 244

def remove_key_alt_name(id, key_alt_name)
  @encrypter.remove_key_alt_name(id, key_alt_name)
end

#rewrap_many_data_key(filter, opts = {}) ⇒ Crypt::RewrapManyDataKeyResult

Decrypts multiple data keys and (re-)encrypts them with a new master_key, or with their current master_key if a new one is not given.

Parameters:

  • filter (Hash)

    Filter used to find keys to be updated.

  • options (Hash)

Returns:

[ GitHub ]

  
# File 'lib/mongo/client_encryption.rb', line 259

def rewrap_many_data_key(filter, opts = {})
  @encrypter.rewrap_many_data_key(filter, opts)
end