pedicel 0.1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74bf3607219c5927eb2c3409abb203f3e8db743ee8d7e264f10b5c801fc8fde3
4
- data.tar.gz: a1bc91e194a468eed00e996af0b149d784a801f3fa8608129696af26d1fc34bd
3
+ metadata.gz: 8effeb35a7193cf9470319b3666b17a7105e3abb94d404882ce5ca705feca4b3
4
+ data.tar.gz: 9586a185c9dd0c0ea2faeea433f938507391cc91c20e7000c1b1a60f47b70e52
5
5
  SHA512:
6
- metadata.gz: dd75b505a0eefefba6fa101e6fcd65e5274b495871a602f3f7b308df34a58ca9b10855b090344256658379c330e76a1658f002aa00e46c55bbe14f15c5f12087
7
- data.tar.gz: c3cf9f84d1e0fb73bfe0241a305c9bec64a5e75f84ea4c42a95303c88873de3eadd0890d511ca6a3cf45dfc20897b87ecb6981d74f70c5122d3342aa2a1df275
6
+ metadata.gz: f3ae4d8a50ec94b2f217a19c63aeeeeab150296d85b7716fe3b2ef2721839059eeb14c69e42c5f503f84f4662dec72e7b7e19292c40ccc2d7ef48eccf84a64fb
7
+ data.tar.gz: '091465c0bcdba0987a5cae1df961ae68f0c0f406278187072a9d7d980c2e20e51b419cd501924d96adfb4448917deaa5343465d69168d8fa0d80330ba1e23442'
data/lib/pedicel/ec.rb CHANGED
@@ -3,6 +3,8 @@ require 'pedicel/base'
3
3
  module Pedicel
4
4
  class EC < Base
5
5
  def ephemeral_public_key
6
+ @token[:header].transform_keys!(&:to_sym)
7
+
6
8
  Base64.decode64(@token[:header][:ephemeralPublicKey])
7
9
  end
8
10
 
