Class: Ibanity::HttpSignature

Inherits:
Object
  • Object
show all
Defined in:
lib/ibanity/http_signature.rb

Constant Summary collapse

PSS_DIGEST_ALGORITHM =
"SHA256"
SIGNATURE_ALGORITHM =
"hs2019"

Instance Method Summary collapse

Constructor Details

#initialize(certificate:, certificate_id:, key:, method:, uri:, query_params:, headers:, payload:) ⇒ HttpSignature

Returns a new instance of HttpSignature.



9
10
11
12
13
14
15
16
17
18
# File 'lib/ibanity/http_signature.rb', line 9

def initialize(certificate:, certificate_id:, key:, method:, uri:, query_params:, headers:, payload:)
  @certificate    = certificate
  @certificate_id = certificate_id
  @key            = key
  @method         = method
  @uri            = URI(uri)
  @headers        = headers
  @payload        = payload
  @query_params   = query_params
end

Instance Method Details

#base64_signatureObject



79
80
81
82
# File 'lib/ibanity/http_signature.rb', line 79

def base64_signature
  signature = @key.sign_pss(PSS_DIGEST_ALGORITHM, signing_string, salt_length: :digest, mgf1_hash: PSS_DIGEST_ALGORITHM)
  Base64.urlsafe_encode64(signature)
end

#compute_digestObject



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ibanity/http_signature.rb', line 37

def compute_digest
  case @payload
    when NilClass
      digest = compute_digest_string("")
    when String
      digest = compute_digest_string(@payload)
    when File
      digest = compute_digest_file(@payload)
  end
  base64 = Base64.urlsafe_encode64(digest.digest)
  "SHA-512=#{base64}"
end

#compute_digest_file(pathname) ⇒ Object



55
56
57
58
59
60
61
62
63
# File 'lib/ibanity/http_signature.rb', line 55

def compute_digest_file(pathname)
  digest = OpenSSL::Digest::SHA512.new
  File.open(pathname, 'rb') do |f|
    while buffer = f.read(256_000)
      digest << buffer
    end
  end
  digest
end

#compute_digest_string(str) ⇒ Object



50
51
52
53
# File 'lib/ibanity/http_signature.rb', line 50

def compute_digest_string(str)
  digest = OpenSSL::Digest::SHA512.new
  digest.update(str)
end

#dateObject



88
89
90
# File 'lib/ibanity/http_signature.rb', line 88

def date
  @date ||= Time.now.to_i
end

#header_value(header) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ibanity/http_signature.rb', line 101

def header_value(header)
  case header
  when "(request-target)"
    request_target
  when "host"
    host
  when "digest"
    payload_digest
  when "(created)"
    date
  else
    camelized_header = header.split("-").collect(&:capitalize).join("-")
    @headers[camelized_header]
  end
end

#headers_to_signObject



65
66
67
68
69
70
71
72
# File 'lib/ibanity/http_signature.rb', line 65

def headers_to_sign
  result = ["(request-target)", "host", "digest", "(created)"]
  result << "authorization" unless @headers["Authorization"].nil?
  @headers.keys.each do |header|
    result << header.to_s.downcase if header.to_s.match(/ibanity/i)
  end
  result
end

#hostObject



84
85
86
# File 'lib/ibanity/http_signature.rb', line 84

def host
  @uri.host
end

#payload_digestObject



33
34
35
# File 'lib/ibanity/http_signature.rb', line 33

def payload_digest
  @payload_digest ||= compute_digest
end

#request_targetObject



74
75
76
77
# File 'lib/ibanity/http_signature.rb', line 74

def request_target
  @uri.query = RestClient::Utils.encode_query_string(@query_params) if @query_params&.keys&.any?
  "#{@method} #{@uri.request_uri}"
end

#signature_headersObject



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/ibanity/http_signature.rb', line 20

def signature_headers
  {
    "Digest"    => payload_digest,
    "Signature" => [
      %(keyId="#{@certificate_id}"),
      %(created="#{date}"),
      %(algorithm="#{SIGNATURE_ALGORITHM}"),
      %(headers="#{headers_to_sign.join(" ")}"),
      %(signature="#{base64_signature}"),
    ].join(",")
  }
end

#signing_stringObject



92
93
94
95
96
97
98
99
# File 'lib/ibanity/http_signature.rb', line 92

def signing_string
  result = []
  headers_to_sign.each do |header_to_sign|
    value = header_value(header_to_sign)
    result << "#{header_to_sign}: #{value}"
  end
  result.join("\n")
end