shmac 0.1.0 → 0.2.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/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +1 -0
- data/README.md +4 -0
- data/lib/shmac.rb +8 -6
- data/lib/shmac/authentication.rb +26 -5
- data/lib/shmac/authorization_header.rb +5 -1
- data/lib/shmac/request.rb +12 -5
- data/lib/shmac/security.rb +16 -0
- data/lib/shmac/signature_calculator.rb +5 -5
- data/lib/shmac/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34daa52fbfddb3e27abe5cd0c80da5cbfbb99fcd
|
4
|
+
data.tar.gz: 7b45ab0eff281a6385baa7c6a9911f87b9211148
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0eb6cb29738a08567fd3f5a394f9a1d48d8dabd522202d8c8355861764e9442a046d8304b512e8e00c8a66fea0ecaea3d23ca473cd768e7a31c254ec73d4ad77
|
7
|
+
data.tar.gz: e3b7c7edee5a3e11c71b146b8860e5ed39778f1cfbba0441f94a08c091b576bf70a49c914b43a87e78580a00ba914d4347c624936e0789228adbc51d9729fc7c
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
Simple HMAC authentication in a semi-usable state.
|
4
4
|
|
5
|
+
[](https://travis-ci.org/Oktavilla/shmac)
|
6
|
+
[](https://codeclimate.com/github/Oktavilla/shmac)
|
7
|
+
[](https://codeclimate.com/github/Oktavilla/shmac/coverage)
|
8
|
+
|
5
9
|
## Installation
|
6
10
|
|
7
11
|
Add this line to your application's Gemfile:
|
data/lib/shmac.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
require "shmac/version"
|
2
|
+
require "shmac/security"
|
2
3
|
require "shmac/authentication"
|
3
4
|
require "shmac/request_adapters"
|
4
5
|
|
5
6
|
module Shmac
|
6
|
-
def self.rails secret, request, namespace: nil
|
7
|
-
authentication(secret, request, namespace: namespace, request_adapter: RequestAdapters::Rails)
|
7
|
+
def self.rails secret, request, namespace: nil, options: {}
|
8
|
+
authentication(secret, request, namespace: namespace, request_adapter: RequestAdapters::Rails, options: options)
|
8
9
|
end
|
9
10
|
|
10
|
-
def self.net_http secret, request, namespace: nil
|
11
|
-
authentication(secret, request, namespace: namespace, request_adapter: RequestAdapters::NetHttp)
|
11
|
+
def self.net_http secret, request, namespace: nil, options: {}
|
12
|
+
authentication(secret, request, namespace: namespace, request_adapter: RequestAdapters::NetHttp, options: options)
|
12
13
|
end
|
13
14
|
|
14
|
-
def self.authentication secret, request, namespace: nil, request_adapter: nil
|
15
|
+
def self.authentication secret, request, namespace: nil, request_adapter: nil, options: {}
|
15
16
|
Authentication.new(
|
16
17
|
secret,
|
17
18
|
request,
|
18
19
|
header_namespace: namespace,
|
19
|
-
request_adapter: request_adapter
|
20
|
+
request_adapter: request_adapter,
|
21
|
+
options: options
|
20
22
|
)
|
21
23
|
end
|
22
24
|
end
|
data/lib/shmac/authentication.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require "shmac/signature_calculator"
|
2
2
|
require "shmac/authorization_header"
|
3
|
+
require "shmac/security"
|
3
4
|
|
4
5
|
module Shmac
|
5
6
|
class Authentication
|
6
7
|
include Comparable
|
7
8
|
|
8
|
-
attr_reader :secret, :header_namespace
|
9
|
+
attr_reader :secret, :header_namespace, :options
|
9
10
|
|
10
11
|
def self.generate_authorization_header request, secret:, access_key:, organization:, header_namespace: nil
|
11
12
|
AuthorizationHeader.generate(
|
@@ -19,29 +20,36 @@ module Shmac
|
|
19
20
|
new(secret, request, header_namespace: header_namespace).signature
|
20
21
|
end
|
21
22
|
|
22
|
-
def initialize secret, request, header_namespace: nil, request_adapter: nil
|
23
|
+
def initialize secret, request, header_namespace: nil, request_adapter: nil, options: {}
|
23
24
|
@secret = secret
|
24
25
|
@request = request
|
25
26
|
@request_adapter = request_adapter
|
26
27
|
@header_namespace = header_namespace
|
28
|
+
self.options = options
|
27
29
|
end
|
28
30
|
|
29
31
|
def == other
|
30
|
-
other.is_a?(self.class)
|
32
|
+
return false unless other.is_a?(self.class)
|
33
|
+
|
34
|
+
Security.secure_compare self.signature, other.signature
|
31
35
|
end
|
32
36
|
|
33
37
|
def signature
|
34
38
|
SignatureCalculator.new(
|
35
39
|
secret: self.secret,
|
36
40
|
request: self.request,
|
37
|
-
header_namespace: self.header_namespace
|
41
|
+
header_namespace: self.header_namespace,
|
42
|
+
options: { skip_path: self.options[:skip_path] }
|
38
43
|
).to_s
|
39
44
|
end
|
40
45
|
|
41
46
|
def authentic?
|
42
47
|
return false if request.authorization.to_s.strip.empty?
|
48
|
+
return false if request.tampered_body?
|
49
|
+
|
50
|
+
given_signature = AuthorizationHeader.new(request.authorization).signature
|
43
51
|
|
44
|
-
|
52
|
+
Security.secure_compare given_signature, self.signature
|
45
53
|
end
|
46
54
|
|
47
55
|
def request
|
@@ -51,5 +59,18 @@ module Shmac
|
|
51
59
|
def request_adapter
|
52
60
|
@request_adapter ||= ->(r) { r }
|
53
61
|
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def options= opts = {}
|
66
|
+
unknown_keys = opts.keys - default_options.keys
|
67
|
+
raise ArgumentError.new("Unknown options: #{unknown_keys.join(", ")}") if unknown_keys.any?
|
68
|
+
|
69
|
+
@options = default_options.merge(opts)
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_options
|
73
|
+
{ skip_path: false, validate_body_contents: true }
|
74
|
+
end
|
54
75
|
end
|
55
76
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "shmac/security"
|
2
|
+
|
1
3
|
module Shmac
|
2
4
|
class AuthorizationHeader
|
3
5
|
include Comparable
|
@@ -17,7 +19,9 @@ module Shmac
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def == other
|
20
|
-
other.is_a?(self.class)
|
22
|
+
return false unless other.is_a?(self.class)
|
23
|
+
|
24
|
+
Security.secure_compare self.to_s, other.to_s
|
21
25
|
end
|
22
26
|
|
23
27
|
def to_s
|
data/lib/shmac/request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "shmac/normalized_http_headers"
|
2
|
+
require "shmac/security"
|
2
3
|
|
3
4
|
module Shmac
|
4
5
|
class Request
|
@@ -24,6 +25,17 @@ module Shmac
|
|
24
25
|
headers.fetch("content-md5") { headers["x-content-md5"] }
|
25
26
|
end
|
26
27
|
|
28
|
+
def tampered_body?
|
29
|
+
return false unless body
|
30
|
+
return false if content_md5.to_s.strip.empty?
|
31
|
+
|
32
|
+
!content_md5_matches_body?
|
33
|
+
end
|
34
|
+
|
35
|
+
def content_md5_matches_body?
|
36
|
+
Security.secure_compare content_md5, Digest::MD5.base64digest(body)
|
37
|
+
end
|
38
|
+
|
27
39
|
def authorization
|
28
40
|
# Test for x-authorization for clients that have issues with standard headers
|
29
41
|
headers.fetch("x-authorization") { headers["authorization"] }
|
@@ -32,10 +44,5 @@ module Shmac
|
|
32
44
|
def headers= headers
|
33
45
|
@headers = NormalizedHttpHeaders.new(headers).to_h
|
34
46
|
end
|
35
|
-
|
36
|
-
def api_version
|
37
|
-
headers.fetch("x-authorization-version", 0).to_i
|
38
|
-
end
|
39
47
|
end
|
40
|
-
|
41
48
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Shmac
|
2
|
+
module Security
|
3
|
+
# Constant time comparison of strings
|
4
|
+
# Borrowed from ActiveSupport::SecurityUtils
|
5
|
+
def self.secure_compare a, b
|
6
|
+
return false if a.empty? || b.empty?
|
7
|
+
return false unless a.bytesize == b.bytesize
|
8
|
+
|
9
|
+
l = a.unpack "C#{a.bytesize}"
|
10
|
+
|
11
|
+
res = 0
|
12
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
13
|
+
res == 0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -3,12 +3,13 @@ require "base64"
|
|
3
3
|
|
4
4
|
module Shmac
|
5
5
|
class SignatureCalculator
|
6
|
-
attr_reader :secret, :request, :header_namespace
|
6
|
+
attr_reader :secret, :request, :header_namespace, :options
|
7
7
|
|
8
|
-
def initialize secret:, request:, header_namespace: nil
|
8
|
+
def initialize secret:, request:, header_namespace: nil, options: {}
|
9
9
|
@secret = secret
|
10
10
|
@request = request
|
11
11
|
@header_namespace = header_namespace.downcase if header_namespace
|
12
|
+
@options = options
|
12
13
|
end
|
13
14
|
|
14
15
|
def to_s
|
@@ -32,9 +33,8 @@ module Shmac
|
|
32
33
|
platform_headers = canonicalized_platform_headers.to_s.strip
|
33
34
|
parts << platform_headers unless platform_headers.empty?
|
34
35
|
|
35
|
-
#
|
36
|
-
|
37
|
-
parts << request.path unless request.api_version > 0
|
36
|
+
# Some clients do not know to which endpoint a message is sent
|
37
|
+
parts << request.path unless options[:skip_path]
|
38
38
|
|
39
39
|
parts.join("\n")
|
40
40
|
end
|
data/lib/shmac/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shmac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Junström
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -75,6 +75,7 @@ extra_rdoc_files: []
|
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
77
|
- ".rspec"
|
78
|
+
- ".ruby-version"
|
78
79
|
- ".travis.yml"
|
79
80
|
- CODE_OF_CONDUCT.md
|
80
81
|
- Gemfile
|
@@ -90,6 +91,7 @@ files:
|
|
90
91
|
- lib/shmac/normalized_http_headers.rb
|
91
92
|
- lib/shmac/request.rb
|
92
93
|
- lib/shmac/request_adapters.rb
|
94
|
+
- lib/shmac/security.rb
|
93
95
|
- lib/shmac/signature_calculator.rb
|
94
96
|
- lib/shmac/version.rb
|
95
97
|
- shmac.gemspec
|