credify 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/README.md +23 -0
- data/lib/credify.rb +31 -1
- data/lib/credify/encryption.rb +10 -23
- data/lib/credify/signing.rb +127 -12
- data/lib/credify/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22b70b4ef4e0b030632a418609e96ff5f84002c57c2ab03f0b8dfa96c2269079
|
4
|
+
data.tar.gz: 27e4c5183a0ddd41131a00ba8ef76ceab4a335c13047a8285ad4c11f3855cd82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd20625b4eaecd91a65127d140d5fb746076ae15b064dac3a27d88f5802049f14129543ef810a166838ba71352ade6cb58b051abb4a6e6b17565d8cc0cd03d60
|
7
|
+
data.tar.gz: 3bfc34738682cab00664f8264e0a9f19118964b7ce4b218e47c14a507f4152711178425caf7235a9a5d869bda59badfd2fbb4f2b6ff6c5cf17795fb095fa79f3
|
data/README.md
CHANGED
@@ -40,6 +40,27 @@ def existing_key_is_used
|
|
40
40
|
valid = signing.verify "message", signature
|
41
41
|
puts valid
|
42
42
|
end
|
43
|
+
|
44
|
+
def generate_approval_token
|
45
|
+
signing = Signing.new
|
46
|
+
signing.generate_key_pair
|
47
|
+
token = signing.generate_approval_token 'client_id', 'entity_id', ['openid', 'email', 'phone'], 'offer-code'
|
48
|
+
puts token
|
49
|
+
end
|
50
|
+
|
51
|
+
def generate_request_token
|
52
|
+
signing = Signing.new
|
53
|
+
signing.generate_key_pair
|
54
|
+
token = signing.generate_request_token 'client_id', 'encryption_public_key', ['openid', 'email', 'phone'], 'offer-code'
|
55
|
+
puts token
|
56
|
+
end
|
57
|
+
|
58
|
+
def generate_claim_token
|
59
|
+
signing = Signing.new
|
60
|
+
signing.generate_key_pair
|
61
|
+
result = signing.generate_claim_token 'provider_id', 'entity_id', 'credify-score', { score: 100 }
|
62
|
+
puts result
|
63
|
+
end
|
43
64
|
```
|
44
65
|
|
45
66
|
### Encryption
|
@@ -54,6 +75,8 @@ def new_key_is_generated
|
|
54
75
|
encryption.generate_key_pair
|
55
76
|
cipher_text = encryption.encrypt "secret message"
|
56
77
|
plain_text = encryption.decrypt cipher_text
|
78
|
+
pem = encryption.export_private_key
|
79
|
+
puts pem
|
57
80
|
end
|
58
81
|
|
59
82
|
def existing_key_is_used
|
data/lib/credify.rb
CHANGED
@@ -1,6 +1,36 @@
|
|
1
1
|
require "credify/version"
|
2
|
+
require 'base64'
|
3
|
+
require 'securerandom'
|
2
4
|
|
3
5
|
module Credify
|
4
6
|
class Error < StandardError; end
|
5
|
-
|
7
|
+
|
8
|
+
class Helpers
|
9
|
+
def self.sha256(message)
|
10
|
+
base64 = Digest::SHA256.base64digest(message)
|
11
|
+
Helpers.short_urlsafe_encode64(Base64.decode64(base64))
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# short_urlsafe_encode64
|
16
|
+
# @param [Binary] - str
|
17
|
+
# @return [String] - Base64 URL encoded string without padding
|
18
|
+
def self.short_urlsafe_encode64(bytes)
|
19
|
+
Base64.urlsafe_encode64(bytes).delete('=')
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# short_urlsafe_decode64
|
24
|
+
# @return [Binary]
|
25
|
+
def self.short_urlsafe_decode64(str)
|
26
|
+
Base64.urlsafe_decode64(str + '=' * (-1 * str.size & 3))
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.generate_commitment(bytes = 32)
|
30
|
+
random_bytes = SecureRandom.random_bytes(bytes)
|
31
|
+
short_urlsafe_encode64(random_bytes)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
6
36
|
end
|
data/lib/credify/encryption.rb
CHANGED
@@ -2,6 +2,7 @@ require 'openssl'
|
|
2
2
|
require 'openssl/oaep'
|
3
3
|
require "base64"
|
4
4
|
require 'openssl_pkcs8_pure'
|
5
|
+
require 'credify'
|
5
6
|
|
6
7
|
class Encryption
|
7
8
|
attr_reader :private_key, :public_key
|
@@ -43,7 +44,7 @@ class Encryption
|
|
43
44
|
# @param [String] payload - Base64 URL encoded string
|
44
45
|
# @return [Boolean]
|
45
46
|
def import_private_key_base64_url(payload)
|
46
|
-
bytes = short_urlsafe_decode64(payload)
|
47
|
+
bytes = Credify::Helpers.short_urlsafe_decode64(payload)
|
47
48
|
base64 = Base64.encode64(bytes)
|
48
49
|
formatted = base64.scan(/.{1,64}/).join("\n")
|
49
50
|
pem = add_box('PRIVATE KEY', formatted)
|
@@ -55,7 +56,7 @@ class Encryption
|
|
55
56
|
# @param [String] payload - Base64 URL encoded string
|
56
57
|
# @return [Boolean]
|
57
58
|
def import_public_key_base64_url(payload)
|
58
|
-
bytes = short_urlsafe_decode64(payload)
|
59
|
+
bytes = Credify::Helpers.short_urlsafe_decode64(payload)
|
59
60
|
base64 = Base64.encode64(bytes)
|
60
61
|
formatted = base64.scan(/.{1,64}/).join("\n")
|
61
62
|
pem = add_box('PUBLIC KEY', formatted)
|
@@ -73,7 +74,7 @@ class Encryption
|
|
73
74
|
label = ''
|
74
75
|
md = OpenSSL::Digest::SHA256
|
75
76
|
cipher_text = @public_key.public_encrypt_oaep(message, label, md)
|
76
|
-
short_urlsafe_encode64(cipher_text)
|
77
|
+
Credify::Helpers.short_urlsafe_encode64(cipher_text)
|
77
78
|
end
|
78
79
|
|
79
80
|
#
|
@@ -86,7 +87,7 @@ class Encryption
|
|
86
87
|
end
|
87
88
|
label = ''
|
88
89
|
md = OpenSSL::Digest::SHA256
|
89
|
-
raw_cipher = short_urlsafe_decode64(cipher)
|
90
|
+
raw_cipher = Credify::Helpers.short_urlsafe_decode64(cipher)
|
90
91
|
raw_text = @private_key.private_decrypt_oaep(raw_cipher, label, md)
|
91
92
|
raw_text
|
92
93
|
end
|
@@ -94,7 +95,7 @@ class Encryption
|
|
94
95
|
#
|
95
96
|
# export_private_key
|
96
97
|
# @param [Boolean] in_base64_url
|
97
|
-
# @return [Signing] - PCKS8 PEM or Base64 URL encoded string
|
98
|
+
# @return [Signing | String] - PCKS8 PEM or Base64 URL encoded string
|
98
99
|
def export_private_key(in_base64_url = false)
|
99
100
|
if @private_key.nil?
|
100
101
|
raise Exception.new 'Please pass private key'
|
@@ -103,7 +104,7 @@ class Encryption
|
|
103
104
|
|
104
105
|
if in_base64_url
|
105
106
|
formatted = remove_box('PRIVATE KEY', pem)
|
106
|
-
short_urlsafe_encode64(Base64.decode64(formatted))
|
107
|
+
Credify::Helpers.short_urlsafe_encode64(Base64.decode64(formatted))
|
107
108
|
else
|
108
109
|
pem
|
109
110
|
end
|
@@ -112,7 +113,7 @@ class Encryption
|
|
112
113
|
#
|
113
114
|
# export_public_key
|
114
115
|
# @param [Boolean] in_base64_url
|
115
|
-
# @return [Signing] - PCKS8 PEM or Base64 URL encoded string
|
116
|
+
# @return [Signing | String] - PCKS8 PEM or Base64 URL encoded string
|
116
117
|
def export_public_key(in_base64_url = false)
|
117
118
|
if @public_key.nil?
|
118
119
|
raise Exception.new 'Please pass public key'
|
@@ -122,7 +123,7 @@ class Encryption
|
|
122
123
|
|
123
124
|
if in_base64_url
|
124
125
|
formatted = remove_box('PUBLIC KEY', pem)
|
125
|
-
short_urlsafe_encode64(Base64.decode64(formatted))
|
126
|
+
Credify::Helpers.short_urlsafe_encode64(Base64.decode64(formatted))
|
126
127
|
else
|
127
128
|
pem
|
128
129
|
end
|
@@ -131,7 +132,7 @@ class Encryption
|
|
131
132
|
|
132
133
|
protected
|
133
134
|
|
134
|
-
|
135
|
+
#
|
135
136
|
# remove_box
|
136
137
|
# @param [String] tag - Either 'PUBLIC KEY' or 'PRIVATE KEY'
|
137
138
|
# @param [String] pem - String value loaded from a PEM file
|
@@ -152,18 +153,4 @@ class Encryption
|
|
152
153
|
"-----BEGIN #{tag}-----\n" << payload << "\n-----END #{tag}-----"
|
153
154
|
end
|
154
155
|
|
155
|
-
#
|
156
|
-
# short_urlsafe_encode64
|
157
|
-
# @param [Binary] - str
|
158
|
-
# @return [String] - Base64 URL encoded string without padding
|
159
|
-
def short_urlsafe_encode64(bytes)
|
160
|
-
Base64.urlsafe_encode64(bytes).delete('=')
|
161
|
-
end
|
162
|
-
|
163
|
-
#
|
164
|
-
# short_urlsafe_decode64
|
165
|
-
# @return [Binary]
|
166
|
-
def short_urlsafe_decode64(str)
|
167
|
-
Base64.urlsafe_decode64(str + '=' * (-1 * str.size & 3))
|
168
|
-
end
|
169
156
|
end
|
data/lib/credify/signing.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "ed25519"
|
2
2
|
require "base64"
|
3
|
+
require 'json'
|
3
4
|
require 'openssl_pkcs8_pure'
|
5
|
+
require 'credify'
|
4
6
|
|
5
7
|
class Signing
|
6
8
|
attr_reader :signing_key
|
@@ -31,7 +33,7 @@ class Signing
|
|
31
33
|
raise Exception.new 'Please pass signing key'
|
32
34
|
end
|
33
35
|
signature = @signing_key.sign(message)
|
34
|
-
short_urlsafe_encode64(signature)
|
36
|
+
Credify::Helpers.short_urlsafe_encode64(signature)
|
35
37
|
end
|
36
38
|
|
37
39
|
#
|
@@ -43,7 +45,7 @@ class Signing
|
|
43
45
|
if @signing_key.nil?
|
44
46
|
raise Exception.new 'Please pass signing key'
|
45
47
|
end
|
46
|
-
raw_sign = short_urlsafe_decode64(signature)
|
48
|
+
raw_sign = Credify::Helpers.short_urlsafe_decode64(signature)
|
47
49
|
@signing_key.verify_key.verify raw_sign, message
|
48
50
|
end
|
49
51
|
|
@@ -57,21 +59,134 @@ class Signing
|
|
57
59
|
Base64.encode64(@signing_key.seed)
|
58
60
|
end
|
59
61
|
|
60
|
-
|
62
|
+
#
|
63
|
+
# generate_jwt
|
64
|
+
# @param [Hash] payload
|
65
|
+
# @return [String]
|
66
|
+
def generate_jwt(payload)
|
67
|
+
if payload.empty?
|
68
|
+
raise Exception.new 'Invalid payload'
|
69
|
+
end
|
70
|
+
header = {
|
71
|
+
alg: 'EdDSA',
|
72
|
+
typ: 'JWT'
|
73
|
+
}
|
74
|
+
message = compose_message(header, payload)
|
75
|
+
signature = sign(message)
|
76
|
+
message << '.' << signature
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# parse_jwt
|
81
|
+
# @param [String] jwt
|
82
|
+
# @return [Hash] - { header: '', payload: {}, signature: '' }
|
83
|
+
def parse_jwt(jwt)
|
84
|
+
components = jwt.split('.')
|
85
|
+
unless components.length == 3
|
86
|
+
raise Exception 'JST is invalid'
|
87
|
+
end
|
88
|
+
|
89
|
+
header = JSON.parse(Credify::Helpers.short_urlsafe_decode64(components[0]))
|
90
|
+
payload = JSON.parse(Credify::Helpers.short_urlsafe_decode64(components[1]))
|
91
|
+
{ header: header, payload: payload, signature: components[2] }
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# verify_jwt
|
96
|
+
# @param [Hash] jwt - { header: '', payload: {}, signature: '' }
|
97
|
+
# @return [Boolean]
|
98
|
+
def verify_jwt(jwt)
|
99
|
+
message = compose_message(jwt[:header], jwt[:payload])
|
100
|
+
verify(jwt[:signature], message)
|
101
|
+
end
|
61
102
|
|
62
103
|
#
|
63
|
-
#
|
64
|
-
# @param [
|
65
|
-
# @
|
66
|
-
|
67
|
-
|
104
|
+
# generate_approval_token
|
105
|
+
# @param [String] client_id
|
106
|
+
# @param [String] entity_id
|
107
|
+
# @param [String[]] approved_scopes
|
108
|
+
# @param [String | nil] offer_code
|
109
|
+
# @return [String]
|
110
|
+
def generate_approval_token(client_id, entity_id, approved_scopes, offer_code = nil)
|
111
|
+
# minus 60 just in case this timestamp could collide one in the server side.
|
112
|
+
now = Time.now.to_i - 60
|
113
|
+
payload = {
|
114
|
+
client_id: client_id,
|
115
|
+
iat: now,
|
116
|
+
iss: entity_id,
|
117
|
+
scopes: approved_scopes.join(' ')
|
118
|
+
}
|
119
|
+
unless offer_code.nil?
|
120
|
+
payload[:offer_code] = offer_code
|
121
|
+
end
|
122
|
+
generate_jwt(payload)
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# generate_claim_token
|
127
|
+
# @param [String] provider_id
|
128
|
+
# @param [String] entity_id
|
129
|
+
# @param [String] scope_name
|
130
|
+
# @param [Hash] claim
|
131
|
+
# @return [Hash]
|
132
|
+
def generate_claim_token(provider_id, entity_id, scope_name, claim)
|
133
|
+
# minus 60 just in case this timestamp could collide one in the server side.
|
134
|
+
now = Time.now.to_i - 60
|
135
|
+
commitment = Credify::Helpers.generate_commitment
|
136
|
+
data = claim[:"#{scope_name}:commitment"] = commitment
|
137
|
+
scope_hash = Credify::Helpers.sha256(data)
|
138
|
+
puts scope_hash
|
139
|
+
payload = {
|
140
|
+
iat: now,
|
141
|
+
iss: provider_id,
|
142
|
+
user_id: entity_id,
|
143
|
+
scope_name: scope_name,
|
144
|
+
scope_hash: scope_hash
|
145
|
+
}
|
146
|
+
token = generate_jwt(payload)
|
147
|
+
{ token: token, commitment: commitment }
|
68
148
|
end
|
69
149
|
|
70
150
|
#
|
71
|
-
#
|
72
|
-
# @
|
73
|
-
|
74
|
-
|
151
|
+
# generate_request_token
|
152
|
+
# @param [String] client_id
|
153
|
+
# @param [String] encryption_public_key - Encryption public key in Base64 URL
|
154
|
+
# @param [String[]] scopes
|
155
|
+
# @param [String | nil] offer_code
|
156
|
+
# @return [String]
|
157
|
+
def generate_request_token(client_id, encryption_public_key, scopes, offer_code = nil)
|
158
|
+
unless scopes.include?('openid')
|
159
|
+
raise Exception 'scopes need to contain openid'
|
160
|
+
end
|
161
|
+
# minus 60 just in case this timestamp could collide one in the server side.
|
162
|
+
now = Time.now.to_i - 60
|
163
|
+
payload = {
|
164
|
+
iat: now,
|
165
|
+
iss: client_id,
|
166
|
+
encryption_public_key: encryption_public_key,
|
167
|
+
scopes: scopes.join(' ')
|
168
|
+
}
|
169
|
+
unless offer_code.nil?
|
170
|
+
payload[:offer_code] = offer_code
|
171
|
+
end
|
172
|
+
generate_jwt(payload)
|
173
|
+
end
|
174
|
+
|
175
|
+
protected
|
176
|
+
|
177
|
+
|
178
|
+
# compose_message
|
179
|
+
# @param [Hash] header
|
180
|
+
# @param [Hash] payload
|
181
|
+
# @return [String]
|
182
|
+
def compose_message(header, payload)
|
183
|
+
encoded_header = header.to_json
|
184
|
+
h = Credify::Helpers.short_urlsafe_encode64(encoded_header)
|
185
|
+
|
186
|
+
encoded_payload = payload.to_json
|
187
|
+
p = Credify::Helpers.short_urlsafe_encode64(encoded_payload)
|
188
|
+
|
189
|
+
h << '.' << p
|
75
190
|
end
|
76
191
|
|
77
192
|
end
|
data/lib/credify/version.rb
CHANGED