hawk-auth 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0520600ff04a5b34f08821c26c42350db21ee2b4
4
+ data.tar.gz: 6bd61486e985287d0cc944595444f4f9012bc736
5
+ SHA512:
6
+ metadata.gz: 0b8136675219d0dfb65acb94022f5fc14b1b4595e61fe2391ecf8969b1e3fc0420b19b0c62ee52d54ad9de4650c8891c4c4a675704f0c2ccb3df244c281023e9
7
+ data.tar.gz: 567bd2820b6a62f80337d37d1c45899d425e770030b060447b1cd92f25aadb06a2542815ef1604692390323ea2c75993eb3a1090a0ffe417efac3f1e5f7fbe80
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
1
+ Copyright (c) 2013 Tent.is, LLC. All rights reserved.
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
10
10
  copyright notice, this list of conditions and the following disclaimer
11
11
  in the documentation and/or other materials provided with the
12
12
  distribution.
13
- * Neither the name of Apollic Software, LLC nor the names of its
13
+ * Neither the name of Tent.is, LLC nor the names of its
14
14
  contributors may be used to endorse or promote products derived from
15
15
  this software without specific prior written permission.
16
16
 
@@ -4,6 +4,7 @@ module Hawk
4
4
  require 'hawk/crypto'
5
5
  require 'hawk/authentication_failure'
6
6
  require 'hawk/authorization_header'
7
+ require 'hawk/timestamp_mac_header'
7
8
  require 'hawk/client'
8
9
  require 'hawk/server'
9
10
  end
@@ -8,7 +8,7 @@ module Hawk
8
8
  def header
9
9
  timestamp = Time.now.to_i
10
10
  if @options[:credentials]
11
- timestamp_mac = Crypto.ts_mac(:ts => timestamp, :credentials => @options[:credentials])
11
+ timestamp_mac = Crypto.ts_mac(:ts => timestamp, :credentials => @options[:credentials]).to_s
12
12
  %(Hawk ts="#{timestamp}", tsm="#{timestamp_mac}", error="#{message}")
13
13
  else
14
14
  %(Hawk error="#{message}")
@@ -34,14 +34,14 @@ module Hawk
34
34
  raise InvalidAlgorithmError.new("#{credentials[:algorithm].inspect} is not a supported algorithm! Use one of the following: #{SUPPORTED_ALGORITHMS.join(', ')}")
35
35
  end
36
36
 
37
- hash = Crypto.hash(options)
37
+ hash = Crypto.hash(options).to_s
38
38
  mac = Crypto.mac(options)
39
39
 
40
40
  parts = {
41
41
  :id => credentials[:id],
42
42
  :ts => options[:ts],
43
43
  :nonce => options[:nonce],
44
- :mac => mac
44
+ :mac => mac.to_s
45
45
  }
46
46
  parts[:hash] = hash if options.has_key?(:payload) && !options[:payload].nil?
47
47
  parts[:ext] = options[:ext] if options.has_key?(:ext)
@@ -89,21 +89,23 @@ module Hawk
89
89
  end
90
90
  end
91
91
 
92
- expected_mac = Crypto.mac(options.merge(
92
+ mac_opts = options.merge(
93
93
  :credentials => credentials,
94
94
  :ts => parts[:ts],
95
95
  :nonce => parts[:nonce],
96
96
  :ext => parts[:ext],
97
97
  :app => options[:app] || parts[:app],
98
98
  :dig => options[:dig] || parts[:dig]
99
- ))
100
- unless expected_mac == parts[:mac]
101
- return AuthenticationFailure.new(:mac, "Invalid mac")
102
- end
99
+ )
103
100
 
104
101
  expected_hash = parts[:hash] ? Crypto.hash(options.merge(:credentials => credentials)) : nil
