smaak 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,128 +3,101 @@ require 'smaak'
3
3
 
4
4
  describe Smaak::AuthMessage do
5
5
  before :all do
6
- @test_nonce = 1234567890
7
6
  @test_server_private_key = OpenSSL::PKey::RSA.new(4096)
7
+ @test_token_life = 10
8
+ @test_nonce = 1234567890
8
9
  @test_psk = "testpresharedkey"
9
10
  @test_server_public_key = @test_server_private_key.public_key
10
- @test_identity = 'test-service'
11
+ @test_identity = "test-service"
12
+ @test_identifier = 'test-service-1.cpt1.host-h.net'
13
+ @test_recipient = @test_server_public_key.export
14
+ @test_encrypt = true
11
15
  end
12
16
 
13
17
  before :each do
14
- @test_message_data = { 'recipient' => @test_server_public_key.export,
15
- 'identity' => @test_identity,
16
- 'psk' => Smaak::obfuscate_psk(@test_psk),
17
- 'expires' => Time.now.to_i + 10,
18
- 'nonce' => @test_nonce }
19
- @test_message = Smaak::build_message(@test_message_data)
20
-
21
- @iut = Smaak::AuthMessage.new(@test_message)
18
+ @test_expires = Time.now.to_i + @test_token_life
19
+ @iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, @test_encrypt)
22
20
  end
23
21
 
24
22
  context "when initialized" do
25
- it "should raise an ArgumentError if no message is provided" do
23
+ it "should raise an ArgumentError if no identifier is provided" do
26
24
  expect {
27
- Smaak::AuthMessage.new(nil)
28
- }.to raise_error ArgumentError, "Message not specified"
29
- end
30
-
31
- it "should remember the message provided" do
32
- expect(@iut.message).to eq(@test_message)
25
+ Smaak::AuthMessage.new(nil, nil, nil, nil, nil, nil)
26
+ }.to raise_error ArgumentError, "Message must have a valid identifier set"
33
27
  end
34
28
 
35
- it "should unpack the message data using Base64::decode and JSON.parse" do
36
- expect(@iut.message_data).to eq(@test_message_data)
29
+ it "should raise an ArgumentError if no nonce is provided" do
30
+ expect {
31
+ Smaak::AuthMessage.new(@test_identifier, nil, nil, nil, nil, nil)
32
+ }.to raise_error ArgumentError, "Message must have a valid nonce set"
37
33
  end
38
34
 
39
- it "should raise an ArgumentError if the message does not have a valid expiry set" do
40
- error = "Message must have a valid expiry set"
41
-
42
- expired_data = @test_message_data.dup
43
-
44
- expect {
45
- expired_data['expires'] = ""
46
- Smaak::AuthMessage.new(Smaak::build_message(expired_data))
47
- }.to raise_error ArgumentError, error
35
+ it "should raise an ArgumentError if an invalid nonce is provided" do
48
36
  expect {
49
- expired_data['expires'] = nil
50
- Smaak::AuthMessage.new(Smaak::build_message(expired_data))
51
- }.to raise_error ArgumentError, error
37
+ Smaak::AuthMessage.new(@test_identifier, 0, nil, nil, nil, nil)
38
+ }.to raise_error ArgumentError, "Message must have a valid nonce set"
52
39
  expect {
53
- expired_data['expires'] = 'sometimes'
54
- Smaak::AuthMessage.new(Smaak::build_message(expired_data))
55
- }.to raise_error ArgumentError, error
40
+ Smaak::AuthMessage.new(@test_identifier, 'invalid nonce', nil, nil, nil, nil)
41
+ }.to raise_error ArgumentError, "Message must have a valid nonce set"
56
42
  end
57
43
 
58
- it "should prevent its message from being changed" do
59
- expect{
60
- @iut.message = ""
61
- }.to raise_error NoMethodError
44
+ it "should raise an ArgumentError if no expiry is provided" do
45
+ expect {
46
+ Smaak::AuthMessage.new(@test_identifier, @test_nonce, nil, nil, nil, nil)
47
+ }.to raise_error ArgumentError, "Message must have a valid expiry set"
62
48
  end
63
49
 
64
- it "should prevent its message data from being changed" do
65
- expect{
66
- @iut.message_data = ""
67
- }.to raise_error NoMethodError
50
+ it "should raise an ArgumentError if an invalid expiry is provided" do
51
+ expect {
52
+ Smaak::AuthMessage.new(@test_identifier, @test_nonce, 0, nil, nil, nil)
53
+ }.to raise_error ArgumentError, "Message must have a valid expiry set"
54
+ expect {
55
+ Smaak::AuthMessage.new(@test_identifier, @test_nonce, 'invalid expire', nil, nil, nil)
56
+ }.to raise_error ArgumentError, "Message must have a valid expiry set"
68
57
  end
69
58
 
