smaak 0.0.10 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +112 -53
- data/lib/smaak/adaptors/net_http_adaptor.rb +43 -0
- data/lib/smaak/adaptors/rack_adaptor.rb +32 -0
- data/lib/smaak/associate.rb +12 -6
- data/lib/smaak/auth_message.rb +55 -39
- data/lib/smaak/cavage_04.rb +84 -0
- data/lib/smaak/client.rb +38 -21
- data/lib/smaak/crypto.rb +45 -0
- data/lib/smaak/server.rb +33 -16
- data/lib/smaak/smaak_service.rb +29 -0
- data/lib/smaak/utils.rb +10 -0
- data/lib/smaak/version.rb +1 -1
- data/lib/smaak.rb +58 -18
- data/smaak.gemspec +1 -0
- data/spec/lib/smaak/adaptors/net_http_adaptor_spec.rb +99 -0
- data/spec/lib/smaak/adaptors/rack_adaptor_spec.rb +83 -0
- data/spec/lib/smaak/auth_message_spec.rb +78 -147
- data/spec/lib/smaak/cavage_04_spec.rb +231 -0
- data/spec/lib/smaak/client_spec.rb +147 -34
- data/spec/lib/smaak/crypto_spec.rb +94 -0
- data/spec/lib/smaak/server_spec.rb +172 -22
- data/spec/lib/smaak/smaak_service_spec.rb +52 -0
- data/spec/lib/smaak_spec.rb +130 -52
- data/spec/spec_helper.rb +3 -0
- metadata +32 -3
- data/lib/smaak/request_signing_validator.rb +0 -20
@@ -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 =
|
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
|
-
@
|
15
|
-
|
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
|
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
|
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
|
36
|
-
expect
|
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
|
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
|
-
|
50
|
-
|
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
|
-
|
54
|
-
|
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
|
59
|
-
expect{
|
60
|
-
@
|
61
|
-
}.to raise_error
|
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
|
65
|
-
expect{
|
66
|
-
@
|
67
|
-
}.to raise_error
|
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
|
71
|
-
expect(@iut.
|
59
|
+
it "should remember the identifier provided" do
|
60
|
+
expect(@iut.identifier).to eq(@test_identifier)
|
72
61
|
end
|
73
62
|
|
74
|
-
it "should
|
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
|
81
|
-
|
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
|
-
|
86
|
-
|
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
|
100
|
-
expect(@iut.
|
75
|
+
it "should remember the recipient provided" do
|
76
|
+
expect(@iut.recipient).to eq(@test_recipient)
|
101
77
|
end
|
102
78
|
|
103
|
-
it "should
|
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
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
expect
|
118
|
-
|
119
|
-
|
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
|
-
|
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(
|
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
|
210
|
-
|
211
|
-
|
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
|
215
|
-
expect(@iut).to(receive(:
|
216
|
-
expect(@iut.verify(@
|
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
|
220
|
-
expect(@iut
|
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
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
230
|
-
|
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
|
+
|