105
- if expected_hash && expected_hash != parts[:hash]
106
- return AuthenticationFailure.new(:hash, "Invalid hash")
102
+ if expected_hash && expected_hash.to_s != parts[:hash]
103
+ return AuthenticationFailure.new(:hash, "Invalid hash. #{expected_hash.normalized_string}")
104
+ end
105
+
106
+ expected_mac = Crypto.mac(mac_opts)
107
+ unless expected_mac.eql?(parts[:mac])
108
+ return AuthenticationFailure.new(:mac, "Invalid mac. #{expected_mac.normalized_string}")
107
109
  end
108
110
 
109
111
  credentials
@@ -14,7 +14,7 @@ module Hawk
14
14
 
15
15
  def calculate_time_offset(authorization_header, options)
16
16
  parts = AuthorizationHeader.parse(authorization_header)
17
- expected_mac = Crypto.ts_mac(:ts => parts[:ts], :credentials => options[:credentials])
17
+ expected_mac = Crypto.ts_mac(:ts => parts[:ts], :credentials => options[:credentials]).to_s
18
18
  return unless expected_mac == parts[:tsm]
19
19
  parts[:ts].to_i - Time.now.to_i
20
20
  end
@@ -22,82 +22,193 @@ module Hawk
22
22
  module Crypto
23
23
  extend self
24
24
 
25
- def hash(options)
26
- parts = []
25
+ class Base
26
+ def to_s(options = {})
27
+ if options[:raw]
28
+ digest
29
+ else
30
+ encode64
31
+ end
32
+ end
27
33
 
28
- parts << "hawk.1.payload"
29
- parts << options[:content_type]
30
- parts << options[:payload].to_s
31
- parts << nil # trailing newline
34
+ def encode64
35
+ Base64.encode64(digest).chomp
36
+ end
32
37
 
33
- Base64.encode64(OpenSSL::Digest.const_get(options[:credentials][:algorithm].upcase).digest(parts.join("\n"))).chomp
34
- end
38
+ def ==(other)
39
+ if self.class === other
40
+ secure_compare(to_s(:raw => true), other.to_s(:raw => true))
41
+ else
42
+ # assume base64 encoded mac
43
+ secure_compare(to_s(:raw => true), Base64.decode64(other))
44
+ end
45
+ end
35
46
 
36
- def normalized_string(options)
37
- options = options.dup
38
- if !options[:hash] && options.has_key?(:payload) && !options[:payload].nil?
39
- options[:hash] = hash(options)
47
+ def eql?(other)
48
+ self == other
40
49
  end
41
50
 
42
- parts = []
51
+ private
52
+
53
+ def secure_compare(a, b)
54
+ return false if a.empty? || b.empty? || a.bytesize != b.bytesize
55
+ b_bytes = b.unpack "C#{b.bytesize}"
56
+
57
+ res = 0
58
+ a.each_byte { |byte| res |= byte ^ b_bytes.shift }
59
+ res == 0
60
+ end
43
61
 
44
- parts << "hawk.1.#{options[:type] || 'header'}"
45
- parts << options[:ts]
46
- parts << options[:nonce]
47
- parts << options[:method].to_s.upcase
48
- parts << options[:request_uri]
49
- parts << options[:host]
50
- parts << options[:port]
51
- parts << options[:hash]
52
- parts << options[:ext]
62
+ def openssl_digest(algorithm)
63
+ OpenSSL::Digest.const_get(algorithm.upcase)
64
+ end
65
+ end
53
66
 
54
- if options[:app]
55
- parts << options[:app]
56
- parts << options[:dig]
67
+ class Mac < Base
68
+ def initialize(key, options, algorithm = 'sha256')
69
+ @key, @options, @algorithm = key, options, algorithm
57
70
  end
58
71
 
59
- parts << nil # trailing newline
72
+ def normalized_string
73
+ options = @options.dup
74
+ if !options[:hash] && options.has_key?(:payload) && !options[:payload].nil?
75
+ options[:hash] = Crypto.hash(options)
76
+ end
77
+
78
+ parts = []
79
+
80
+ parts << "hawk.1.#{options[:type] || 'header'}"
81
+ parts << options[:ts]
82
+ parts << options[:nonce]
83
+ parts << options[:method].to_s.upcase
84
+ parts << options[:request_uri]
85
+ parts << options[:host]
86
+ parts << options[:port]
87
+ parts << options[:hash]
88
+ parts << options[:ext]
89
+
90
+ if options[:app]
91
+ parts << options[:app]
92
+ parts << options[:dig]
93
+ end
94
+
95
+ parts << nil # trailing newline
60
96
 