70
- it "should extract the identity from the message data and remember it" do
71
- expect(@iut.identity).to eq("test-service")
59
+ it "should remember the identifier provided" do
60
+ expect(@iut.identifier).to eq(@test_identifier)
72
61
  end
73
62
 
74
- it "should prevent its identity from being changed" do
75
- expect{
76
- @iut.identity = ""
77
- }.to raise_error NoMethodError
63
+ it "should remember the nonce provided" do
64
+ expect(@iut.nonce).to eq(@test_nonce)
78
65
  end
79
66
 
80
- it "should raise an ArgumentError if the message does not have a valid nonce set" do
81
- error = "Message must have a valid nonce"
82
-
83
- noncense_data = @test_message_data.dup
67
+ it "should remember the expiry provided" do
68
+ expect(@iut.expires).to eq(@test_expires)
69
+ end
84
70
 
85
- expect {
86
- noncense_data['nonce'] = "broken"
87
- Smaak::AuthMessage.new(Smaak::build_message(noncense_data))
88
- }.to raise_error ArgumentError, error
89
- expect {
90
- noncense_data['nonce'] = nil
91
- Smaak::AuthMessage.new(Smaak::build_message(noncense_data))
92
- }.to raise_error ArgumentError, error
93
- expect {
94
- noncense_data['nonce'] = 0
95
- Smaak::AuthMessage.new(Smaak::build_message(noncense_data))
96
- }.to raise_error ArgumentError, error
71
+ it "should remember the psk provided" do
72
+ expect(@iut.psk).to eq(Smaak::Crypto::obfuscate_psk(@test_psk))
97
73
  end
98
74
 
99
- it "should extract the nonce from the message data and remember it" do
100
- expect(@iut.nonce).to eq(@test_nonce)
75
+ it "should remember the recipient provided" do
76
+ expect(@iut.recipient).to eq(@test_recipient)
101
77
  end
102
78
 
103
- it "should prevent its nonce from being changed" do
104
- expect{
105
- @iut.nonce = ""
106
- }.to raise_error NoMethodError
79
+ it "should remember a boolean representation of whether encryption is required" do
80
+ expect(@iut.encrypt).to eq(true)
107
81
  end
108
82
 
109
- it "should raise an ArgumentError if the message data does not contain an identity" do
110
- error = "Message must have a valid identity set"
111
- anonymous_data = @test_message_data.dup
112
- anonymous_data['identity'] = nil
113
- expect {
114
- Smaak::AuthMessage.new(Smaak::build_message(anonymous_data))
115
- }.to raise_error ArgumentError, error
116
- anonymous_data['identity'] = ""
117
- expect {
118
- Smaak::AuthMessage.new(Smaak::build_message(anonymous_data))
119
- }.to raise_error ArgumentError, error
83
+ it "should translate the encrypt parameter from string to boolean" do
84
+ iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, false)
85
+ expect(iut.encrypt).to eq(false)
86
+
87
+ iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, true)
88
+ expect(iut.encrypt).to eq(true)
89
+
90
+ iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, "false")
91
+ expect(iut.encrypt).to eq(false)
92
+
93
+ iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, "true")
94
+ expect(iut.encrypt).to eq(true)
120
95
  end
121
96
  end
122
97
 
123
98
  context "when asked if it has expired" do
124
99
  it "should return true if the current timestamp exceeds that of the message expiry" do
125
- expired_data = @test_message_data.dup
126
- expired_data['expires'] = Time.now - 1
127
- iut = Smaak::AuthMessage.new(Smaak::build_message(expired_data))
100
+ iut = Smaak::AuthMessage.new(@test_identifier, @test_nonce, Time.now - 1, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, false)
128
101
  expect(iut.expired?).to eq(true)
129
102
  end
130
103
 
@@ -133,54 +106,13 @@ describe Smaak::AuthMessage do
133
106
  end
134
107
  end
135
108
 
136
- context "when asked if the signature is ok given a signature and public key" do
137
- before :each do
138
- @test_signature = Smaak::sign_message_data(@test_message_data, @test_server_private_key)
139
- end
140
-
141
- it "should return false if the public key provided is not the correct key for the signature" do
142
- mismatched = OpenSSL::PKey::RSA.new(4096).public_key
143
- expect(@iut.signature_ok?(@test_signature, mismatched)).to eq(false)
144
- end
145
-
146
- it "should return false if the signature is nil" do
147
- expect(@iut.signature_ok?(nil, @test_server_public_key)).to eq(false)
148
- end
149
-
150
- it "should return false if the public key is nil" do
151
- expect(@iut.signature_ok?(@test_signature, nil)).to eq(false)
152
- end
153
-
154
- it "should return false if OpenSSL cannot verify the message against the signature using the public key" do
155
- broken = @test_message_data
156
- broken['identity'] = 'suspect-identity'
157
- iut = Smaak::AuthMessage.new(Smaak::build_message(broken))
158
- expect(iut.signature_ok?(@test_signature, @test_server_public_key)).to eq(false)
159
- end
160
-
161
- it "should return true if OpenSSL succesfully verifies message against the signature using the public key" do
162
- expect(@iut.signature_ok?(@test_signature, @test_server_public_key)).to eq(true)
163
- end
164
- end
165
-
166
109
  context "when asked whether a message's psk matched" do
