wepay-signer 1.0.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.
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: []