123456789_123456789_123456789_123456789_123456789_

Class: Mongo::Crypt::Handle Private

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: lib/mongo/crypt/handle.rb

Overview

A handle to the libmongocrypt library that wraps a mongocrypt_t object, allowing clients to set options on that object or perform operations such as encryption and decryption

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#crypt_shared_lib_available?Boolean (readonly)

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 151

def crypt_shared_lib_available?
  crypt_shared_lib_version != 0
end

#kms_providersCrypt::KMS::Credentials (readonly)

Returns:

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 29

attr_reader :kms_providers

Instance Method Details

#crypt_shared_lib_version

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 147

def crypt_shared_lib_version
  Binding.crypt_shared_lib_version(self)
end

#do_aes(key_binary_p, iv_binary_p, input_binary_p, output_binary_p, response_length_p, status_p, decrypt: false, mode: :CBC) (private)

Perform AES encryption or decryption and write the output to the provided mongocrypt_binary_t object.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 257

def do_aes(key_binary_p, iv_binary_p, input_binary_p, output_binary_p,
           response_length_p, status_p, decrypt: false, mode: :CBC)
  key = Binary.from_pointer(key_binary_p).to_s
  iv = Binary.from_pointer(iv_binary_p).to_s
  input = Binary.from_pointer(input_binary_p).to_s

  write_binary_string_and_set_status(output_binary_p, status_p) do
    output = Hooks.aes(key, iv, input, decrypt: decrypt, mode: mode)
    response_length_p.write_int(output.bytesize)

    output
  end
end

#do_hmac_sha(digest_name, key_binary_p, input_binary_p, output_binary_p, status_p) (private)

Perform HMAC SHA encryption and write the output to the provided mongocrypt_binary_t object.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 273

def do_hmac_sha(digest_name, key_binary_p, input_binary_p,
                output_binary_p, status_p)
  key = Binary.from_pointer(key_binary_p).to_s
  input = Binary.from_pointer(input_binary_p).to_s

  write_binary_string_and_set_status(output_binary_p, status_p) do
    Hooks.hmac_sha(digest_name, key, input)
  end
end

#do_rsaes_pkcs_signature(key_binary_p, input_binary_p, output_binary_p, status_p) (private)

Perform signing using RSASSA-PKCS1-v1_5 with SHA256 hash and write the output to the provided mongocrypt_binary_t object.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 285

def do_rsaes_pkcs_signature(key_binary_p, input_binary_p,
                            output_binary_p, status_p)
  key = Binary.from_pointer(key_binary_p).to_s
  input = Binary.from_pointer(input_binary_p).to_s

  write_binary_string_and_set_status(output_binary_p, status_p) do
    Hooks.rsaes_pkcs_signature(key, input)
  end
end

#handle_error(status_p) ⇒ true | false (private)

Yields to the provided block and rescues exceptions raised by the block. If an exception was raised, sets the specified status to the exception message and returns false. If no exceptions were raised, does not modify the status and returns true.

This method is meant to be used with libmongocrypt callbacks and follows the API defined by libmongocrypt.

Parameters:

  • status_p (FFI::Pointer)

    A pointer to libmongocrypt status object

Returns:

  • (true | false)

    Whether block executed without raising exceptions.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 224

def handle_error(status_p)
  yield

  true
rescue StandardError => e
  status = Status.from_pointer(status_p)
  status.update(:error_client, 1, "#{e.class}: #{e}")
  false
end

#initialize_mongocrypt (private)

Initialize the underlying mongocrypt_t object and raise an error if the operation fails

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 400

def initialize_mongocrypt
  Binding.init(self)
  # There is currently no test for the error(?) code path
end

#kms_tls_options(provider) ⇒ Hash

Return TLS options for KMS provider. If there are no TLS options set, empty hash is returned. Named providers (e.g. "kmip:name1") fall back to the base-type options (e.g. :kmip) when no exact match is found.

Parameters:

  • provider (String)

    KMS provider name or named identifier.

Returns:

  • (Hash)

    TLS options to connect to KMS provider.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 134

def kms_tls_options(provider)
  provider_str = provider.to_s
  base_type = KMS.provider_base_type(provider_str)

  @kms_tls_options.fetch(provider_str) do
    @kms_tls_options.fetch(provider_str.to_sym) do
      @kms_tls_options.fetch(base_type) do
        @kms_tls_options.fetch(base_type.to_sym, {})
      end
    end
  end
end

#maybe_set_schema_map(options) (private)

Set the schema map option on the underlying mongocrypt_t object

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 158

def maybe_set_schema_map(options)
  if !options[:schema_map] && !options[:schema_map_path]
    @schema_map = nil
  elsif options[:schema_map] && options[:schema_map_path]
    raise ArgumentError.new(
      'Cannot set both schema_map and schema_map_path options.'
    )
  elsif options[:schema_map]
    unless options[:schema_map].is_a?(Hash)
      raise ArgumentError.new(
        "#{@schema_map} is an invalid schema_map; schema_map must be a Hash or nil."
      )
    end
    @schema_map = options[:schema_map]
    Binding.setopt_schema_map(self, @schema_map)
  elsif options[:schema_map_path]
    @schema_map = BSON::ExtJSON.parse(File.read(options[:schema_map_path]))
    Binding.setopt_schema_map(self, @schema_map)
  end
rescue Errno::ENOENT
  raise ArgumentError.new(
    "#{@schema_map_path} is an invalid path to a file contains schema_map."
  )
