smaak 0.0.10 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/spec/lib/smaak_spec.rb
CHANGED
@@ -1,88 +1,166 @@
|
|
1
1
|
require './spec/spec_helper.rb'
|
2
|
+
require 'net/http'
|
2
3
|
|
3
4
|
describe Smaak do
|
5
|
+
before :all do
|
6
|
+
@test_server_private_key = OpenSSL::PKey::RSA.new(4096)
|
7
|
+
@test_server_public_key = @test_server_private_key.public_key
|
8
|
+
@request = Net::HTTP::Post.new("http://rubygems.org:80/gems/smaak")
|
9
|
+
@request.body = "body-data"
|
10
|
+
@test_nonce = 1234567890
|
11
|
+
@test_expires = Time.now.to_i + 5
|
12
|
+
@test_psk = "testpresharedkey"
|
13
|
+
@test_identifier = 'test-service-1.cpt1.host-h.net'
|
14
|
+
@test_recipient = @test_server_public_key.export
|
15
|
+
@test_encrypt = true
|
16
|
+
@auth_message = Smaak::AuthMessage.new(@test_identifier, @test_nonce, @test_expires, Smaak::Crypto::obfuscate_psk(@test_psk), @test_recipient, @test_encrypt)
|
17
|
+
@adaptor = Smaak::create_adaptor(@request)
|
18
|
+
@mock_specification = Smaak::Cavage04.new(@adaptor)
|
19
|
+
end
|
20
|
+
|
4
21
|
context "when loaded" do
|
5
22
|
it "should specify a default token life" do
|
6
23
|
expect(Smaak::DEFAULT_TOKEN_LIFE).to eq(2)
|
7
24
|
end
|
8
25
|
end
|
9
26
|
|
10
|
-
context "when asked
|
11
|
-
it "should
|
12
|
-
expect(Smaak::
|
27
|
+
context "when asked which headers should be signed" do
|
28
|
+
it "should list all the smaak header extensions" do
|
29
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-recipient")).to eql(true)
|
30
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-identifier")).to eql(true)
|
31
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-psk")).to eql(true)
|
32
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-expires")).to eql(true)
|
33
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-nonce")).to eql(true)
|
34
|
+
expect(Smaak::headers_to_be_signed.include?("x-smaak-encrypt")).to eql(true)
|
13
35
|
end
|
14
36
|
end
|
15
37
|
|
16
|
-
context "when
|
17
|
-
it "should
|
18
|
-
|
19
|
-
expect(Smaak::
|
38
|
+
context "when told about a new request adaptor" do
|
39
|
+
it "should remember the adaptor class associated with the request class" do
|
40
|
+
Smaak::add_request_adaptor(Integer, String)
|
41
|
+
expect(Smaak::adaptors[Integer]).to eql(String)
|
20
42
|
end
|
21
43
|
end
|
22
44
|
|
23
|
-
context "when asked to
|
24
|
-
it "should
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
45
|
+
context "when asked to create a request adaptor" do
|
46
|
+
it "should raise an ArgumentError if the request type does not have an adaptor configured" do
|
47
|
+
expect {
|
48
|
+
Smaak::create_adaptor(0.1)
|
49
|
+
}.to raise_error ArgumentError, "Unknown request class Float. Add an adaptor using Smaak::add_request_adaptor."
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should create a new instance of the adaptor class specified in the request adaptor dictionary" do
|
53
|
+
adaptor = Smaak::create_adaptor(Net::HTTP::Post.new("http://rubygems.org:80/gems/smaak"))
|
54
|
+
expect(adaptor.is_a? Smaak::NetHttpAdaptor).to eql(true)
|
30
55
|
end
|
31
56
|
end
|
32
57
|
|
33
|
-
context "when asked to
|
34
|
-
it "should
|
35
|
-
expect
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
failed_p = (failed.to_f / threshold) * 100
|
49
|
-
puts "I've seen #{failed_p} % of nonces before in #{threshold} generations"
|
50
|
-
expect(failed_p < 0.01).to eq(true)
|
58
|
+
context "when asked to select a header signature specification" do
|
59
|
+
it "should raise an ArgumentError if the specification is unknown" do
|
60
|
+
expect {
|
61
|
+
Smaak::select_specification(@adaptor, "unknown specification")
|
62
|
+
}.to raise_error ArgumentError, "Unknown specification"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return an instance of a known specification" do
|
66
|
+
expect(Smaak::select_specification(@adaptor, Smaak::Cavage04::SPECIFICATION).is_a?(Smaak::Cavage04)).to eql(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should raise an ArgumentError if the adaptor specified is nil" do
|
70
|
+
expect {
|
71
|
+
Smaak::select_specification(nil, Smaak::Cavage04::SPECIFICATION)
|
72
|
+
}.to raise_error ArgumentError, "Adaptor must be provided"
|
51
73
|
end
|
52
74
|
end
|
53
75
|
|
54
|
-
context "when asked to
|
55
|
-
|
56
|
-
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
76
|
+
context "when asked to sign authorization headers given a key, auth_message, request adaptor and specification" do
|
77
|
+
it "should select the requested specification" do
|
78
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
79
|
+
Smaak::sign_authorization_headers(@test_server_private_key, @auth_message, @adaptor, Smaak::Cavage04::SPECIFICATION)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should compile the signature header from the auth_message using the specification" do
|
83
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
84
|
+
expect(@mock_specification).to receive(:compile_signature_headers).with(@auth_message).and_return "headers"
|
85
|
+
Smaak::sign_authorization_headers(@test_server_private_key, @auth_message, @adaptor, Smaak::Cavage04::SPECIFICATION)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should sign the signature headers using the key" do
|
89
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
90
|
+
expect(@mock_specification).to receive(:compile_signature_headers).with(@auth_message).and_return "headers"
|
91
|
+
expect(Smaak::Crypto).to receive(:sign_data).with("headers", @test_server_private_key).and_return("signed headers")
|
92
|
+
Smaak::sign_authorization_headers(@test_server_private_key, @auth_message, @adaptor, Smaak::Cavage04::SPECIFICATION)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should compile an auth header using the signature as the signature data base 64 encoded" do
|
96
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
97
|
+
expect(@mock_specification).to receive(:compile_signature_headers).with(@auth_message).and_return "headers"
|
98
|
+
expect(Smaak::Crypto).to receive(:sign_data).with("headers", @test_server_private_key).and_return("signed headers")
|
99
|
+
expect(@mock_specification).to receive(:compile_auth_header).with(Base64::strict_encode64("signed headers"))
|
100
|
+
Smaak::sign_authorization_headers(@test_server_private_key, @auth_message, @adaptor, Smaak::Cavage04::SPECIFICATION)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should return the adapter" do
|
104
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
105
|
+
expect(Smaak::sign_authorization_headers(@test_server_private_key, @auth_message, @adaptor, Smaak::Cavage04::SPECIFICATION)).to eql(@adaptor)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when asked if signed authorization headers are ok given a public key" do
|
110
|
+
it "should select the requested specification" do
|
111
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
112
|
+
expect {
|
113
|
+
Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)
|
114
|
+
}.to raise_error
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should extract the signature headers using the specification" do
|
118
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
119
|
+
expect(@mock_specification).to receive(:extract_signature_headers).and_return "headers"
|
120
|
+
expect {
|
121
|
+
Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)
|
122
|
+
}.to raise_error
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should extract the signature using the specification" do
|
126
|
+
expect(Smaak::Cavage04).to receive(:new).and_return(@mock_specification)
|
127
|
+
expect(@mock_specification).to receive(:extract_signature_headers).and_return "headers"
|
128
|
+
expect(@mock_specification).to receive(:extract_signature).and_return "signature"
|
129
|
+
Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)
|
61
130
|
end
|
62
131
|
|
63
|
-
|
64
|
-
|
65
|
-
@
|
132
|
+
it "should return false if the signature is nil" do
|
133
|
+
expect(Smaak).to receive(:get_signature_data_from_request).with(@adaptor).and_return(["headers", nil])
|
134
|
+
expect(Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)).to eql(false)
|
66
135
|
end
|
67
136
|
|
68
|
-
it "should
|
69
|
-
expect(
|
137
|
+
it "should return false if the signature headers are nil" do
|
138
|
+
expect(Smaak).to receive(:get_signature_data_from_request).with(@adaptor).and_return([nil, "signature"])
|
139
|
+
expect(Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)).to eql(false)
|
70
140
|
end
|
71
141
|
|
72
|
-
it "should
|
73
|
-
expect
|
142
|
+
it "should raise an ArgumentError if the public key is nil" do
|
143
|
+
expect {
|
144
|
+
Smaak::verify_authorization_headers(@adaptor, nil)
|
145
|
+
}.to raise_error ArgumentError, "Key is required"
|
74
146
|
end
|
75
147
|
|
76
|
-
it "should
|
77
|
-
expect(
|
148
|
+
it "should verify the signature using the signature headers base 64 encoded, with the public key. I.e. can I produce the same signature?" do
|
149
|
+
expect(Smaak).to receive(:get_signature_data_from_request).with(@adaptor).and_return(["headers", "signature"])
|
150
|
+
expect(Smaak::Crypto).to receive(:verify_signature).with("signature", Smaak::Crypto::encode64("headers"), @test_server_public_key).and_return true
|
151
|
+
Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)
|
78
152
|
end
|
79
153
|
|
80
|
-
it "should
|
81
|
-
expect(
|
154
|
+
it "should return false if verification fails" do
|
155
|
+
expect(Smaak).to receive(:get_signature_data_from_request).with(@adaptor).and_return(["headers", "signature"])
|
156
|
+
expect(Smaak::Crypto).to receive(:verify_signature).with("signature", Smaak::Crypto::encode64("headers"), @test_server_public_key).and_return false
|
157
|
+
expect(Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)).to eql(false)
|
82
158
|
end
|
83
159
|
|
84
|
-
it "should
|
85
|
-
expect(
|
160
|
+
it "should return true if verification succeeds" do
|
161
|
+
expect(Smaak).to receive(:get_signature_data_from_request).with(@adaptor).and_return(["headers", "signature"])
|
162
|
+
expect(Smaak::Crypto).to receive(:verify_signature).with("signature", Smaak::Crypto::encode64("headers"), @test_server_public_key).and_return true
|
163
|
+
expect(Smaak::verify_authorization_headers(@adaptor, @test_server_public_key)).to eql(true)
|
86
164
|
end
|
87
165
|
end
|
88
166
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,6 +4,8 @@ require 'tempfile'
|
|
4
4
|
require 'simplecov'
|
5
5
|
require 'simplecov-rcov'
|
6
6
|
require 'byebug'
|
7
|
+
require 'net/http'
|
8
|
+
require 'rack/request'
|
7
9
|
|
8
10
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
9
11
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
@@ -20,6 +22,7 @@ require 'lib/smaak/associate.rb'
|
|
20
22
|
require 'lib/smaak/server.rb'
|
21
23
|
require 'lib/smaak/client.rb'
|
22
24
|
require 'lib/smaak/auth_message.rb'
|
25
|
+
require 'lib/smaak/smaak_service.rb'
|
23
26
|
|
24
27
|
RSpec.configure do |config|
|
25
28
|
config.run_all_when_everything_filtered = true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smaak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ernst van Graan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: persistent-cache
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - '>='
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rack
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
description: Signed Message Authentication and Authorization with Key validation
|
126
140
|
email:
|
127
141
|
- ernst.van.graan@hetzner.co.za
|
@@ -137,17 +151,27 @@ files:
|
|
137
151
|
- README.md
|
138
152
|
- Rakefile
|
139
153
|
- lib/smaak.rb
|
154
|
+
- lib/smaak/adaptors/net_http_adaptor.rb
|
155
|
+
- lib/smaak/adaptors/rack_adaptor.rb
|
140
156
|
- lib/smaak/associate.rb
|
141
157
|
- lib/smaak/auth_message.rb
|
158
|
+
- lib/smaak/cavage_04.rb
|
142
159
|
- lib/smaak/client.rb
|
143
|
-
- lib/smaak/
|
160
|
+
- lib/smaak/crypto.rb
|
144
161
|
- lib/smaak/server.rb
|
162
|
+
- lib/smaak/smaak_service.rb
|
163
|
+
- lib/smaak/utils.rb
|
145
164
|
- lib/smaak/version.rb
|
146
165
|
- smaak.gemspec
|
166
|
+
- spec/lib/smaak/adaptors/net_http_adaptor_spec.rb
|
167
|
+
- spec/lib/smaak/adaptors/rack_adaptor_spec.rb
|
147
168
|
- spec/lib/smaak/associate_spec.rb
|
148
169
|
- spec/lib/smaak/auth_message_spec.rb
|
170
|
+
- spec/lib/smaak/cavage_04_spec.rb
|
149
171
|
- spec/lib/smaak/client_spec.rb
|
172
|
+
- spec/lib/smaak/crypto_spec.rb
|
150
173
|
- spec/lib/smaak/server_spec.rb
|
174
|
+
- spec/lib/smaak/smaak_service_spec.rb
|
151
175
|
- spec/lib/smaak_spec.rb
|
152
176
|
- spec/mock/request.rb
|
153
177
|
- spec/spec_helper.rb
|
@@ -180,10 +204,15 @@ summary: 'This gems caters for both client and server side of a signed message i
|
|
180
204
|
turned on), Replay (nonce + expires), Forgery (signature), Masquerading (recipient
|
181
205
|
pub key check), Clear-text password compromise (MD5 pre-shared key)'
|
182
206
|
test_files:
|
207
|
+
- spec/lib/smaak/adaptors/net_http_adaptor_spec.rb
|
208
|
+
- spec/lib/smaak/adaptors/rack_adaptor_spec.rb
|
183
209
|
- spec/lib/smaak/associate_spec.rb
|
184
210
|
- spec/lib/smaak/auth_message_spec.rb
|
211
|
+
- spec/lib/smaak/cavage_04_spec.rb
|
185
212
|
- spec/lib/smaak/client_spec.rb
|
213
|
+
- spec/lib/smaak/crypto_spec.rb
|
186
214
|
- spec/lib/smaak/server_spec.rb
|
215
|
+
- spec/lib/smaak/smaak_service_spec.rb
|
187
216
|
- spec/lib/smaak_spec.rb
|
188
217
|
- spec/mock/request.rb
|
189
218
|
- spec/spec_helper.rb
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Smaak
|
2
|
-
class RequestSigningValidator
|
3
|
-
def validate(data)
|
4
|
-
raise ArgumentError.new("Request data needs to be a Hash") if not data.is_a? Hash
|
5
|
-
ensure_present(data, "HTTPRequestMethod")
|
6
|
-
ensure_present(data, "CanonicalURI")
|
7
|
-
ensure_present(data, "CanonicalQueryString")
|
8
|
-
ensure_present(data, "CanonicalHeaders")
|
9
|
-
ensure_present(data, "SignedHeaders")
|
10
|
-
ensure_present(data, "RequestPayload")
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def ensure_present(data, key)
|
16
|
-
# raise ArgumentError.new("Request data needs to include #{key}") if data[key].nil?
|
17
|
-
puts "WARNING: Request data needs to include #{key}" if data[key].nil?
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|