167
- it "should return false if no psk was provided" do
168
- expect(@iut.psk_match?(nil)).to eq(false)
169
- end
170
-
171
- it "should return false if the message data does not include a psk" do
172
- broken = @test_message_data
173
- broken['psk'] = nil
174
- iut = Smaak::AuthMessage.new(Smaak::build_message(broken))
175
- expect(iut.psk_match?(@test_psk)).to eq(false)
176
- end
177
-
178
110
  it "should return false if the PSKs do not match" do
179
111
  expect(@iut.psk_match?("doesnotmatch")).to eq(false)
180
112
  end
181
113
 
182
114
  it "should return true if the PSKs do match" do
183
- expect(@iut.psk_match?(@test_psk)).to eq(false)
115
+ expect(@iut.psk_match?(@test_psk)).to eq(true)
184
116
  end
185
117
  end
186
118
 
@@ -190,13 +122,6 @@ describe Smaak::AuthMessage do
190
122
  expect(@iut.intended_for_recipient?(mismatched.export)).to eq (false)
191
123
  end
192
124
 
193
- it "should return false if the message_data does not include a recipient" do
194
- broken = @test_message_data
195
- broken['recipient'] = nil
196
- iut = Smaak::AuthMessage.new(Smaak::build_message(broken))
197
- expect(iut.intended_for_recipient?(@test_server_public_key.export)).to eq (false)
198
- end
199
-
200
125
  it "should return false if the public key is not specified" do
201
126
  expect(@iut.intended_for_recipient?(nil)).to eq (false)
202
127
  end
@@ -206,28 +131,34 @@ describe Smaak::AuthMessage do
206
131
  end
207
132
  end
208
133
 
209
- context "when asked to verify the message using a signature" do
210
- before :each do
211
- @test_signature = Smaak::sign_message_data(@test_message_data, @test_server_private_key)
134
+ context "when asked to verify the message" do
135
+ it "should check message expiry and return false if the message has expired" do
136
+ expect(@iut).to(receive(:expired?)).and_return(true)
137
+ expect(@iut.verify(Smaak::Crypto::obfuscate_psk(@test_psk))).to eq(false)
212
138
  end
213
139
 
214
- it "should check message expiry return false if the message has expired" do
215
- expect(@iut).to(receive(:expired?)).and_return(true)
216
- expect(@iut.verify(@test_signature, @test_server_public_key, @test_psk)).to eq(false)
140
+ it "should try and match the PSK and return false if it cannot" do
141
+ expect(@iut).to(receive(:psk_match?)).and_return(false)
142
+ expect(@iut.verify(Smaak::Crypto::obfuscate_psk(@test_psk))).to eq(false)
217
143
  end
218
144
 
219
- it "should check the signature and return false if not OK" do
220
- expect(@iut).to(receive(:signature_ok?)).and_return(false)
221
- expect(@iut.verify(@test_signature, @test_server_public_key, @test_psk)).to eq(false)
145
+ it "should return true if the message was successfully verified" do
146
+ expect(@iut.verify(@test_psk)).to eq(true)
222
147
  end
148
+ end
223
149
 
224
- it "should try and match the PSK and return false if it cannot" do
225
- expect(@iut).to(receive(:psk_match?)).and_return(false)
226
- expect(@iut.verify(@test_signature, @test_server_public_key, @test_psk)).to eq(false)
150
+ context "when asked to create an AuthMessage from scratch" do
151
+ it "should initialize with the recipient_public_key, psk, expires, identifier, nonce, encrypt provided, calculating expiry, generating a nonce, and obfuscating the PSK" do
152
+ allow(Smaak::Crypto).to receive(:generate_nonce).and_return(@test_nonce)
153
+ expect(Smaak::AuthMessage).to receive(:new).with(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, @test_encrypt)
154
+ Smaak::AuthMessage.create(@test_recipient, @test_psk, @test_token_life, @test_identifier, @test_encrypt)
227
155
  end
156
+ end
228
157
 
229
- it "should return the identity specified in the message if the message was successfully verified" do
230
- expect(@iut.verify(@test_signature, @test_server_public_key, @test_psk)).to eq(@test_identity)
158
+ context "when asked to build an AuthMessage from existing data" do
159
+ it "should initialize with the recipient_public_key, psk, expires, identifier, nonce, encrypt provided" do
160
+ expect(Smaak::AuthMessage).to receive(:new).with(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, @test_encrypt)
161
+ Smaak::AuthMessage.build(@test_recipient, Smaak::Crypto::obfuscate_psk(@test_psk), @test_expires, @test_identifier, @test_nonce, @test_encrypt)
231
162
  end
232
163
  end
233
164
  end