61
- parts.join("\n")
97
+ parts.join("\n")
98
+ end
99
+
100
+ def digest
101
+ @digest ||= OpenSSL::HMAC.digest(openssl_digest(@algorithm).new, @key, normalized_string)
102
+ end
62
103
  end
63
104
 
64
- def mac(options)
65
- Base64.encode64(
66
- OpenSSL::HMAC.digest(
67
- openssl_digest(options[:credentials][:algorithm]).new,
68
- options[:credentials][:key],
69
- normalized_string(options)
70
- )
71
- ).chomp
105
+ class Hash < Base
106
+ def initialize(content_type, payload, algorithm)
107
+ @content_type, @payload, @algorithm = content_type, payload, algorithm
108
+ end
109
+
110
+ def normalized_string
111
+ @normalized_string ||= begin
112
+ parts = []
113
+
114
+ parts << "hawk.1.payload"
115
+ parts << @content_type
116
+ parts << @payload.to_s
117
+ parts << nil # trailing newline
118
+
119
+ parts.join("\n")
120
+ end
121
+ end
122
+
123
+ def digest
124
+ @digest ||= openssl_digest(@algorithm).digest(normalized_string)
125
+ end
72
126
  end
73
127
 
74
- def ts_mac(options)
75
- Base64.encode64(
76
- OpenSSL::HMAC.digest(
77
- openssl_digest(options[:credentials][:algorithm]).new,
78
- options[:credentials][:key],
79
- "hawk.1.ts\n#{options[:ts]}\n"
80
- )
81
- ).chomp
128
+ class TSMac < Base
129
+ def initialize(key, ts, algorithm = 'sha256')
130
+ @key, @ts, @algorithm = key, ts, algorithm
131
+ end
132
+
133
+ def normalized_string
134
+ @normalized_string ||= "hawk.1.ts\n#{@ts}\n"
135
+ end
136
+
137
+ def digest
138
+ @digest ||= OpenSSL::HMAC.digest(openssl_digest(@algorithm).new, @key, normalized_string)
139
+ end
82
140
  end
83
141
 
84
- def bewit(options)
85
- options[:ts] ||= Time.now.to_i + options[:ttl].to_i
142
+ class Bewit < Base
143
+ def self.decode(bewit)
144
+ padding = '=' * ((4 - bewit.size) % 4)
145
+ id, timestamp, mac, ext = Base64.decode64(bewit + padding).split('\\')
146
+
147
+ new(id, nil, { :mac => mac, :ext => ext, :ts => timestamp }, nil)
148
+ end
86
149
 
87
- _mac = mac(options.merge(:type => 'bewit'))
150
+ attr_reader :id, :ts, :ext
151
+ def initialize(id, key, options, algorithm = 'sha256')
152
+ @ts = options[:ts] ||= Time.now.to_i + options[:ttl].to_i
153
+ @ext = options[:ext]
154
+ @id, @key, @options, @algorithm = id, key, options.dup, algorithm
155
+ @mac = options.delete(:mac) if options[:mac]
156
+ end
157
+
158
+ def mac
159
+ @mac ||= Crypto::Mac.new(@key, @options.merge(:type => 'bewit'), @algorithm)
160
+ end
161
+
162
+ def normalized_string
163
+ @normalized_string ||= begin
164
+ parts = []
88
165
 
89
- parts = []
166
+ parts << @id
167
+ parts << @ts
168
+ parts << mac.to_s
169
+ parts << @ext
90
170
 
91
- parts << options[:credentials][:id]
92
- parts << options[:ts]
93
- parts << _mac
94
- parts << options[:ext]
171
+ parts.join("\\")
172
+ end
173
+ end
174
+
175
+ def encode64
176
+ @encoded ||= Base64.urlsafe_encode64(normalized_string).chomp.sub(/=+\Z/, '')
177
+ end
95
178
 
