wepay-signer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/signer.rb +189 -0
- 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: []
|