@@ -0,0 +1,231 @@
1
+ require './spec/spec_helper.rb'
2
+
3
+ describe Smaak::Cavage04 do
4
+ context "when signing headers" do
5
+ before :all do
6
+ @test_server_private_key = OpenSSL::PKey::RSA.new(4096)
7
+ end
8
+
9
+ before :each do
10
+ @request = Net::HTTP::Post.new("http://rubygems.org:80/gems/smaak")
11
+ @request.body = "body-data"
12
+ @adaptor = Smaak::NetHttpAdaptor.new(@request)
13
+ @iut = Smaak::Cavage04.new(@adaptor)
14
+ @test_token_life = 10
15
+ @test_nonce = 1234567890
16
+ @test_expires = Time.now.to_i + @test_token_life
17
+ @test_psk = "testpresharedkey"
18
+ @test_server_public_key = @test_server_private_key.public_key
19
+ @test_identity = "test-service"
20
+ @test_identifier = 'test-service-1.cpt1.host-h.net'
21
+ @test_recipient = @test_server_public_key.export
22
+ @test_encrypt = true
23
+ @auth_message = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, @test_encrypt)
24
+ end
25
+
26
+ context "as a specification implementation" do
27
+ it "should publish the specification that it is based on" do
28
+ expect(Smaak::Cavage04::SPECIFICATION.nil?).to eq(false)
29
+ expect(Smaak::Cavage04::SPECIFICATION.is_a? String).to eq(true)
30
+ end
31
+
32
+ it "should provide a list of headers to be signed" do
33
+ expect(Smaak::Cavage04::headers_to_be_signed.include? "host").to eq(true)
34
+ expect(Smaak::Cavage04::headers_to_be_signed.include? "date").to eq(true)
35
+ expect(Smaak::Cavage04::headers_to_be_signed.include? "digest").to eq(true)
36
+ expect(Smaak::Cavage04::headers_to_be_signed.include? "content-length").to eq(true)
37
+ end
38
+
39
+ it "should indicate that the specification specific header (request-target) is to be signed" do
40
+ expect(Smaak::Cavage04::headers_to_be_signed.include? "(request-target)").to eq(true)
41
+ end
42
+ end
43
+
44
+ context "when initialized" do
45
+ it "should raise an ArgumentError if no adaptor is provided" do
46
+ expect {
47
+ Smaak::Cavage04.new(nil)
48
+ }.to raise_error ArgumentError, "Must provide a valid request adaptor"
49
+ end
50
+
51
+ it "should remember an adaptor provided" do
52
+ expect(@iut.adaptor).to eq(@adaptor)
53
+ end
54
+
55
+ it "should set its headers to be signed to that of the specification and that of Smaak" do
56
+ Smaak::Cavage04::headers_to_be_signed.each do |header|
57
+ expect(@iut.headers_to_be_signed.include? header).to eq(true)
58
+ end
59
+ Smaak::headers_to_be_signed.each do |header|
60
+ expect(@iut.headers_to_be_signed.include? header).to eq(true)
61
+ end
62
+ end
63
+ end
64
+
65
+ context "when asked to compile the authorization header with signature speficied" do
66
+ it "should raise an ArgumentError if the signature is invalid" do
67
+ expect {
68
+ @iut.compile_auth_header(nil)
69
+ }.to raise_error ArgumentError, "invalid signature"
70
+ expect {
71
+ @iut.compile_auth_header("")
72
+ }.to raise_error ArgumentError, "invalid signature"
73
+ expect {
74
+ @iut.compile_auth_header(" ")
75
+ }.to raise_error ArgumentError, "invalid signature"
76
+ expect {
77
+ @iut.compile_auth_header(978)
78
+ }.to raise_error ArgumentError, "invalid signature"
79
+ end
80
+
81
+ context "when compiled" do
82
+ before :each do
83
+ @iut.adaptor.set_header("x-smaak-nonce", "12345")
84
+ @iut.adaptor.set_header("x-smaak-encrypt", "true")
85
+ @iut.compile_auth_header("signature")
86
+ end
87
+
88
+ it "should indicate the use of RSA keys" do
89
+ expect(@iut.adaptor.request["authorization"].include?("keyId=\"rsa-key-1\"")).to eq(true)
90
+ end
91
+
92
+ it "should indicate the use of the rsa-sha256 algorithm" do
93
+ expect(@iut.adaptor.request["authorization"].include?("algorithm=\"rsa-sha256\"")).to eq(true)
94
+ end
95
+
96
+ it "should insert the signature in the header" do
97
+ expect(@iut.adaptor.request["authorization"].include?("signature=\"signature\"")).to eq(true)
98
+ end
99
+
100
+ it "should specify the order in which the headers that are signed appear in the request so that the signature can be verified by the receiver" do
101
+ expect(@iut.adaptor.request["authorization"].include?("\"x-smaak-nonce x-smaak-encrypt\"")).to eq(true)
102
+ end
103
+
104
+ it "should set the authorization header on the adaptor to the compiled header" do
105
+ expect(@iut.adaptor.request["authorization"]).to eq("Signature keyId=\"rsa-key-1\",algorithm=\"rsa-sha256\", headers=\"x-smaak-nonce x-smaak-encrypt\", signature=\"signature\"")
106
+ end
107
+ end
108
+ end
109
+
110
+ context "when asked to compile the headers to be included in the signature" do
111
+ it "should use an empty string as body if the body is not provided" do
112
+ @adaptor.request.body = nil
113
+ expect(Digest::SHA256).to receive(:hexdigest).with("").and_return(Digest::SHA256.hexdigest(""))
114
+ @iut.compile_signature_headers(@auth_message)
115
+ end
116
+
117
+ it "should set the authorization header to an empty string to preserve it at the top of the request headers list, in order not to interfere with the remaining headers' order" do
118
+ @iut.compile_signature_headers(@auth_message)
119
+ expect(@iut.adaptor.request["authorization"]).to eq("")
120
+ end
121
+
122
+ it "should set the host header to the adaptor request's host" do
123
+ @iut.compile_signature_headers(@auth_message)
124
+ expect(@iut.adaptor.request["host"]).to eq("rubygems.org")
125
+ end
126
+
127
+ it "should set the date header to the current timestamp, in GMT" do
128
+ @iut.compile_signature_headers(@auth_message)
129
+ expect(@iut.adaptor.request["date"]).to eq(Time.now.gmtime.to_s.gsub("UTC", "GMT"))
130
+ end
131
+
132
+ it "should set the digest header to a SHA256 hexadecimal digest of the body, labeled with SHA-256=" do
133
+ @iut.compile_signature_headers(@auth_message)
134
+ expect(@iut.adaptor.request["digest"]).to eq("SHA-256=#{Digest::SHA256.hexdigest(@request.body)}")
135
+ end
136
+
137
+ it "should set the x-smaak-recipient header to the base64 encoding of the message recipient's public key (source: auth_message), with no newlines" do
138
+ @iut.compile_signature_headers(@auth_message)
139
+ expect(@iut.adaptor.request["x-smaak-recipient"]).to eql(Base64.strict_encode64(@auth_message.recipient))
140
+ end
141
+
142
+ it "should set the x-smaak-psk header to the obfuscated pre-shared key (source: auth_message)" do
143
+ @iut.compile_signature_headers(@auth_message)
144
+ expect(@iut.adaptor.request["x-smaak-psk"]).to eql(@auth_message.psk)
145
+ end
146
+
147
+ it "should set the x-smaak-expires header to the expiry (source: auth_message)" do
148
+ @iut.compile_signature_headers(@auth_message)
149
+ expect(@iut.adaptor.request["x-smaak-expires"]).to eql("#{@auth_message.expires}")
150
+ end
151
+
152
+ it "should set the x-smaak-nonce header to the nonce (source: auth_message)" do
153
+ @iut.compile_signature_headers(@auth_message)
154
+ expect(@iut.adaptor.request["x-smaak-nonce"]).to eql("#{@auth_message.nonce}")
155
+ end
156
+
157
+ it "should set the x-smaak-encrypt header to the encryption choice (source: auth_message)" do
158
+ @iut.compile_signature_headers(@auth_message)
159
+ expect(@iut.adaptor.request["x-smaak-encrypt"]).to eql("#{@auth_message.encrypt}")
160
+ end
161
+
162
+ it "should set the content-type to text/plain" do
163
+ @iut.compile_signature_headers(@auth_message)
164
+ expect(@iut.adaptor.request["content-type"]).to eql("text/plain")
165
+ end
166
+
167
+ it "should set the content-length to the length of the adaptor's request body" do
168
+ @iut.compile_signature_headers(@auth_message)
169
+ expect(@iut.adaptor.request["content-length"]).to eql("#{@request.body.size}")
170
+ end
171
+
172
+ it "should compile and return (preserving insert order) the list of signature headers, pre-pended with the (request-target) header" do
173
+ headers = @iut.compile_signature_headers(@auth_message).split("\n")
174
+ expect(headers[0].split(":")[0]).to eql("(request-target)")
175
+ expect(headers[1].split(":")[0]).to eql("host")
176
+ expect(headers[2].split(":")[0]).to eql("date")
177
+ expect(headers[3].split(":")[0]).to eql("digest")
178
+ expect(headers[4].split(":")[0]).to eql("x-smaak-recipient")
179
+ expect(headers[5].split(":")[0]).to eql("x-smaak-identifier")
180
+ expect(headers[6].split(":")[0]).to eql("x-smaak-psk")
181
+ expect(headers[7].split(":")[0]).to eql("x-smaak-expires")
182
+ expect(headers[8].split(":")[0]).to eql("x-smaak-nonce")
183
+ expect(headers[9].split(":")[0]).to eql("x-smaak-encrypt")
184
+ expect(headers[10].split(":")[0]).to eql("content-length")
185
+ end
186
+
187
+ it "should not include int he list of signature headers non-signature headers" do
188
+ content_type_present = false
189
+ headers = @iut.compile_signature_headers(@auth_message).split("\n")
190
+ headers.each do |header|
191
+ content_type_present = true if header.split(":")[0] == "content-type"
192
+ end
193
+ expect(content_type_present).to eql(false)
194
+ end
195
+ end
196
+ end
197
+
198
+ context "when receiving a signed header" do
199
+ before :each do
200
+ @env = \
201
+ {"CONTENT_LENGTH"=>"25", "CONTENT_TYPE"=>"text/plain", "GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/secure-service", "QUERY_STRING"=>"", "REMOTE_ADDR"=>"10.0.0.224", "REMOTE_HOST"=>"service-provider-public", "REQUEST_METHOD"=>"POST", "REQUEST_URI"=>"http://service-provider-internal:9393/secure-service", "SCRIPT_NAME"=>"", "SERVER_NAME"=>"service-provider-internal", "SERVER_PORT"=>"9393", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/2.0.0/2014-02-24)", "HTTP_ACCEPT_ENCODING"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "HTTP_ACCEPT"=>"*/*", "HTTP_USER_AGENT"=>"Ruby", "HTTP_AUTHORIZATION"=>"Signature keyId=\"rsa-key-1\",algorithm=\"rsa-sha256\", headers=\"host date digest x-smaak-recipient x-smaak-identifier x-smaak-psk x-smaak-expires x-smaak-nonce x-smaak-encrypt content-length\", signature=\"RQgXQo+Fugz1ubgV1UAJvdPaNHiwTMtu0x+LNJ/7rvY5gaY5R88tUPtcFMzjRzw2QXtY5pettjfbq9LvISnW5MFG7p+goY4YsF4a6b7KgbU8RCAMLVyj4zWEIh/R+3WovuhcG8e5iLGN5/HGHkgDjZzi1a2WwU+tcwSwKBQ0BN+hKUV6haAHxUcNJ8bOgtnZZpSbD0megEmmBwiOjY5EsdM9wFMqGRrBWYV950xs/cPgO7Hjgq4kTnBiFC8Zkcz5zmkkokVE6VliNSPrqIZHm4fGk9UWyDYydlE+4z/wa4KrDs7/JXCQh+HF+BfSlnhG1xm9UT857o8Uz3j8ds4hvzUJyVcHX5B7wFln5szSFz5cdNFdMq6RP3e/TWGEV9J3sWi3pLymQog9jfkS1sjBSUxlc0Nh1hyiBFjybPZcbx6L77hsYV7dnCKF1z5UItvNj2JOkUCe+ppDkfhNxNkSUv9KBir+U+xJwDh+uyO/IAj8TB0cklsdnJNNHCDA4Mmi59RnA6uMsjOo6j7btkRF8nZmDvq0AWmgIUnwIWNWt13ecBH6u1Y03s5D09gX8sILKWuhC4oGEzjE7gBxrORn/MSPNAwAOsx/3ud4PFlOa7DGKApolpL0099w5QgFDqDYALujDdZC2GNgHCdoJqNLoMCEkyVWArvvgxtQ4Xq/0zU=\"", "HTTP_HOST"=>"service-provider-internal", "HTTP_DATE"=>"2015-06-23 13:40:07 GMT", "HTTP_DIGEST"=>"SHA-256=748957b58cc24d2bb9eb8f9c468571712a14f6a89ce936c0fb2d3c5016e4dbdc", "HTTP_X_SMAAK_RECIPIENT"=>"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxL2tiYjdBNWllQWV1WlBBVnI3MAo5cjl1TkFzc2dmYkdjeGMzZTc3RDNndkY4U2tzbURNQmQyTUt5TUh0ZjBrM1pqSVdZemJJVG5jQXM1Nnd4cmRSClhiVHpIZnhjMll1dDMwd0ljR2YvUVk4ZTJXNmdMWko4aVM3MXlYb0JQNFpEc2lLSXd4ajFsenYyVFlXWnNSL3EKd28xSzBxZ1NzOXJJVEVkWDVqampycHBYWTdobHNPMGVKQ2JBRG0weEtnU1hMcFQycnJzUnJ2OFllRXFvZTRMaQpDOFd6RjZZRlh1U3RHR1E4SXlxbjdPaTN5aVU2WFc3OTl2cFpIeHJlaERYaytDalZuU0ZXWkVPUHg3cENpam9SCnlXb0gyUmR6QVpQczdVdVJWOUdGWWFQeHRudmttNVdVZDVTdWVCNlMxT2E4dVZ3UnpyeXl6WkRjdG0xdWs1VjIKUE0zLzFqbFJMbFJzTWxSeHdZUDRzaFMzVlhjTkdGYjkvbzkvTjkzbitKZUFpSGd4YU5pQjN6YVV0a05XWWs0Vgozang2d0psTythOUNxdGJJeXg2ZzdyTHhOanVqRFpRZTZGcUdsMzVkVDR5MHA2UmVuUWQ4b1p5aWw3dlpqSkJaCjluTWRJblMyU05wWUZFclBsb25rdXNZKzZsam9TbFNLMXVSRmd2S3dzeGE3RmROMXZWSnRJQk9qdVJzSk9DaHYKOTB2K0ZEQWwxSnNZVUNPUnByUmtMWXB2TWI4Q1BZaUlzb3JmTUdKNnI3NktYUEIzRS9xejRmaWJ1UmZVeWJxMgp5eGxRTVJKb216d1BPemUrbWRQUU5Hd3VTTjU0VnByYXhoNGFpcWtaUVBsSWpRb1dFaFVKRWxMb0NtQXZ4TmtxCmRBcVZJMXZ3cS9FRXFBTEh3amJKRXIwQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", "HTTP_X_SMAAK_IDENTIFIER"=>"service-provider-public", "HTTP_X_SMAAK_PSK"=>"917e5f9bcf6d7c20a338d8a39bbf79ef", "HTTP_X_SMAAK_EXPIRES"=>"1435066809", "HTTP_X_SMAAK_NONCE"=>"6457661831", "HTTP_X_SMAAK_ENCRYPT"=>"false", "HTTP_CONNECTION"=>"close"}
202
+ @request = Rack::Request.new(@env)
203
+ @adaptor = Smaak::RackAdaptor.new(@request)
204
+ @iut = Smaak::Cavage04.new(@adaptor)
205
+ end
206
+
207
+ context "when asked to extract signature headers from a request" do
208
+ it "should find the signature headers list in the authorization header return them separated using spaces" do
209
+ expect(@iut.extract_signature_headers).to eq(\
210
+ "(request-target): post /secure-service\nhost: service-provider-internal\ndate: 2015-06-23 13:40:07 GMT\ndigest: SHA-256=748957b58cc24d2bb9eb8f9c468571712a14f6a89ce936c0fb2d3c5016e4dbdc\nx-smaak-recipient: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxL2tiYjdBNWllQWV1WlBBVnI3MAo5cjl1TkFzc2dmYkdjeGMzZTc3RDNndkY4U2tzbURNQmQyTUt5TUh0ZjBrM1pqSVdZemJJVG5jQXM1Nnd4cmRSClhiVHpIZnhjMll1dDMwd0ljR2YvUVk4ZTJXNmdMWko4aVM3MXlYb0JQNFpEc2lLSXd4ajFsenYyVFlXWnNSL3EKd28xSzBxZ1NzOXJJVEVkWDVqampycHBYWTdobHNPMGVKQ2JBRG0weEtnU1hMcFQycnJzUnJ2OFllRXFvZTRMaQpDOFd6RjZZRlh1U3RHR1E4SXlxbjdPaTN5aVU2WFc3OTl2cFpIeHJlaERYaytDalZuU0ZXWkVPUHg3cENpam9SCnlXb0gyUmR6QVpQczdVdVJWOUdGWWFQeHRudmttNVdVZDVTdWVCNlMxT2E4dVZ3UnpyeXl6WkRjdG0xdWs1VjIKUE0zLzFqbFJMbFJzTWxSeHdZUDRzaFMzVlhjTkdGYjkvbzkvTjkzbitKZUFpSGd4YU5pQjN6YVV0a05XWWs0Vgozang2d0psTythOUNxdGJJeXg2ZzdyTHhOanVqRFpRZTZGcUdsMzVkVDR5MHA2UmVuUWQ4b1p5aWw3dlpqSkJaCjluTWRJblMyU05wWUZFclBsb25rdXNZKzZsam9TbFNLMXVSRmd2S3dzeGE3RmROMXZWSnRJQk9qdVJzSk9DaHYKOTB2K0ZEQWwxSnNZVUNPUnByUmtMWXB2TWI4Q1BZaUlzb3JmTUdKNnI3NktYUEIzRS9xejRmaWJ1UmZVeWJxMgp5eGxRTVJKb216d1BPemUrbWRQUU5Hd3VTTjU0VnByYXhoNGFpcWtaUVBsSWpRb1dFaFVKRWxMb0NtQXZ4TmtxCmRBcVZJMXZ3cS9FRXFBTEh3amJKRXIwQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=\nx-smaak-identifier: service-provider-public\nx-smaak-psk: 917e5f9bcf6d7c20a338d8a39bbf79ef\nx-smaak-expires: 1435066809\nx-smaak-nonce: 6457661831\nx-smaak-encrypt: false\ncontent-length: 25")
211
+ end
212
+
213
+ it "should prepend the (request-target) header" do
214
+ expect(@iut.extract_signature_headers[0..15]).to eq("(request-target)")
215
+ end
216
+
217
+ it "should return the signature headers in the order expressed in the signature, so that signature verification can succeed" do
218
+ #host date digest x-smaak-recipient x-smaak-identifier x-smaak-psk x-smaak-expires x-smaak-nonce x-smaak-encrypt content-length
219
+ expect(@iut.extract_signature_headers).to eq(\
220
+ "(request-target): post /secure-service\nhost: service-provider-internal\ndate: 2015-06-23 13:40:07 GMT\ndigest: SHA-256=748957b58cc24d2bb9eb8f9c468571712a14f6a89ce936c0fb2d3c5016e4dbdc\nx-smaak-recipient: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxL2tiYjdBNWllQWV1WlBBVnI3MAo5cjl1TkFzc2dmYkdjeGMzZTc3RDNndkY4U2tzbURNQmQyTUt5TUh0ZjBrM1pqSVdZemJJVG5jQXM1Nnd4cmRSClhiVHpIZnhjMll1dDMwd0ljR2YvUVk4ZTJXNmdMWko4aVM3MXlYb0JQNFpEc2lLSXd4ajFsenYyVFlXWnNSL3EKd28xSzBxZ1NzOXJJVEVkWDVqampycHBYWTdobHNPMGVKQ2JBRG0weEtnU1hMcFQycnJzUnJ2OFllRXFvZTRMaQpDOFd6RjZZRlh1U3RHR1E4SXlxbjdPaTN5aVU2WFc3OTl2cFpIeHJlaERYaytDalZuU0ZXWkVPUHg3cENpam9SCnlXb0gyUmR6QVpQczdVdVJWOUdGWWFQeHRudmttNVdVZDVTdWVCNlMxT2E4dVZ3UnpyeXl6WkRjdG0xdWs1VjIKUE0zLzFqbFJMbFJzTWxSeHdZUDRzaFMzVlhjTkdGYjkvbzkvTjkzbitKZUFpSGd4YU5pQjN6YVV0a05XWWs0Vgozang2d0psTythOUNxdGJJeXg2ZzdyTHhOanVqRFpRZTZGcUdsMzVkVDR5MHA2UmVuUWQ4b1p5aWw3dlpqSkJaCjluTWRJblMyU05wWUZFclBsb25rdXNZKzZsam9TbFNLMXVSRmd2S3dzeGE3RmROMXZWSnRJQk9qdVJzSk9DaHYKOTB2K0ZEQWwxSnNZVUNPUnByUmtMWXB2TWI4Q1BZaUlzb3JmTUdKNnI3NktYUEIzRS9xejRmaWJ1UmZVeWJxMgp5eGxRTVJKb216d1BPemUrbWRQUU5Hd3VTTjU0VnByYXhoNGFpcWtaUVBsSWpRb1dFaFVKRWxMb0NtQXZ4TmtxCmRBcVZJMXZ3cS9FRXFBTEh3amJKRXIwQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=\nx-smaak-identifier: service-provider-public\nx-smaak-psk: 917e5f9bcf6d7c20a338d8a39bbf79ef\nx-smaak-expires: 1435066809\nx-smaak-nonce: 6457661831\nx-smaak-encrypt: false\ncontent-length: 25")
221
+ end
222
+ end
223
+
224
+ context "when asked to extract the signature from a request" do
225
+ it "should extract only the signature field from the request" do
226
+ expect(@iut.extract_signature).to eq("RQgXQo+Fugz1ubgV1UAJvdPaNHiwTMtu0x+LNJ/7rvY5gaY5R88tUPtcFMzjRzw2QXtY5pettjfbq9LvISnW5MFG7p+goY4YsF4a6b7KgbU8RCAMLVyj4zWEIh/R+3WovuhcG8e5iLGN5/HGHkgDjZzi1a2WwU+tcwSwKBQ0BN+hKUV6haAHxUcNJ8bOgtnZZpSbD0megEmmBwiOjY5EsdM9wFMqGRrBWYV950xs/cPgO7Hjgq4kTnBiFC8Zkcz5zmkkokVE6VliNSPrqIZHm4fGk9UWyDYydlE+4z/wa4KrDs7/JXCQh+HF+BfSlnhG1xm9UT857o8Uz3j8ds4hvzUJyVcHX5B7wFln5szSFz5cdNFdMq6RP3e/TWGEV9J3sWi3pLymQog9jfkS1sjBSUxlc0Nh1hyiBFjybPZcbx6L77hsYV7dnCKF1z5UItvNj2JOkUCe+ppDkfhNxNkSUv9KBir+U+xJwDh+uyO/IAj8TB0cklsdnJNNHCDA4Mmi59RnA6uMsjOo6j7btkRF8nZmDvq0AWmgIUnwIWNWt13ecBH6u1Y03s5D09gX8sILKWuhC4oGEzjE7gBxrORn/MSPNAwAOsx/3ud4PFlOa7DGKApolpL0099w5QgFDqDYALujDdZC2GNgHCdoJqNLoMCEkyVWArvvgxtQ4Xq/0zU=")
227
+ end
228
+ end
229
+ end
230
+ end
231
+