@@ -1,4 +1,6 @@
1
- require 'dry-validation'
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
- module Predicates
13
- include Dry::Logic::Predicates
14
-
15
- CUSTOM_PREDICATE_ERRORS = {
16
- base64?: 'must be Base64',
17
- hex?: 'must be hex',
18
- pan?: 'must be a pan',
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
- # Support Ruby 2.3, but use the faster #match? when available.
29
- match_b = String.new.respond_to?(:match?) ? ->(s, re) { s.match?(re) } : ->(s, re) { !!(s =~ re) }
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
- predicate(:base64?) do |x|
32
- str?(x) &&
33
- match_b.(x, /\A[=A-Za-z0-9+\/]*\z/) && # allowable chars
34
- x.length.remainder(4).zero? && # multiple of 4
35
- !match_b.(x, /=[^$=]/) && # may only end with ='s
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
- # We should figure out how strict we should be. Hopefully we can discard
40
- # the above Base64? predicate and use the following simpler one:
41
- #predicate(:strict_base64?) { |x| !!Base64.strict_decode64(x) rescue false }
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
- predicate(:base64_sha256?) { |x| base64?(x) && Base64.decode64(x).length == 32 }
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
- predicate(:hex?) { |x| str?(x) && match_b.(x, /\A[a-f0-9]*\z/i) }
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
- predicate(:hex_sha256?) { |x| hex?(x) && x.length == 64 }
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
- predicate(:pan?) { |x| str?(x) && match_b.(x, /\A[1-9][0-9]{11,18}\z/) }
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
- predicate(:yymmdd?) { |x| str?(x) && match_b.(x, /\A\d{6}\z/) }
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
- predicate(:eci?) { |x| str?(x) && match_b.(x, /\A\d{1,2}\z/) }
111
+ class TokenHeaderContract < Dry::Validation::Contract
112
+ json do
113
+ optional(:applicationData).filled(:str?)
54
114
 
55
- predicate(:ec_public_key?) { |x| base64?(x) && OpenSSL::PKey::EC.new(Base64.decode64(x)).check_key rescue false }
115
+ optional(:ephemeralPublicKey).filled(:str?)
56
116
 
57
- predicate(:pkcs7_signature?) { |x| base64?(x) && !!OpenSSL::PKCS7.new(Base64.decode64(x)) rescue false }
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
- class BaseSchema < Dry::Validation::Schema::JSON
63
- predicates(Predicates)
64
- def self.messages
65
- super.merge(en: { errors: Predicates::CUSTOM_PREDICATE_ERRORS })
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 = Dry::Validation.Schema(BaseSchema) do
70
- optional(:applicationData).filled(:str?, :hex?, :hex_sha256?)
134
+ TokenHeaderSchema = TokenHeaderContract.new
71
135
 
72
- optional(:ephemeralPublicKey).filled(:str?, :base64?, :ec_public_key?)
136
+ class TokenContract < Dry::Validation::Contract
137
+ json do
138
+ required(:data).filled(:str?)
73
139
 
74
- optional(:wrappedKey).filled(:str?, :base64?)
140
+ required(:header).hash(TokenHeaderContract.schema)
75
141
 
76
- rule('ephemeralPublicKey xor wrappedKey': [:ephemeralPublicKey, :wrappedKey]) do |e, w|
77
- e.filled? ^ w.filled?
78
- end
142
+ required(:signature).filled(:str?)
79
143
 
80
- required(:publicKeyHash).filled(:str?, :base64?, :base64_sha256?)
144
+ required(:version).filled(:str?, included_in?: %w[EC_v1 RSA_v1])
81
145
 
82
- required(:transactionId).filled(:str?, :hex?)
146
+ end
147
+ rule(:data).validate(:is_base64)
148
+ rule(:signature).validate(:is_base64, :is_pkcs7_signature)
83
149
  end
84
150
 
85
- TokenSchema = Dry::Validation.Schema(BaseSchema) do
86
- required(:data).filled(:str?, :base64?)
151
+ TokenSchema = TokenContract.new
87
152
 
88
- required(:header).schema(TokenHeaderSchema)
153
+ class TokenDataPaymentDataContract < Dry::Validation::Contract
154
+ json do
155
+ optional(:onlinePaymentCryptogram).filled(:str?)
156
+ optional(:eciIndicator).filled(:str?)
89
157
 
90
- required(:signature).filled(:str?, :base64?, :pkcs7_signature?)
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
- required(:version).filled(:str?, included_in?: %w[EC_v1 RSA_v1])
164
+ rule(:emvData).validate(:is_base64)
165
+ rule(:encryptedPINData).validate(:is_hex)
93
166
  end
94
167
 
95
- TokenDataPaymentDataSchema = Dry::Validation.Schema(BaseSchema) do
96
- optional(:onlinePaymentCryptogram).filled(:str?, :base64?)
97
- optional(:eciIndicator).filled(:str?, :eci?)
168
+ TokenDataPaymentDataSchema = TokenDataPaymentDataContract.new
98
169
 
99
- optional(:emvData).filled(:str?, :base64?)
100
- optional(:encryptedPINData).filled(:str?, :hex?)
101
- end
170
+ class TokenDataContract < Dry::Validation::Contract
171
+ json do
172
+ required(:applicationPrimaryAccountNumber).filled(:str?)
173
+
174
+ required(:applicationExpirationDate).filled(:str?)
102
175
 
103
- TokenDataSchema = Dry::Validation.Schema(BaseSchema) do
104
- required(:applicationPrimaryAccountNumber).filled(:str?, :pan?)
176
+ required(:currencyCode).filled(:str?)
105
177
 
106
- required(:applicationExpirationDate).filled(:str?, :yymmdd?)
178
+ required(:transactionAmount).filled(:int?)
107
179
 
108
- required(:currencyCode).filled(:str?, :iso4217_numeric?)
180
+ optional(:cardholderName).filled(:str?)
109
181
 
110
- required(:transactionAmount).filled(:int?)
182
+ required(:deviceManufacturerIdentifier).filled(:str?)
111
183
 
112
- optional(:cardholderName).filled(:str?)
184
+ required(:paymentDataType).filled(:str?, included_in?: %w[3DSecure EMV])
113
185
 
114
- required(:deviceManufacturerIdentifier).filled(:str?, :hex?)
186
+ required(:paymentData).hash(TokenDataPaymentDataContract.schema)
187
+ end
188
+ rule(:applicationPrimaryAccountNumber).validate(:is_pan)
115
189
 
116
- required(:paymentDataType).filled(:str?, included_in?: %w[3DSecure EMV])
190
+ rule(:applicationExpirationDate).validate(:is_yymmdd)
117
191
 
118
- required(:paymentData).schema(TokenDataPaymentDataSchema)
192
+ rule(:currencyCode).validate(:is_iso4217_numeric)
119
193
 
120
- rule('when paymentDataType is 3DSecure, onlinePaymentCryptogram': [:paymentDataType, [:paymentData, :onlinePaymentCryptogram]]) do |type, cryptogram|
121
- type.eql?('3DSecure') > cryptogram.filled?
122
- end
123
- rule('when paymentDataType is 3DSecure, emvData': [:paymentDataType, [:paymentData, :emvData]]) do |type, emv|
124
- type.eql?('3DSecure') > emv.none?
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
- rule('when paymentDataType is 3DSecure, encryptedPINData': [:paymentDataType, [:paymentData, :encryptedPINData]]) do |type, pin|
127
- type.eql?('3DSecure') > pin.none?
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('when paymentDataType is EMV, onlinePaymentCryptogram': [:paymentDataType, [:paymentData, :onlinePaymentCryptogram]]) do |type, cryptogram|
131
- type.eql?('EMV') > cryptogram.none?
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
- rule('when paymentDataType is EMV, eciIndicator': [:paymentDataType, [:paymentData, :eciIndicator]]) do |type, eci|
134
- type.eql?('EMV') > eci.none?
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
- rule('when paymentDataType is EMV, emvData': [:paymentDataType, [:paymentData, :emvData]]) do |type, emv|
137
- type.eql?('EMV') > emv.filled?
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
- rule('when paymentDataType is EMV, encryptedPINData': [:paymentDataType, [:paymentData, :encryptedPINData]]) do |type, pin|
140
- type.eql?('EMV') > pin.filled?
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.output
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])
@@ -1,3 +1,3 @@
1
1
  module Pedicel
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '1.0.1'.freeze
3
3
  end
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.0
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: 2020-11-17 00:00:00.000000000 Z
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: 0.11.1
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: 0.11.1
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.5.5
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.0.8
142
+ rubygems_version: 3.1.6
101
143
  signing_key:
102
144
  specification_version: 4
103
145
  summary: Decryption of Apple Pay payment tokens