96
- Base64.urlsafe_encode64(parts.join("\\")).chomp.sub(/=+\Z/, '')
179
+ def to_s(options = {})
180
+ encode64
181
+ end
182
+
183
+ def ==(other)
184
+ if self.class === other
185
+ mac == other.mac
186
+ else
187
+ # assume base64 encoded bewit
188
+ self == self.class.decode(other)
189
+ end
190
+ end
97
191
  end
98
192
 
99
- def openssl_digest(algorithm)
100
- OpenSSL::Digest.const_get(algorithm.upcase)
193
+ def hash(options)
194
+ Hash.new(options[:content_type], options[:payload], options[:credentials][:algorithm])
195
+ end
196
+
197
+ def mac(options)
198
+ Mac.new(options[:credentials][:key], options, options[:credentials][:algorithm])
199
+ end
200
+
201
+ def ts_mac(options)
202
+ TSMac.new(options[:credentials][:key], options[:ts], options[:credentials][:algorithm])
203
+ end
204
+
205
+ def bewit(options)
206
+ Bewit.new(
207
+ options[:credentials][:id],
208
+ options[:credentials][:key],
209
+ options,
210
+ options[:credentials][:algorithm]
211
+ )
101
212
  end
102
213
  end
103
214
  end
@@ -6,15 +6,14 @@ module Hawk
6
6
  Hawk::AuthorizationHeader.authenticate(authorization_header, options)
7
7
  end
8
8
 
9
- def authenticate_bewit(bewit, options)
10
- padding = '=' * ((4 - bewit.size) % 4)
11
- id, timestamp, mac, ext = Base64.decode64(bewit + padding).split('\\')
9
+ def authenticate_bewit(encoded_bewit, options)
10
+ bewit = Crypto::Bewit.decode(encoded_bewit)
12
11
 
13
- unless options[:credentials_lookup].respond_to?(:call) && (credentials = options[:credentials_lookup].call(id))
12
+ unless options[:credentials_lookup].respond_to?(:call) && (credentials = options[:credentials_lookup].call(bewit.id))
14
13
  return AuthenticationFailure.new(:id, "Unidentified id")
15
14
  end
16
15
 
17
- if Time.at(timestamp.to_i) < Time.now
16
+ if Time.at(bewit.ts.to_i) < Time.now
18
17
  return AuthenticationFailure.new(:ts, "Stale timestamp")
19
18
  end
20
19
 
@@ -24,13 +23,13 @@ module Hawk
24
23
  :request_uri => remove_bewit_param_from_path(options[:request_uri]),
25
24
  :port => options[:port],
26
25
  :method => options[:method],
27
- :ts => timestamp,
28
- :ext => ext
26
+ :ts => bewit.ts,
27
+ :ext => bewit.ext
29
28
  )
30
29
 
31
- unless expected_bewit == bewit
30
+ unless expected_bewit.eql?(bewit)
32
31
  if options[:request_uri].to_s =~ /\Ahttp/