end

#refFFI::Pointer

Return the reference to the underlying @mongocrypt object

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 123

def ref
  @mongocrypt
end

#set_bypass_query_analysis (private)

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 193

def set_bypass_query_analysis
  unless [ true, false ].include?(@bypass_query_analysis)
    raise ArgumentError.new(
      "#{@bypass_query_analysis} is an invalid bypass_query_analysis value; must be a Boolean or nil"
    )
  end

  Binding.setopt_bypass_query_analysis(self) if @bypass_query_analysis
end

#set_crypto_hooks (private)

We are building libmongocrypt without crypto functions to remove the external dependency on OpenSSL. This method binds native Ruby crypto methods to the underlying mongocrypt_t object so that libmongocrypt can still perform cryptography.

Every crypto binding ignores its first argument, which is an option mongocrypt_ctx_t object and is not required to use crypto hooks.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 302

def set_crypto_hooks
  @aes_encrypt = proc do |_, key_binary_p, iv_binary_p, input_binary_p, output_binary_p, response_length_p, status_p|
    do_aes(
      key_binary_p,
      iv_binary_p,
      input_binary_p,
      output_binary_p,
      response_length_p,
      status_p
    )
  end

  @aes_decrypt = proc do |_, key_binary_p, iv_binary_p, input_binary_p, output_binary_p, response_length_p, status_p|
    do_aes(
      key_binary_p,
      iv_binary_p,
      input_binary_p,
      output_binary_p,
      response_length_p,
      status_p,
      decrypt: true
    )
  end

  @random = proc do |_, output_binary_p, num_bytes, status_p|
    write_binary_string_and_set_status(output_binary_p, status_p) do
      Hooks.random(num_bytes)
    end
  end

  @hmac_sha_512 = proc do |_, key_binary_p, input_binary_p, output_binary_p, status_p|
    do_hmac_sha('SHA512', key_binary_p, input_binary_p, output_binary_p, status_p)
  end

  @hmac_sha_256 = proc do |_, key_binary_p, input_binary_p, output_binary_p, status_p|
    do_hmac_sha('SHA256', key_binary_p, input_binary_p, output_binary_p, status_p)
  end

  @hmac_hash = proc do |_, input_binary_p, output_binary_p, status_p|
    input = Binary.from_pointer(input_binary_p).to_s

    write_binary_string_and_set_status(output_binary_p, status_p) do
      Hooks.hash_sha256(input)
    end
  end

  Binding.setopt_crypto_hooks(
    self,
    @aes_encrypt,
    @aes_decrypt,
    @random,
    @hmac_sha_512,
    @hmac_sha_256,
    @hmac_hash
  )

  @aes_ctr_encrypt = proc do |_, key_binary_p, iv_binary_p, input_binary_p, output_binary_p, response_length_p, status_p|
    do_aes(
      key_binary_p,
      iv_binary_p,
      input_binary_p,
      output_binary_p,
      response_length_p,
      status_p,
      mode: :CTR
    )
  end

  @aes_ctr_decrypt = proc do |_, key_binary_p, iv_binary_p, input_binary_p, output_binary_p, response_length_p, status_p|
    do_aes(
      key_binary_p,
      iv_binary_p,
      input_binary_p,
      output_binary_p,
      response_length_p,
      status_p,
      decrypt: true,
      mode: :CTR
    )
  end

  Binding.setopt_aes_256_ctr(
    self,
    @aes_ctr_encrypt,
    @aes_ctr_decrypt
  )

  @rsaes_pkcs_signature_cb = proc do |_, key_binary_p, input_binary_p, output_binary_p, status_p|
    do_rsaes_pkcs_signature(key_binary_p, input_binary_p, output_binary_p, status_p)
  end

  Binding.setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(
    self,
    @rsaes_pkcs_signature_cb
  )
end

#set_encrypted_fields_map (private)

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 183

def set_encrypted_fields_map
  unless @encrypted_fields_map.is_a?(Hash)
    raise ArgumentError.new(
      "#{@encrypted_fields_map} is an invalid encrypted_fields_map: must be a Hash or nil"
    )
  end

  Binding.setopt_encrypted_field_config_map(self, @encrypted_fields_map)
end

#set_logger_callback (private)

Send the logs from libmongocrypt to the ::Mongo::Logger

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 204

def set_logger_callback
  @log_callback = proc do |level, msg|
    @logger.send(level, msg)
  end

  Binding.setopt_log_handler(@mongocrypt, @log_callback)
end

#write_binary_string_and_set_status(output_binary_p, status_p) ⇒ true | false (private)

Yields to the provided block and writes the return value of block to the specified mongocrypt_binary_t object. If an exception is raised during execution of the block, writes the exception message to the specified status object and returns false. If no exception is raised, does not modify status and returns true. message to the mongocrypt_status_t object.

Parameters:

  • output_binary_p (FFI::Pointer)

    A pointer to libmongocrypt Binary object to receive the result of block's execution

  • status_p (FFI::Pointer)

    A pointer to libmongocrypt status object

Returns:

  • (true | false)

    Whether block executed without raising exceptions.

[ GitHub ]

  
# File 'lib/mongo/crypt/handle.rb', line 247

def write_binary_string_and_set_status(output_binary_p, status_p)
  handle_error(status_p) do
    output = yield

    Binary.from_pointer(output_binary_p).write(output)
  end
end