wepay-signer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/signer.rb +189 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3e479de889f8eb00c3156d88c8cee78009301599
4
+ data.tar.gz: 7a3c9b4b299eda43cad03715e539024e42f0f082
5
+ SHA512:
6
+ metadata.gz: c9e4fa128e14c42f8f715946d615cf39aa31d22c4e6fac937926d6ebbc11d3835707ec5ec4ad3a580645ba7b72cffa4be9101e45208a728d7df515cbec1cf128
7
+ data.tar.gz: de5d9d3b9b51cc7a80ca1015ff45cd4d4b9a615776fdcf84fe731f89f01f5a4c59652178e642b589a8ccd27296c98f61bfa0a918120fcfb9a5b7ec75f78b24dc
data/lib/signer.rb ADDED
@@ -0,0 +1,189 @@
1
+ ##
2
+ # Copyright (c) 2015 WePay.
3
+ #
4
+ # Based on a stripped-down version of the AWS Signature v4 implementation.
5
+ #
6
+ # http://opensource.org/licenses/Apache2.0
7
+ ##
8
+
9
+ require 'openssl'
10
+
11
+ ##
12
+ # The root WePay namespace.
13
+ ##
14
+ module WePay
15
+
16
+ ##
17
+ # The Signer class is designed for those who are signing data on behalf of a public-private keypair.
18
+ #
19
+ # In principle, the "client party" has public key (i.e., `client_id`) has a matching private key
20
+ # (i.e., `client_secret`) that can be verified by both the signer, as well as the client, but
21
+ # by nobody else as we don't want to make forgeries possible.
22
+ #
23
+ # The "signing party" has a simple an identifier which acts as an additional piece of entropy in the
24
+ # algorithm, and can help differentiate between multiple signing parties if the client party does
25
+ # something like try to use the same public-private keypair independently of a signing party
26
+ # (as is common with GPG signing).
27
+ #
28
+ # For example, in the original AWS implementation, the "self key" for AWS was "AWS4".
29
+ ##
30
+ class Signer
31
+
32
+ ##
33
+ # Constructs a new instance of this class.
34
+ #
35
+ # @param client_id [String] A string which is the public portion of the keypair identifying the client party. The
36
+ # pairing of the public and private portions of the keypair should only be known to the client party and the
37
+ # signing party.
38
+ # @param client_secret [String] A string which is the private portion of the keypair identifying the client party.
39
+ # The pairing of the public and private portions of the keypair should only be known to the client party and
40
+ # the signing party.
41
+ # @option options [String] self_key (WePay) A string which identifies the signing party and adds additional entropy.
42
+ # @option options [String] hash_algo (sha512) The hash algorithm to use for signing.
43
+ ##
44
+ def initialize(client_id, client_secret, options = {})
45
+ @client_id = client_id
46
+ @client_secret = client_secret
47
+
48
+ options = {
49
+ :self_key => 'WePay',
50
+ :hash_algo => 'sha512',
51
+ }.merge(options);
52
+
53
+ @self_key = options[:self_key]
54
+ @hash_algo = options[:hash_algo]
55
+ end
56
+
57
+ ##
58
+ # Gets the self key that was set in the constructor.
59
+ #
60
+ # @return [String] The self key.
61
+ ##
62
+ def get_self_key
63
+ @self_key
64
+ end
65
+
66
+ ##
67
+ # Gets the client key that was set in the constructor.
68
+ #
69
+ # @return [String] The client key.
70
+ ##
71
+ def get_client_id
72
+ @client_id
73
+ end
74
+
75
+ ##
76
+ # Gets the client secret that was set in the constructor.
77
+ #
78
+ # @return [String] The client secret.
79
+ ##
80
+ def get_client_secret
81
+ @client_secret
82
+ end
83
+
84
+ ##
85
+ # Sign the payload to produce a signature for its contents.
86
+ #
87
+ # @param payload [Hash] The data to generate a signature for.
88
+ # @option payload [required, String] token The one-time-use token.
89
+ # @option payload [required, String] page The WePay URL to access.
90
+ # @option payload [required, String] redirect_uri The partner URL to return to once the action is completed.
91
+ # @return [String] The signature for the payload contents.
92
+ ##
93
+ def sign(payload)
94
+ payload = payload.merge({
95
+ :client_id => @client_id,
96
+ :client_secret => @client_secret,
97
+ })
98
+
99
+ scope = create_scope
100
+ context = create_context(payload)
101
+ s2s = create_string_to_sign(scope, context)
102
+ signing_key = get_signing_salt
103
+ signature = OpenSSL::HMAC.hexdigest(@hash_algo, signing_key, s2s)
104
+
105
+ return signature
106
+ end
107
+
108
+ ##
109
+ # Signs and generates the query string URL parameters to use when making a request.
110
+ #
111
+ # @param payload [Hash] The data to generate a signature for.
112
+ # @option payload [required, String] token The one-time-use token.
113
+ # @option payload [required, String] page The WePay URL to access.
114
+ # @option payload [required, String] redirect_uri The partner URL to return to once the action is completed.
115
+ # @return [String] The query string parameters to append to the end of a URL.
116
+ ##
117
+ def generate_query_string_params(payload)
118
+ signed_token = sign(payload)
119
+ payload[:client_id] = @client_id
120
+ payload[:stoken] = signed_token
121
+ qsa = Array.new
122
+
123
+ payload.keys.sort.each do | key |
124
+ qsa.push sprintf("%s=%s", key, payload[key])
125
+ end
126
+
127
+ return qsa.join("&")
128
+ end
129
+
130
+ private
131
+
132
+ ##
133
+ # Creates the string-to-sign based on a variety of factors.
134
+ #
135
+ # @param scope [String] The results of a call to the `create_scope()` method.
136
+ # @param context [String] The results of a call to the `create_context()` method.
137
+ # @return [String] The final string to be signed.
138
+ ##
139
+ def create_string_to_sign(scope, context)
140
+ scope_hash = OpenSSL::Digest.new(@hash_algo, scope)
141
+ context_hash = OpenSSL::Digest.new(@hash_algo, context)
142
+ s2s = sprintf "SIGNER-HMAC-%s\n%s\n%s\n%s\n%s", @hash_algo.upcase, @self_key, @client_id, scope_hash, context_hash
143
+ end
144
+
145
+ ##
146
+ # An array of key-value pairs representing the data that you want to sign.
147
+ # All values must be `scalar`.
148
+ #
149
+ # @param payload [Hash] The data that you want to sign.
150
+ # @option payload [String] self_key (WePay) A string which identifies the signing party and adds additional entropy.
151
+ # @return [String] A canonical string representation of the data to sign.
152
+ ##
153
+ def create_context(payload)
154
+ canonical_payload = []
155
+
156
+ payload.keys.each do | key |
157
+ val = payload[key].downcase
158
+ key = key.downcase
159
+ canonical_payload.push(sprintf "%s=%s\n", key, val)
160
+ end
161
+
162
+ canonical_payload.sort!
163
+
164
+ signed_headers_string = payload.keys.sort_by {|s| s.to_s}.join(";")
165
+ canonical_context = canonical_payload.join("") + "\n" + signed_headers_string
166
+ end
167
+
168
+ ##
169
+ # Gets the salt value that should be used for signing.
170
+ #
171
+ # @return [String] The signing salt.
172
+ ##
173
+ def get_signing_salt
174
+ self_key_sign = OpenSSL::HMAC.digest(@hash_algo, @client_secret, @self_key)
175
+ client_id_sign = OpenSSL::HMAC.digest(@hash_algo, self_key_sign, @client_id)
176
+ salt = OpenSSL::HMAC.digest(@hash_algo, client_id_sign, 'signer')
177
+ end
178
+
179
+ ##
180
+ # Creates the "scope" in which the signature is valid.
181
+ #
182
+ # @return [String] The string which represents the scope in which the signature is valid.
183
+ ##
184
+ def create_scope
185
+ scope = sprintf "%s/%s/signer", @self_key, @client_id
186
+ end
187
+
188
+ end
189
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wepay-signer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - WePay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A Modern Ruby SDK for signing WePay requests.
14
+ email: api@wepay.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/signer.rb
20
+ homepage: https://github.com/wepay/signer-ruby
21
+ licenses:
22
+ - Apache-2.0
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.4.5
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: WePay Signer for Ruby
44
+ test_files: []