pedicel 0.1.0 → 1.0.1
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/lib/pedicel/ec.rb +2 -0
- data/lib/pedicel/validator.rb +225 -88
- data/lib/pedicel/version.rb +1 -1
- metadata +48 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8effeb35a7193cf9470319b3666b17a7105e3abb94d404882ce5ca705feca4b3
|
4
|
+
data.tar.gz: 9586a185c9dd0c0ea2faeea433f938507391cc91c20e7000c1b1a60f47b70e52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3ae4d8a50ec94b2f217a19c63aeeeeab150296d85b7716fe3b2ef2721839059eeb14c69e42c5f503f84f4662dec72e7b7e19292c40ccc2d7ef48eccf84a64fb
|
7
|
+
data.tar.gz: '091465c0bcdba0987a5cae1df961ae68f0c0f406278187072a9d7d980c2e20e51b419cd501924d96adfb4448917deaa5343465d69168d8fa0d80330ba1e23442'
|
data/lib/pedicel/ec.rb
CHANGED
data/lib/pedicel/validator.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
require 'dry
|
1
|
+
require 'dry/validation'
|
2
|
+
require 'dry/schema'
|
3
|
+
require 'dry/logic'
|
2
4
|
require 'base64'
|
3
5
|
require 'openssl'
|
4
6
|
|
@@ -8,140 +10,275 @@ module Pedicel
|
|
8
10
|
# https://developer.apple.com/library/content/documentation/PassKit/Reference/PaymentTokenJSON/PaymentTokenJSON.html
|
9
11
|
# This purposefully only does syntactic validation (as opposed to semantic).
|
10
12
|
module Validator
|
13
|
+
CUSTOM_ERRORS = {
|
14
|
+
is_base64: 'must be Base64',
|
15
|
+
is_hex: 'must be hex',
|
16
|
+
is_pan: 'must be a pan',
|
17
|
+
is_yymmdd: 'must be formatted YYMMDD',
|
18
|
+
is_ec_public_key: 'must be an EC public key',
|
19
|
+
is_pkcs7_signature: 'must be a PKCS7 Signature',
|
20
|
+
is_eci: 'must be an ECI',
|
21
|
+
is_hex_sha256: 'must be a hex-encoded SHA-256',
|
22
|
+
is_base64_sha256: 'must be a Base64-encoded SHA-256',
|
23
|
+
is_iso4217_numeric: 'must be an ISO 4217 numeric code',
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
Dry::Validation.register_macro(:is_hex) do
|
27
|
+
if key?
|
28
|
+
unless /\A[a-f0-9]*\z/i.match?(value)
|
29
|
+
key.failure(CUSTOM_ERRORS[:is_hex])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
11
33
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
yymmdd?: 'must be formatted YYMMDD',
|
20
|
-
ec_public_key?: 'must be an EC public key',
|
21
|
-
pkcs7_signature?: 'must be a PKCS7 Signature',
|
22
|
-
eci?: 'must be an ECI',
|
23
|
-
hex_sha256?: 'must be a hex-encoded SHA-256',
|
24
|
-
base64_sha256?: 'must be a Base64-encoded SHA-256',
|
25
|
-
iso4217_numeric?: 'must be an ISO 4217 numeric code',
|
26
|
-
}.freeze
|
34
|
+
Dry::Validation.register_macro(:is_hex_sha256) do
|
35
|
+
if key?
|
36
|
+
unless :is_hex && value.length == 64
|
37
|
+
key.failure(CUSTOM_ERRORS[:is_hex_sha256])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
27
41
|
|
28
|
-
|
29
|
-
|
42
|
+
Dry::Validation.register_macro(:is_base64) do
|
43
|
+
if key?
|
44
|
+
unless /\A[=A-Za-z0-9+\/]*\z/.match?(value) &&
|
45
|
+
value.length.remainder(4).zero? &&
|
46
|
+
!/=[^$=]/.match?(value) &&
|
47
|
+
!/===/.match?(value)
|
48
|
+
key.failure(CUSTOM_ERRORS[:is_base64])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
30
52
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
!match_b.(x, /===/) # at most 2 ='s
|
53
|
+
Dry::Validation.register_macro(:is_base64_sha256) do
|
54
|
+
if key?
|
55
|
+
unless :is_base64 && Base64.decode64(value).length == 32
|
56
|
+
key.failure(CUSTOM_ERRORS[:is_base64_sha256])
|
57
|
+
end
|
37
58
|
end
|
59
|
+
end
|
38
60
|
|
39
|
-
|
40
|
-
|
41
|
-
|
61
|
+
Dry::Validation.register_macro(:is_ec_public_key) do
|
62
|
+
if key?
|
63
|
+
ec = lambda {OpenSSL::PKey::EC.new(Base64.decode64(value)).check_key rescue false}.()
|
64
|
+
unless :is_base64 && ec
|
65
|
+
key.failure(CUSTOM_ERRORS[:is_ec_public_key])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
42
69
|
|
43
|
-
|
70
|
+
Dry::Validation.register_macro(:is_pkcs7_signature) do
|
71
|
+
if key?
|
72
|
+
pkcs7 = lambda {!!OpenSSL::PKCS7.new(Base64.decode64(value)) rescue false}.()
|
73
|
+
unless :is_base64 && pkcs7
|
74
|
+
key.failure(CUSTOM_ERRORS[:is_pkcs7_signature])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
44
78
|
|
45
|
-
|
79
|
+
Dry::Validation.register_macro(:is_eci) do
|
80
|
+
if key?
|
81
|
+
unless /\A\d{1,2}\z/.match?(value)
|
82
|
+
key.failure(CUSTOM_ERRORS[:is_eci])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
46
86
|
|
47
|
-
|
87
|
+
Dry::Validation.register_macro(:is_pan) do
|
88
|
+
if key?
|
89
|
+
unless /\A[1-9][0-9]{11,18}\z/.match?(value)
|
90
|
+
key.failure(CUSTOM_ERRORS[:is_pan])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
48
94
|
|
49
|
-
|
95
|
+
Dry::Validation.register_macro(:is_yymmdd) do
|
96
|
+
if key?
|
97
|
+
unless /\A\d{6}\z/.match?(value)
|
98
|
+
key.failure(CUSTOM_ERRORS[:is_yymmdd])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
50
102
|
|
51
|
-
|
103
|
+
Dry::Validation.register_macro(:is_iso4217_numeric) do
|
104
|
+
if key?
|
105
|
+
unless /\A[0-9]{3}\z/.match?(value.rjust(3, "0"))
|
106
|
+
key.failure(CUSTOM_ERRORS[:is_iso4217_numeric])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
52
110
|
|
53
|
-
|
111
|
+
class TokenHeaderContract < Dry::Validation::Contract
|
112
|
+
json do
|
113
|
+
optional(:applicationData).filled(:str?)
|
54
114
|
|
55
|
-
|
115
|
+
optional(:ephemeralPublicKey).filled(:str?)
|
56
116
|
|
57
|
-
|
117
|
+
optional(:wrappedKey).filled(:str?)
|
58
118
|
|
59
|
-
predicate(:iso4217_numeric?) { |x| match_b.(x, /\A[0-9]{3}\z/) }
|
60
|
-
end
|
61
119
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
120
|
+
required(:publicKeyHash).filled(:str?)
|
121
|
+
required(:transactionId).filled(:str?)
|
122
|
+
end
|
123
|
+
rule(:applicationData).validate(:is_hex, :is_hex_sha256)
|
124
|
+
|
125
|
+
rule(:ephemeralPublicKey).validate(:is_base64, :is_ec_public_key)
|
126
|
+
rule(:publicKeyHash).validate(:is_base64, :is_base64_sha256)
|
127
|
+
rule(:wrappedKey).validate(:is_base64)
|
128
|
+
rule(:transactionId).validate(:is_hex)
|
129
|
+
rule(:ephemeralPublicKey, :wrappedKey) do
|
130
|
+
key.failure('ephemeralPublicKey xor wrappedKey') unless values[:ephemeralPublicKey].nil? ^ values[:wrappedKey].nil?
|
66
131
|
end
|
67
132
|
end
|
68
133
|
|
69
|
-
TokenHeaderSchema =
|
70
|
-
optional(:applicationData).filled(:str?, :hex?, :hex_sha256?)
|
134
|
+
TokenHeaderSchema = TokenHeaderContract.new
|
71
135
|
|
72
|
-
|
136
|
+
class TokenContract < Dry::Validation::Contract
|
137
|
+
json do
|
138
|
+
required(:data).filled(:str?)
|
73
139
|
|
74
|
-
|
140
|
+
required(:header).hash(TokenHeaderContract.schema)
|
75
141
|
|
76
|
-
|
77
|
-
e.filled? ^ w.filled?
|
78
|
-
end
|
142
|
+
required(:signature).filled(:str?)
|
79
143
|
|
80
|
-
|
144
|
+
required(:version).filled(:str?, included_in?: %w[EC_v1 RSA_v1])
|
81
145
|
|
82
|
-
|
146
|
+
end
|
147
|
+
rule(:data).validate(:is_base64)
|
148
|
+
rule(:signature).validate(:is_base64, :is_pkcs7_signature)
|
83
149
|
end
|
84
150
|
|
85
|
-
TokenSchema =
|
86
|
-
required(:data).filled(:str?, :base64?)
|
151
|
+
TokenSchema = TokenContract.new
|
87
152
|
|
88
|
-
|
153
|
+
class TokenDataPaymentDataContract < Dry::Validation::Contract
|
154
|
+
json do
|
155
|
+
optional(:onlinePaymentCryptogram).filled(:str?)
|
156
|
+
optional(:eciIndicator).filled(:str?)
|
89
157
|
|
90
|
-
|
158
|
+
optional(:emvData).filled(:str?)
|
159
|
+
optional(:encryptedPINData).filled(:str?)
|
160
|
+
end
|
161
|
+
rule(:onlinePaymentCryptogram).validate(:is_base64)
|
162
|
+
rule(:eciIndicator).validate(:is_eci)
|
91
163
|
|
92
|
-
|
164
|
+
rule(:emvData).validate(:is_base64)
|
165
|
+
rule(:encryptedPINData).validate(:is_hex)
|
93
166
|
end
|
94
167
|
|
95
|
-
TokenDataPaymentDataSchema =
|
96
|
-
optional(:onlinePaymentCryptogram).filled(:str?, :base64?)
|
97
|
-
optional(:eciIndicator).filled(:str?, :eci?)
|
168
|
+
TokenDataPaymentDataSchema = TokenDataPaymentDataContract.new
|
98
169
|
|
99
|
-
|
100
|
-
|
101
|
-
|
170
|
+
class TokenDataContract < Dry::Validation::Contract
|
171
|
+
json do
|
172
|
+
required(:applicationPrimaryAccountNumber).filled(:str?)
|
173
|
+
|
174
|
+
required(:applicationExpirationDate).filled(:str?)
|
102
175
|
|
103
|
-
|
104
|
-
required(:applicationPrimaryAccountNumber).filled(:str?, :pan?)
|
176
|
+
required(:currencyCode).filled(:str?)
|
105
177
|
|
106
|
-
|
178
|
+
required(:transactionAmount).filled(:int?)
|
107
179
|
|
108
|
-
|
180
|
+
optional(:cardholderName).filled(:str?)
|
109
181
|
|
110
|
-
|
182
|
+
required(:deviceManufacturerIdentifier).filled(:str?)
|
111
183
|
|
112
|
-
|
184
|
+
required(:paymentDataType).filled(:str?, included_in?: %w[3DSecure EMV])
|
113
185
|
|
114
|
-
|
186
|
+
required(:paymentData).hash(TokenDataPaymentDataContract.schema)
|
187
|
+
end
|
188
|
+
rule(:applicationPrimaryAccountNumber).validate(:is_pan)
|
115
189
|
|
116
|
-
|
190
|
+
rule(:applicationExpirationDate).validate(:is_yymmdd)
|
117
191
|
|
118
|
-
|
192
|
+
rule(:currencyCode).validate(:is_iso4217_numeric)
|
119
193
|
|
120
|
-
rule(
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
type
|
194
|
+
rule(:deviceManufacturerIdentifier).validate(:is_hex)
|
195
|
+
|
196
|
+
rule(:paymentDataType, paymentData: :onlinePaymentCryptogram) do
|
197
|
+
# rule('when paymentDataType is 3DSecure, onlinePaymentCryptogram': [:paymentDataType, [:paymentData, :onlinePaymentCryptogram]]) do |type, cryptogram|
|
198
|
+
# type can only be 3DSecure if cryptogram is filled
|
199
|
+
# type.eql?('3DSecure') > cryptogram.filled?
|
200
|
+
# end
|
201
|
+
if values[:paymentDataType].eql?('3DSecure')
|
202
|
+
key.failure('when paymentDataType is 3DSecure, onlinePaymentCryptogram must be filled') unless
|
203
|
+
values[:paymentData] && values[:paymentData][:onlinePaymentCryptogram]
|
204
|
+
end
|
125
205
|
end
|
126
|
-
|
127
|
-
|
206
|
+
|
207
|
+
rule(:paymentDataType, paymentData: :emvData) do
|
208
|
+
# type can only be 3DSecure if emvData is empty
|
209
|
+
# old rule:
|
210
|
+
# rule('when paymentDataType is 3DSecure, emvData': [:paymentDataType, [:paymentData, :emvData]]) do |type, emv|
|
211
|
+
# type.eql?('3DSecure') > emv.none?
|
212
|
+
# end
|
213
|
+
if values[:paymentDataType].eql?('3DSecure')
|
214
|
+
key.failure('when paymentDataType is 3DSecure, emvData cannot be defined') unless
|
215
|
+
values[:paymentData] && values[:paymentData][:emvData].nil?
|
216
|
+
end
|
128
217
|
end
|
129
218
|
|
130
|
-
rule(
|
131
|
-
type
|
219
|
+
rule(:paymentDataType, paymentData: :encryptedPINData) do
|
220
|
+
# type can only be 3DSecure if emvData is empty
|
221
|
+
# old rule:
|
222
|
+
# rule('when paymentDataType is 3DSecure, encryptedPINData': [:paymentDataType, [:paymentData, :encryptedPINData]]) do |type, pin|
|
223
|
+
# type.eql?('3DSecure') > pin.none?
|
224
|
+
# end
|
225
|
+
if values[:paymentDataType].eql?('3DSecure')
|
226
|
+
key.failure('when paymentDataType is 3DSecure, encryptedPINData cannot be defined') unless
|
227
|
+
values[:paymentData] && values[:paymentData][:encryptedPINData].nil?
|
228
|
+
end
|
132
229
|
end
|
133
|
-
|
134
|
-
|
230
|
+
|
231
|
+
rule(:paymentDataType, paymentData: :onlinePaymentCryptogram) do
|
232
|
+
# type can only be 3DSecure if emvData is empty
|
233
|
+
# old rule:
|
234
|
+
# rule('when paymentDataType is EMV, onlinePaymentCryptogram': [:paymentDataType, [:paymentData, :onlinePaymentCryptogram]]) do |type, cryptogram|
|
235
|
+
# type.eql?('EMV') > cryptogram.none?
|
236
|
+
# end
|
237
|
+
if values[:paymentDataType].eql?('EMV')
|
238
|
+
key.failure('when paymentDataType is EMV, onlinePaymentCryptogram cannot be defined') unless
|
239
|
+
values[:paymentData] && values[:paymentData][:onlinePaymentCryptogram].nil?
|
240
|
+
end
|
135
241
|
end
|
136
|
-
|
137
|
-
|
242
|
+
|
243
|
+
rule(:paymentDataType, paymentData: :eciIndicator) do
|
244
|
+
# type can only be 3DSecure if emvData is empty
|
245
|
+
# old rule:
|
246
|
+
# rule('when paymentDataType is EMV, eciIndicator': [:paymentDataType, [:paymentData, :eciIndicator]]) do |type, eci|
|
247
|
+
# type.eql?('EMV') > eci.none?
|
248
|
+
# end
|
249
|
+
if values[:paymentDataType].eql?('EMV')
|
250
|
+
key.failure('when paymentDataType is EMV, eciIndicator cannot be defined') unless
|
251
|
+
values[:paymentData] && values[:paymentData][:eciIndicator].nil?
|
252
|
+
end
|
138
253
|
end
|
139
|
-
|
140
|
-
|
254
|
+
|
255
|
+
rule(:paymentDataType, paymentData: :emvData) do
|
256
|
+
# type can only be 3DSecure if emvData is empty
|
257
|
+
# old rule:
|
258
|
+
# rule('when paymentDataType is EMV, emvData': [:paymentDataType, [:paymentData, :emvData]]) do |type, emv|
|
259
|
+
# type.eql?('EMV') > emv.filled?
|
260
|
+
# end
|
261
|
+
if values[:paymentDataType].eql?('EMV')
|
262
|
+
key.failure('when paymentDataType is EMV, emvData must be filled') unless
|
263
|
+
values[:paymentData] && values[:paymentData][:emvData]
|
264
|
+
end
|
141
265
|
end
|
142
266
|
|
267
|
+
rule(:paymentDataType, paymentData: :encryptedPINData) do
|
268
|
+
# type can only be 3DSecure if emvData is empty
|
269
|
+
# old rule:
|
270
|
+
# rule('when paymentDataType is EMV, encryptedPINData': [:paymentDataType, [:paymentData, :encryptedPINData]]) do |type, pin|
|
271
|
+
# type.eql?('EMV') > pin.filled?
|
272
|
+
# end
|
273
|
+
if values[:paymentDataType].eql?('EMV')
|
274
|
+
key.failure('when paymentDataType is EMV, encryptedPINData must be filled') unless
|
275
|
+
values[:paymentData] && values[:paymentData][:encryptedPINData]
|
276
|
+
end
|
277
|
+
end
|
143
278
|
end
|
144
279
|
|
280
|
+
TokenDataSchema = TokenDataContract.new
|
281
|
+
|
145
282
|
class Error < StandardError; end
|
146
283
|
|
147
284
|
module InstanceMethods
|
@@ -150,11 +287,11 @@ module Pedicel
|
|
150
287
|
def validate
|
151
288
|
@validation ||= @schema.call(@input)
|
152
289
|
|
153
|
-
@output = @validation.
|
290
|
+
@output = @validation.to_h
|
154
291
|
|
155
292
|
return true if @validation.success?
|
156
293
|
|
157
|
-
raise Error, "validation error: #{@validation.errors.keys.join(', ')}"
|
294
|
+
raise Error, "validation error: #{@validation.errors.to_h.keys.join(', ')}"
|
158
295
|
end
|
159
296
|
|
160
297
|
def valid?
|
@@ -166,7 +303,7 @@ module Pedicel
|
|
166
303
|
def errors
|
167
304
|
valid? unless @validation
|
168
305
|
|
169
|
-
@validation.errors
|
306
|
+
@validation.errors.to_h.sort
|
170
307
|
end
|
171
308
|
|
172
309
|
def errors_formatted(node = [errors])
|
data/lib/pedicel/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pedicel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clearhaus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-validation
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-schema
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
33
|
+
version: '1.9'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
40
|
+
version: '1.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-logic
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.0'
|
69
111
|
description:
|
70
112
|
email: hello@clearhaus.com
|
71
113
|
executables: []
|
@@ -90,14 +132,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
132
|
requirements:
|
91
133
|
- - "~>"
|
92
134
|
- !ruby/object:Gem::Version
|
93
|
-
version: 2.
|
135
|
+
version: 2.7.4
|
94
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
137
|
requirements:
|
96
138
|
- - ">="
|
97
139
|
- !ruby/object:Gem::Version
|
98
140
|
version: '0'
|
99
141
|
requirements: []
|
100
|
-
rubygems_version: 3.
|
142
|
+
rubygems_version: 3.1.6
|
101
143
|
signing_key:
|
102
144
|
specification_version: 4
|
103
145
|
summary: Decryption of Apple Pay payment tokens
|