class Cryptograpi::Decryption

Public Class Methods

new(creds) click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 11
def initialize(creds)
  raise 'Some credentials are missing' unless validate_credentials(creds)

  @papi = creds.access_key_id
  @sapi = creds.signing_key
  @srsa = creds.secret_access_key
  @host = creds.host.blank? ? CRYPTOGRAPI_HOST : creds.host

  @decryption_started = false
  @decryption_ready = true
end

Public Instance Methods

begin_decryption() click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 23
def begin_decryption
  raise ' Decryption is not ready' unless @decryption_ready

  raise ' Decryption already started' if @decryption_started

  raise 'Decryption already in progress' if @key.present? && @key.key?('dec')

  @decryption_started = true
  @data = ''
end
close_decryption() click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 154
def close_decryption
  raise 'Decryption currently running' if @decryption_started

  # Reset the decryption object
  if @key.present?
    if @key['uses'].positive?
      query_url = "#{endpoint}/#{@key['finger_print']}/#{@key['session']}"
      url = "#{endpoint_base}/decryption/key/#{@key['finger_print']}/#{@key['session']}"
      query = { uses: @key['uses'] }
      headers = Signature.headers(query_url, @host, 'patch', @papi, query, @sapi)

      response = HTTParty.patch(
        url,
        body: query.to_json,
        headers: headers
      )

      remove_instance_variable(:@data)
      remove_instance_variable(:@key)
    end
  end
end
endpoint() click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 177
def endpoint
  '/api/v0/encryption/key'
end
endpoint_base() click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 181
def endpoint_base
  "#{@host}/api/v0"
end
finish_decryption() click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 128
def finish_decryption
  raise 'Decryption is not started' unless @decryption_started

  # Update maintains tag-size bytes in the buffer
  # When this function is called, all data must already be
  # in the decryption object
  sz = @data.length - @cipher[:tag_length]

  raise 'Invalid Tag!' if sz.negative?

  if sz.zero?
    @key['dec'].auth_tag = @data
    begin
      plain_text = @key['dec'].final
      # Delete the context
      @key.delete('dec')
      # Return the plain text
      @decryption_started = false
      plain_text
    rescue Exception
      print 'Invalid cipher data or tag!'
      ''
    end
  end
end
update_decryption(data) click to toggle source
# File lib/cryptograpi_ruby/decrypt.rb, line 34
def update_decryption(data)
  raise ' Decryption is not started' unless @decryption_started

  # Act as a buffer for data
  @data += data

  # If there is no key or dec member of key, a header is still being built
  if !@key.present? || !@key.key?('dec')
    struct_length = [1, 1, 1, 1, 1].pack('CCCCn').length
    packed_struct = @data[0...struct_length]

    # Does the buffer contain enough of the header to determine
    # the lengths of the initialization vector and the key?
    if @data.length > struct_length
      version, flags, algorithm_id, iv_length, key_length = packed_struct.unpack('CCCCn')

      raise 'Invalid encryption header' if (version != 0) || ((flags & ~Cipher::CRYPTOFLAG) != 0)

      if @data.length > struct_length + iv_length + key_length
        # Extract the initialization vector
        iv = @data[struct_length...iv_length + struct_length]
        # Extract the encrypted key
        enc_key = @data[struct_length + iv_length..key_length + struct_length + iv_length]
        # Remove the header from the buffer
        @data = @data[struct_length + iv_length + key_length..-1]

        # Generate a key identifier
        hash_sha512 = OpenSSL::Digest.new('SHA512')
        hash_sha512 << enc_key
        client_id = hash_sha512.digest

        if @key.present?
          close if @key['client_id'] != client_id
        end

        unless @key.present?
          url = endpoint_base + '/decrypt/key'
          query = { encrypted_data_key: Base64.strict_encode64(enc_key) }
          headers = Signature.headers(endpoint, @host, 'post', @papi, query, @sapi)

          response = HTTParty.post(
            url,
            body: query.to_json,
            headers: headers
          )

          if response.code == WEBrick::HTTPStatus::RC_OK
            @key = {}
            @key['finger_print'] = response[key_fingerprint]
            @key['client_id'] = client_id
            @key['session'] = response['encryption_session']

            # Get the cipher name
            @key['algorithm'] = Cipher.new.find_algorithm(algorithm_id)

            encrypted_private_key = response['encrypted_private_key']
            # Decrypt WDK from base64
            wdk = Base64.strict_decode64(wrapped_data_key)
            # Use private key to decrypt the wdk
            dk = private_key.private_decrypt(wdk, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)

            @key['raw'] = dk
            @key['uses'] = 0
          else
            raise "HTTPError response: Expected 201 got #{response.code}"
          end
        end

        # If the key object exists, create a new decryptor.
        # Increment the key usage
        if @key.present?
          @cipher = Cipher.new.get_algorithm(@key['algorithm'])
          @key['dec'] = Cipher.new.decryptor(@cipher, @key['raw'], iv)

          if (flags & Cipher::CRYPTOFLAG) != 0
            @key['dec'].auth_data = packed_struct + iv + enc_key
          end
          @key['uses'] += 1
        end
      end
    end
  end

  plain_text = ''
  if @key.present? && @key.key?('dec')
    size = @data.length - @cipher[:tag_length]
    if size.positive?
      plain_text = @key['dec'].update(@data[0..size - 1])
      @data = @data[size..-1]
    end
    plain_text
  end
end