Class: Fluent::Plugin::SdnsApiSinger::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/fluent/plugin/aksk/signer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSigner

Returns a new instance of Signer.



60
61
62
63
# File 'lib/fluent/plugin/aksk/signer.rb', line 60

def initialize
  @key = ""
  @secret = ""
end

Instance Attribute Details

#keyObject

Returns the value of attribute key.



58
59
60
# File 'lib/fluent/plugin/aksk/signer.rb', line 58

def key
  @key
end

#secretObject

Returns the value of attribute secret.



58
59
60
# File 'lib/fluent/plugin/aksk/signer.rb', line 58

def secret
  @secret
end

Instance Method Details

#auth_header_value(signature, app_key, signed_headers) ⇒ Object



139
140
141
# File 'lib/fluent/plugin/aksk/signer.rb', line 139

def auth_header_value(signature, app_key, signed_headers)
  "#{Algorithm} Access=#{app_key}, SignedHeaders=#{signed_headers.join(';')}, Signature=#{signature}"
end

#canonical_headers(r, signed_headers) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/fluent/plugin/aksk/signer.rb', line 112

def canonical_headers(r, signed_headers)
  a = []
  headers = {}
  r.headers.each do |key, value|
    key_encoded = key.downcase
    value_encoded = value.strip
    headers[key_encoded] = value_encoded
    r.headers[key] = value_encoded.encode('utf-8').force_encoding('iso-8859-1')
  end
  signed_headers.each { |key| a << "#{key}:#{headers[key]}" }
  "#{a.join("\n")}\n"
end

#canonical_query_string(r) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/fluent/plugin/aksk/signer.rb', line 99

def canonical_query_string(r)
  keys = r.query.keys.sort
  keys.map do |key|
    k = url_encode(key)
    value = r.query[key]
    if value.is_a?(Array)
      value.sort.map { |v| "#{k}=#{url_encode(v.to_s)}" }
    else
      "#{k}=#{url_encode(value.to_s)}"
    end
  end.join('&')
end

#canonical_request(r, signed_headers) ⇒ Object



82
83
84
85
86
87
88
89
# File 'lib/fluent/plugin/aksk/signer.rb', line 82

def canonical_request(r, signed_headers)
  canonical_headers = canonical_headers(r, signed_headers)
  hexencode = find_header(r, HeaderContentSha256)
  if hexencode.nil?
    hexencode = hex_encode_sha256_hash(r.body)
  end
  "#{r.method.upcase}\n#{canonical_uri(r)}\n#{canonical_query_string(r)}\n#{canonical_headers}\n#{signed_headers.join(';')}\n#{hexencode}"
end

#canonical_uri(r) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/fluent/plugin/aksk/signer.rb', line 91

def canonical_uri(r)
  patterns = CGI.unescape(r.uri).split('/')
  uri = patterns.map { |v| url_encode(v) }
  url_path = uri.join('/')
  url_path += '/' unless url_path[-1] == '/'
  url_path
end

#find_header(r, header) ⇒ Object



143
144
145
146
147
148
# File 'lib/fluent/plugin/aksk/signer.rb', line 143

def find_header(r, header)
  r.headers.each do |k, v|
    return v if k.downcase == header.downcase
  end
  nil
end

#hex_encode_sha256_hash(data) ⇒ Object



69
70
71
# File 'lib/fluent/plugin/aksk/signer.rb', line 69

def hex_encode_sha256_hash(data)
  Digest::SHA256.hexdigest(data)
end

#hmacsha256(key_byte, message) ⇒ Object



65
66
67
# File 'lib/fluent/plugin/aksk/signer.rb', line 65

def hmacsha256(key_byte, message)
  OpenSSL::HMAC.digest('sha256', key_byte, message)
end

#sign(r) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/fluent/plugin/aksk/signer.rb', line 162

def sign(r)
  r.body = r.body.encode('UTF-8') if r.body.is_a?(String)
  header_time = find_header(r, HeaderXDate)
  if header_time.nil?
    t = DateTime.now
    r.headers[HeaderXDate] = t.strftime(BasicDateFormat)
  else
    t = DateTime.strptime(header_time, BasicDateFormat)
  end

  unless r.headers.keys.any? { |key| key.downcase == 'host' }
    r.headers["host"] = r.host
  end
  signed_headers = signed_headers(r)
  canonical_request = canonical_request(r, signed_headers)
  string_to_sign = string_to_sign(canonical_request, t)
  signature = sign_string_to_sign(string_to_sign, @secret)
  auth_value = auth_header_value(signature, @key, signed_headers)
  r.headers[HeaderAuthorization] = auth_value
  r.headers["content-length"] = r.body.length.to_s
  query_string = canonical_query_string(r)
  r.uri += "?#{query_string}" unless query_string.empty?
end

#sign_string_to_sign(string_to_sign, signing_key) ⇒ Object



134
135
136
137
# File 'lib/fluent/plugin/aksk/signer.rb', line 134

def sign_string_to_sign(string_to_sign, signing_key)
  hm = hmacsha256(signing_key, string_to_sign)
  hm.unpack1('H*')
end

#signed_headers(r) ⇒ Object



130
131
132
# File 'lib/fluent/plugin/aksk/signer.rb', line 130

def signed_headers(r)
  r.headers.keys.map(&:downcase).sort
end

#string_to_sign(canonical_request, t) ⇒ Object



73
74
75
76
# File 'lib/fluent/plugin/aksk/signer.rb', line 73

def string_to_sign(canonical_request, t)
  bytes = hex_encode_sha256_hash(canonical_request)
  "#{Algorithm}\n#{t.strftime(BasicDateFormat)}\n#{bytes}"
end

#url_encode(s) ⇒ Object



78
79
80
# File 'lib/fluent/plugin/aksk/signer.rb', line 78

def url_encode(s)
  CGI.escape(s).gsub('+', '%20')
end

#verify(r, authorization) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
# File 'lib/fluent/plugin/aksk/signer.rb', line 150

def verify(r, authorization)
  r.body = r.body.encode('UTF-8') if r.body.is_a?(String)
  header_time = find_header(r, HeaderXDate)
  return false if header_time.nil?

  t = DateTime.strptime(header_time, BasicDateFormat)
  signed_headers = signed_headers(r)
  canonical_request = canonical_request(r, signed_headers)
  string_to_sign = string_to_sign(canonical_request, t)
  authorization == sign_string_to_sign(string_to_sign, @secret)
end