class Bunq::Signature

Constants

BUNQ_HEADER_PREFIX

headers in raw_headers hash in rest client are all lower case

BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER

Attributes

private_key[R]
server_public_key[R]

Public Class Methods

new(private_key, server_public_key) click to toggle source
# File lib/bunq/signature.rb, line 11
def initialize(private_key, server_public_key)
  fail ArgumentError, 'private_key is mandatory' unless private_key
  fail ArgumentError, 'server_public_key is mandatory' unless server_public_key

  @private_key = OpenSSL::PKey::RSA.new(private_key)
  @server_public_key = OpenSSL::PKey::RSA.new(server_public_key)
end

Public Instance Methods

create(body) click to toggle source
# File lib/bunq/signature.rb, line 19
def create(body)
  signature = private_key.sign(digest, body.to_s)

  Base64.strict_encode64(signature)
end
verify!(response) click to toggle source
# File lib/bunq/signature.rb, line 25
def verify!(response)
  return if skip_signature_check(response.code)

  signature_headers = response.raw_headers.find { |k, _| k.to_s.downcase == BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER }
  unless signature_headers
    fail AbsentResponseSignature.new(code: response.code, headers: response.raw_headers, body: response.body)
  end

  signature_headers_value = signature_headers[1]
  unless signature_headers_value
    fail AbsentResponseSignature.new(code: response.code, headers: response.raw_headers, body: response.body)
  end

  signature = Base64.strict_decode64(signature_headers_value.first)
  if !verify_modern(signature, response) && !verify_legacy(signature, response)
    fail InvalidResponseSignature.new(code: response.code, headers: response.raw_headers, body: response.body)
  end
end

Private Instance Methods

digest() click to toggle source
# File lib/bunq/signature.rb, line 48
def digest
  OpenSSL::Digest::SHA256.new
end
skip_signature_check(response_code) click to toggle source
# File lib/bunq/signature.rb, line 57
def skip_signature_check(response_code)
  (Bunq.configuration.sandbox && response_code == 409) || response_code == 429
end
verifiable_header?(header_name, _) click to toggle source
# File lib/bunq/signature.rb, line 52
def verifiable_header?(header_name, _)
  the_header_name = header_name.to_s.downcase
  the_header_name.start_with?(BUNQ_HEADER_PREFIX) && the_header_name != BUNQ_SERVER_SIGNATURE_RESPONSE_HEADER
end
verify(signature, data) click to toggle source
# File lib/bunq/signature.rb, line 78
def verify(signature, data)
  server_public_key.verify(digest, signature, data)
end
verify_legacy(signature, response) click to toggle source
# File lib/bunq/signature.rb, line 61
def verify_legacy(signature, response)
  sorted_bunq_headers = response
    .raw_headers
    .select(&method(:verifiable_header?))
    .sort
    .to_h
    .map do |k, v|
      "#{k.to_s.split('-').map(&:capitalize).join('-')}: #{v.first}"
    end

  verify(signature, %(#{response.code}\n#{sorted_bunq_headers.join("\n")}\n\n#{response.body}))
end
verify_modern(signature, response) click to toggle source
# File lib/bunq/signature.rb, line 74
def verify_modern(signature, response)
  verify(signature, response.body)
end