33
- return authenticate_bewit(bewit, options.merge(
32
+ return authenticate_bewit(encoded_bewit, options.merge(
34
33
  :request_uri => options[:request_uri].sub(%r{\Ahttps?://[^/]+}, '')
35
34
  ))
36
35
  else
@@ -45,6 +44,10 @@ module Hawk
45
44
  Hawk::AuthorizationHeader.build(options, [:hash, :ext, :mac])
46
45
  end
47
46
 
47
+ def build_tsm_header(options)
48
+ Hawk::TimestampMacHeader.build(options)
49
+ end
50
+
48
51
  private
49
52
 
50
53
  def remove_bewit_param_from_path(path)
@@ -0,0 +1,30 @@
1
+ module Hawk
2
+ module TimestampMacHeader
3
+ extend self
4
+
5
+ REQUIRED_CREDENTIAL_MEMBERS = AuthorizationHeader::REQUIRED_CREDENTIAL_MEMBERS
6
+ SUPPORTED_ALGORITHMS = AuthorizationHeader::SUPPORTED_ALGORITHMS
7
+
8
+ InvalidCredentialsError = Class.new(StandardError)
9
+ InvalidAlgorithmError = Class.new(StandardError)
10
+
11
+ def build(options)
12
+ options[:ts] ||= Time.now.to_i
13
+
14
+ credentials = options[:credentials]
15
+ REQUIRED_CREDENTIAL_MEMBERS.each do |key|
16
+ unless credentials.has_key?(key)
17
+ raise InvalidCredentialsError.new("#{key.inspect} is missing!")
18
+ end
19
+ end
20
+
21
+ unless SUPPORTED_ALGORITHMS.include?(credentials[:algorithm])
22
+ raise InvalidAlgorithmError.new("#{credentials[:algorithm].inspect} is not a supported algorithm! Use one of the following: #{SUPPORTED_ALGORITHMS.join(', ')}")
23
+ end
24
+
25
+ tsm = Crypto.ts_mac(options)
26
+
27
+ %(Hawk ts="#{options[:ts]}", tsm="#{tsm}")
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module Hawk
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -40,8 +40,8 @@ describe Hawk::Client do
40
40
  _input
41
41
  end
42
42
 
43
- let(:expected_mac) { Hawk::Crypto.mac(client_input) }
44
- let(:expected_hash) { client_input[:payload] ? Hawk::Crypto.hash(client_input) : nil }
43
+ let(:expected_mac) { Hawk::Crypto.mac(client_input).to_s }
44
+ let(:expected_hash) { client_input[:payload] ? Hawk::Crypto.hash(client_input).to_s : nil }
45
45
 
46
46
  let(:authorization_header) do
47
47
  parts = []
@@ -74,8 +74,8 @@ describe Hawk::Client do
74
74
  Time.stubs(:now).returns(now)
75
75
  end
76
76
 
77
- let(:expected_mac) { Hawk::Crypto.mac(input) }
78
- let(:expected_hash) { input[:payload] ? Hawk::Crypto.hash(input) : nil }
77
+ let(:expected_mac) { Hawk::Crypto.mac(input).to_s }
78
+ let(:expected_hash) { input[:payload] ? Hawk::Crypto.hash(input).to_s : nil }
79
79
 
80
80
  let(:input) do
81
81
  _input = {
@@ -125,13 +125,15 @@ describe Hawk::Crypto do
125
125
  expect(described_class.ts_mac(input)).to eql("h/Ff6XI1euObD78ZNflapvLKXGuaw1RiLI4Q6Q5sAbM=")
126
126
  end
127
127
  end
128
+ end
128
129
 
130
+ describe Hawk::Crypto::Mac do
129
131
  describe ".normalized_string" do
130
132
  let(:normalization_method) { "normalized_string" }
131
133
 
132
134
  shared_examples "an input normalization method" do
133
135
  it "returns a valid normalized string" do
134
- expect(described_class.send(normalization_method, input)).to eql(expected_output)
136
+ expect(described_class.new(nil, input, nil).send(normalization_method)).to eql(expected_output)
135
137
  end
136
138
  end
137
139
 
@@ -54,8 +54,8 @@ describe Hawk::Server do
54
54
  _input
55
55
  end
56
56
 
57
- let(:expected_mac) { Hawk::Crypto.mac(client_input) }
58
- let(:expected_hash) { client_input[:payload] ? Hawk::Crypto.hash(client_input) : nil }
57
+ let(:expected_mac) { Hawk::Crypto.mac(client_input).to_s }
58
+ let(:expected_hash) { client_input[:payload] ? Hawk::Crypto.hash(client_input).to_s : nil }
59
59
 
60
60
  let(:authorization_header) do
61
61
  parts = []
@@ -116,7 +116,7 @@ describe Hawk::Server do
116
116
  it "returns error object" do
117
117
  actual = described_class.authenticate(authorization_header, input)
118
118
  expect(actual).to be_a(Hawk::AuthenticationFailure)
119
- expect(actual.key).to eql(:mac)
119
+ expect(actual.key).to eql(:hash)
120
120
  expect(actual.message).to_not eql(nil)
121
121
  end
122
122
  end
@@ -176,8 +176,8 @@ describe Hawk::Server do
176
176
  end
177
177
 
178
178
  describe ".build_authorization_header" do
179
- let(:expected_mac) { Hawk::Crypto.mac(input) }
180
- let(:expected_hash) { input[:payload] ? Hawk::Crypto.hash(input) : nil }
179
+ let(:expected_mac) { Hawk::Crypto.mac(input).to_s }
180
+ let(:expected_hash) { input[:payload] ? Hawk::Crypto.hash(input).to_s : nil }
181
181
  let(:timestamp) { Time.now.to_i }
182
182
  let(:nonce) { 'Ygvqdz' }
183
183
 
@@ -221,6 +221,44 @@ describe Hawk::Server do
221
221
  end
222
222
  end
223
223
 
224
+ describe ".build_tsm_header" do
225
+ let(:expected_tsm) { Hawk::Crypto.ts_mac(input).to_s }
226
+ let(:timestamp) { Time.now.to_i }
227
+
228
+ let(:input) do
229
+ {
230
+ :credentials => credentials,
231
+ :ts => timestamp
232
+ }
233
+ end
234
+
235
+ let(:expected_output_parts) do
236
+ [
237
+ %(ts="#{timestamp}"),
238
+ %(tsm="#{expected_tsm}")
239
+ ]
240
+ end
241
+
242
+ let(:expected_output) do
243
+ "Hawk #{expected_output_parts.join(', ')}"
244
+ end
245
+
246
+ context "when using sha256" do
247
+ let(:algorithm) { "sha256" }
248
+
249
+ it "builds tsm header" do
250
+ actual = described_class.build_tsm_header(input)
251
+
252
+ expected_output_parts.each do |expected_part|
253
+ matcher = Regexp === expected_part ? expected_part : Regexp.new(Regexp.escape(expected_part))
254
+ expect(actual).to match(matcher)
255
+ end
256
+
257
+ expect(actual).to eql(expected_output)
258
+ end
259
+ end
260
+ end
261
+
224
262
  describe ".authenticate_bewit" do
225
263
  shared_examples "authenticate_bewit" do
226
264
  context "when valid" do
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hawk-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.2.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jesse Stuart
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-06-13 00:00:00.000000000 Z
11
+ date: 2013-09-03 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,23 +27,20 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: mocha
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -95,6 +86,7 @@ files:
95
86
  - lib/hawk/client.rb
96
87
  - lib/hawk/crypto.rb
97
88
  - lib/hawk/server.rb
89
+ - lib/hawk/timestamp_mac_header.rb
98
90
  - lib/hawk/version.rb
99
91
  - spec/authentication_header_spec.rb
100
92
  - spec/client_spec.rb
@@ -104,27 +96,26 @@ files:
104
96
  - spec/support/shared_examples/authorization_header.rb
105
97
  homepage: ''
106
98
  licenses: []
99
+ metadata: {}
107
100
  post_install_message:
108
101
  rdoc_options: []
109
102
  require_paths:
110
103
  - lib
111
104
  required_ruby_version: !ruby/object:Gem::Requirement
112
- none: false
113
105
  requirements:
114
- - - ! '>='
106
+ - - '>='
115
107
  - !ruby/object:Gem::Version
116
108
  version: '0'
117
109
  required_rubygems_version: !ruby/object:Gem::Requirement
118
- none: false
119
110
  requirements:
120
- - - ! '>='
111
+ - - '>='
121
112
  - !ruby/object:Gem::Version
122
113
  version: '0'
123
114
  requirements: []
124
115
  rubyforge_project:
125
- rubygems_version: 1.8.23
116
+ rubygems_version: 2.0.7
126
117
  signing_key:
127
- specification_version: 3
118
+ specification_version: 4
128
119
  summary: Ruby implementation of Hawk
129
120
  test_files:
130
121
  - spec/authentication_header_spec.rb