verifiabl-issuer 0.1.0.pre.rc.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 +7 -0
- data/CHANGELOG.md +12 -0
- data/LICENSE +21 -0
- data/README.md +347 -0
- data/lib/verifiabl/issuer/assets/frame-1440.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-480.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-720.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-960.vfr1 +0 -0
- data/lib/verifiabl/issuer/base32.rb +34 -0
- data/lib/verifiabl/issuer/client.rb +361 -0
- data/lib/verifiabl/issuer/configuration.rb +48 -0
- data/lib/verifiabl/issuer/crypto.rb +46 -0
- data/lib/verifiabl/issuer/errors.rb +57 -0
- data/lib/verifiabl/issuer/frame_assets.rb +82 -0
- data/lib/verifiabl/issuer/generated/frame_widths.rb +10 -0
- data/lib/verifiabl/issuer/generated/pii_text_profile.rb +38 -0
- data/lib/verifiabl/issuer/instrumentation.rb +47 -0
- data/lib/verifiabl/issuer/payload.rb +89 -0
- data/lib/verifiabl/issuer/pii.rb +130 -0
- data/lib/verifiabl/issuer/png_encoder.rb +86 -0
- data/lib/verifiabl/issuer/qr.rb +192 -0
- data/lib/verifiabl/issuer/rendering.rb +415 -0
- data/lib/verifiabl/issuer/rendering_assets.rb +10 -0
- data/lib/verifiabl/issuer/responses.rb +129 -0
- data/lib/verifiabl/issuer/serialization.rb +98 -0
- data/lib/verifiabl/issuer/validation.rb +85 -0
- data/lib/verifiabl/issuer/version.rb +7 -0
- data/lib/verifiabl/issuer.rb +50 -0
- data/lib/verifiabl-issuer.rb +3 -0
- data/sig/verifiabl-issuer.rbs +136 -0
- metadata +189 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 92288131fb3b5bf6cc2533bf0eb1c8f7a0d351ba5256fe98a7a66a8a40992bcc
|
|
4
|
+
data.tar.gz: 3cbe2c9fade1351a663c98578ec719161b39d0201ed21839d9ebb8181335fb07
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2d390c9bf09d86a4675690d8d9af78def89063fcb7b36ff2e75ea53ef1fb3be4a4e6b3ae7b6f7db9733e49441893e48257b79a720d24c295cd35da47ebabb62e
|
|
7
|
+
data.tar.gz: 23f6605d84dce4cd2f10a6da1228183589ee37b45a8e60759aa5094d6a4cadf147bb78e0a6b26867120d45f4a03b63c5f49874ff1b14ee6856d0d307d5584817
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Verifiabl Ruby SDK will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## [0.1.0-rc.1] - 2026-09-17
|
|
8
|
+
|
|
9
|
+
- Add the official Ruby issuer client for single and batch payslip registration, with OAuth token caching, safe retries, request deadlines, validation, and observability hooks.
|
|
10
|
+
- Add compatible PII encryption and Verifiabl v2 QR generation with SVG and PNG rendering using the official frame assets.
|
|
11
|
+
- Add runnable API-managed and self-managed issuance examples.
|
|
12
|
+
- Support Ruby 3.3 through Ruby 4.0 with checked RBS signatures.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Verifiabl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Verifiabl Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for issuing Verifiabl payslip QR codes. It includes offline protocol primitives,
|
|
4
|
+
cross-SDK-compatible QR generation, deterministic SVG/PNG rendering, a hardened OAuth issuer
|
|
5
|
+
client, RBS signatures, and packed-gem consumer qualification.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add the gem to your bundle:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem "verifiabl-issuer"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then run `bundle install`. Bundler loads the gem through its package-name entry point:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require "verifiabl-issuer"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
That entry point is a compatibility shim and loads the canonical namespaced entry point. Applications
|
|
22
|
+
that require dependencies explicitly can use this path instead:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require "verifiabl/issuer"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Both forms load the same `Verifiabl::Issuer` implementation; applications do not need to require
|
|
29
|
+
both. Ruby 3.3 or newer is required. QR encoding uses the pure-Ruby `rqrcode_core` gem; Verifiabl
|
|
30
|
+
owns canonical mask selection and badge rendering rather than depending on the gem's PNG or SVG
|
|
31
|
+
exporters.
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Construct and retain an issuer client explicitly:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
issuer = Verifiabl::Issuer::Client.new(
|
|
39
|
+
Verifiabl::Issuer::Configuration.new(
|
|
40
|
+
environment: :sandbox,
|
|
41
|
+
client_id: ENV.fetch("VERIFIABL_CLIENT_ID"),
|
|
42
|
+
client_secret: ENV.fetch("VERIFIABL_CLIENT_SECRET")
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The SDK is framework-neutral: it does not load Rails or Active Support, install framework hooks, or
|
|
48
|
+
configure logging. Applications own their long-lived client and place it in their normal dependency
|
|
49
|
+
container; Rails applications commonly construct it in an initializer. A client is safe to share
|
|
50
|
+
between threads and refreshes its OAuth cache after a process fork.
|
|
51
|
+
|
|
52
|
+
Set `on_request`, `on_response`, and `on_error` on `Configuration` when request telemetry is needed.
|
|
53
|
+
Events contain request metadata but never request bodies, credentials, or payslip data. Observer
|
|
54
|
+
failures do not change request behavior.
|
|
55
|
+
|
|
56
|
+
The default overall deadline is 30 seconds and includes OAuth, 401 refresh, retry attempts, and
|
|
57
|
+
backoff. The Verifiabl reference is the idempotency key. `register_non_pii` generates and sends one
|
|
58
|
+
when omitted, so it and batch registration can safely retry transport failures, 408s, 429s, and 5xx
|
|
59
|
+
responses. API-managed barcode registration uses a server-generated reference and retries only 429,
|
|
60
|
+
which is rejected before processing. Configure these limits when needed:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
configuration = Verifiabl::Issuer::Configuration.new(
|
|
64
|
+
client_id: ENV.fetch("VERIFIABL_CLIENT_ID"),
|
|
65
|
+
client_secret: ENV.fetch("VERIFIABL_CLIENT_SECRET"),
|
|
66
|
+
timeout: 10,
|
|
67
|
+
max_retries: 2
|
|
68
|
+
)
|
|
69
|
+
issuer = Verifiabl::Issuer::Client.new(configuration)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Endpoint overrides are intended only for development. Issuer overrides require HTTPS except for
|
|
73
|
+
loopback HTTP. OAuth overrides are restricted to Verifiabl auth hosts or loopback addresses.
|
|
74
|
+
|
|
75
|
+
## Issue payslips
|
|
76
|
+
|
|
77
|
+
Choose one barcode flow for each payslip. Do not call both registration methods for the same
|
|
78
|
+
issuance.
|
|
79
|
+
|
|
80
|
+
### Self-managed barcode flow
|
|
81
|
+
|
|
82
|
+
Use this flow when your application renders the barcode. The following example is generated from
|
|
83
|
+
the runnable
|
|
84
|
+
[`examples/issuer/basic/issue_payslips_self_managed.rb`](../../examples/issuer/basic/issue_payslips_self_managed.rb)
|
|
85
|
+
source.
|
|
86
|
+
|
|
87
|
+
<!-- snippet:ruby.basic-issuance:start -->
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
require "base64"
|
|
91
|
+
require "fileutils"
|
|
92
|
+
require "verifiabl/issuer"
|
|
93
|
+
|
|
94
|
+
PAYSLIP = {
|
|
95
|
+
external_id: "PAY-1001",
|
|
96
|
+
pii: {
|
|
97
|
+
employee_name: "Jane A. Doe",
|
|
98
|
+
position: "Senior Developer",
|
|
99
|
+
department: "Engineering",
|
|
100
|
+
employer_abn: "12345678901",
|
|
101
|
+
bsb: "062-000",
|
|
102
|
+
account_number: "12345678",
|
|
103
|
+
account_name: "Jane A Doe",
|
|
104
|
+
address: "12 Example St, Sydney NSW 2000"
|
|
105
|
+
},
|
|
106
|
+
non_pii: {
|
|
107
|
+
period_start: "2026-08-01",
|
|
108
|
+
period_end: "2026-08-31",
|
|
109
|
+
payment_date: "2026-09-04",
|
|
110
|
+
currency: "AUD",
|
|
111
|
+
gross_cents: 900_000,
|
|
112
|
+
paygw_cents: 225_000,
|
|
113
|
+
net_cents: 675_000,
|
|
114
|
+
ytd_gross_cents: 5_400_000,
|
|
115
|
+
ytd_paygw_cents: 1_350_000
|
|
116
|
+
}
|
|
117
|
+
}.freeze
|
|
118
|
+
|
|
119
|
+
issuer = Verifiabl::Issuer::Client.new(
|
|
120
|
+
Verifiabl::Issuer::Configuration.new(
|
|
121
|
+
environment: :sandbox,
|
|
122
|
+
client_id: ENV.fetch("VERIFIABL_CLIENT_ID"),
|
|
123
|
+
client_secret: ENV.fetch("VERIFIABL_CLIENT_SECRET")
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
provider_encryption_key = Base64.strict_decode64(
|
|
128
|
+
ENV.fetch("VERIFIABL_ENCRYPTION_KEY_BASE64")
|
|
129
|
+
)
|
|
130
|
+
plaintext = Verifiabl::Issuer.format_pii(PAYSLIP.fetch(:pii))
|
|
131
|
+
|
|
132
|
+
encrypted = Verifiabl::Issuer.encrypt_pii(plaintext, provider_encryption_key)
|
|
133
|
+
|
|
134
|
+
registration = {
|
|
135
|
+
schema: "au.payslip.v1",
|
|
136
|
+
issued_at: Time.now.utc,
|
|
137
|
+
payslip_non_pii: PAYSLIP.fetch(:non_pii),
|
|
138
|
+
encryption_metadata: encrypted.encryption_metadata
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
result = issuer.register_non_pii(**registration)
|
|
142
|
+
|
|
143
|
+
barcode = Verifiabl::Issuer.build_barcode_svg(
|
|
144
|
+
verifiabl_reference: result.verifiabl_reference,
|
|
145
|
+
encrypted_pii: encrypted.encrypted_pii,
|
|
146
|
+
environment: :sandbox
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
output_path = File.expand_path("output/self-managed-barcode.svg", __dir__)
|
|
150
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
151
|
+
|
|
152
|
+
File.write(output_path, barcode.svg)
|
|
153
|
+
puts "Registered #{result.verifiabl_reference} and wrote #{output_path}"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
<!-- snippet:ruby.basic-issuance:end -->
|
|
157
|
+
|
|
158
|
+
`register_non_pii` sends the non-PII fields and encryption metadata, but not the encrypted PII. The
|
|
159
|
+
application combines the returned reference with the encrypted PII to render the SVG locally.
|
|
160
|
+
|
|
161
|
+
### API-managed barcode flow
|
|
162
|
+
|
|
163
|
+
Use this alternative when Verifiabl should render the barcode PNG. The following example is
|
|
164
|
+
generated from the runnable
|
|
165
|
+
[`examples/issuer/basic/issue_payslips_api_managed.rb`](../../examples/issuer/basic/issue_payslips_api_managed.rb)
|
|
166
|
+
source.
|
|
167
|
+
|
|
168
|
+
<!-- snippet:ruby.api-managed-issuance:start -->
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
require "base64"
|
|
172
|
+
require "fileutils"
|
|
173
|
+
require "verifiabl/issuer"
|
|
174
|
+
|
|
175
|
+
PAYSLIP = {
|
|
176
|
+
external_id: "PAY-1001",
|
|
177
|
+
pii: {
|
|
178
|
+
employee_name: "Jane A. Doe",
|
|
179
|
+
position: "Senior Developer",
|
|
180
|
+
department: "Engineering",
|
|
181
|
+
employer_abn: "12345678901",
|
|
182
|
+
bsb: "062-000",
|
|
183
|
+
account_number: "12345678",
|
|
184
|
+
account_name: "Jane A Doe",
|
|
185
|
+
address: "12 Example St, Sydney NSW 2000"
|
|
186
|
+
},
|
|
187
|
+
non_pii: {
|
|
188
|
+
period_start: "2026-08-01",
|
|
189
|
+
period_end: "2026-08-31",
|
|
190
|
+
payment_date: "2026-09-04",
|
|
191
|
+
currency: "AUD",
|
|
192
|
+
gross_cents: 900_000,
|
|
193
|
+
paygw_cents: 225_000,
|
|
194
|
+
net_cents: 675_000,
|
|
195
|
+
ytd_gross_cents: 5_400_000,
|
|
196
|
+
ytd_paygw_cents: 1_350_000
|
|
197
|
+
}
|
|
198
|
+
}.freeze
|
|
199
|
+
|
|
200
|
+
issuer = Verifiabl::Issuer::Client.new(
|
|
201
|
+
Verifiabl::Issuer::Configuration.new(
|
|
202
|
+
environment: :sandbox,
|
|
203
|
+
client_id: ENV.fetch("VERIFIABL_CLIENT_ID"),
|
|
204
|
+
client_secret: ENV.fetch("VERIFIABL_CLIENT_SECRET")
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
provider_encryption_key = Base64.strict_decode64(
|
|
209
|
+
ENV.fetch("VERIFIABL_ENCRYPTION_KEY_BASE64")
|
|
210
|
+
)
|
|
211
|
+
plaintext = Verifiabl::Issuer.format_pii(PAYSLIP.fetch(:pii))
|
|
212
|
+
|
|
213
|
+
encrypted = Verifiabl::Issuer.encrypt_pii(plaintext, provider_encryption_key)
|
|
214
|
+
|
|
215
|
+
registration = {
|
|
216
|
+
schema: "au.payslip.v1",
|
|
217
|
+
issued_at: Time.now.utc,
|
|
218
|
+
payslip_non_pii: PAYSLIP.fetch(:non_pii),
|
|
219
|
+
encryption_metadata: encrypted.encryption_metadata
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
result = issuer.register_and_build_barcode(
|
|
223
|
+
encrypted_pii: encrypted.encrypted_pii,
|
|
224
|
+
**registration
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
output_path = File.expand_path("output/api-managed-barcode.png", __dir__)
|
|
228
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
229
|
+
|
|
230
|
+
File.binwrite(output_path, Base64.strict_decode64(result.barcode.data))
|
|
231
|
+
puts "Registered #{result.verifiabl_reference} and wrote #{output_path}"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
<!-- snippet:ruby.api-managed-issuance:end -->
|
|
235
|
+
|
|
236
|
+
`register_and_build_barcode` sends the encrypted PII in addition to the non-PII fields and encryption
|
|
237
|
+
metadata. Verifiabl uses the ciphertext to render the returned PNG and does not retain it. Plaintext
|
|
238
|
+
PII is never sent in either flow.
|
|
239
|
+
|
|
240
|
+
### Retries and batches
|
|
241
|
+
|
|
242
|
+
For a self-managed registration that must remain retryable across process restarts, generate and
|
|
243
|
+
persist the reference before the first call, then reuse it on every attempt:
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
reference = Verifiabl::Issuer.generate_verifiabl_reference
|
|
247
|
+
persist_with_issuance_record(reference)
|
|
248
|
+
result = issuer.register_non_pii(
|
|
249
|
+
verifiabl_reference: reference,
|
|
250
|
+
**registration
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
For pay runs, register up to 1,000 self-managed records in one request:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
batch = issuer.register_non_pii_batch(records)
|
|
258
|
+
batch.results.each do |item|
|
|
259
|
+
case item.status
|
|
260
|
+
when "created", "duplicate"
|
|
261
|
+
puts item.verifiabl_reference
|
|
262
|
+
when "error"
|
|
263
|
+
warn "#{item.external_id}: #{item.code} #{item.detail}"
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The API returns `201` for the first registration and `200` for an identical replay. Reusing a
|
|
269
|
+
reference with different content raises `ApiError` with code `CONFLICT`.
|
|
270
|
+
|
|
271
|
+
Successful calls return immutable typed result objects under `Verifiabl::Issuer::Responses`.
|
|
272
|
+
Malformed successful API responses raise `TransportError`. Issuer HTTP errors raise `ApiError` (or
|
|
273
|
+
`IvReuseError` for `IV_REUSED`). Non-timeout OAuth connectivity failures and invalid or unsuccessful
|
|
274
|
+
token responses raise `AuthError`. Deadline expiry raises `TimeoutError`, while issuer network
|
|
275
|
+
failures raise `TransportError`. Request and error messages never include the submitted payslip body.
|
|
276
|
+
|
|
277
|
+
## Offline protocol primitives
|
|
278
|
+
|
|
279
|
+
Format and encrypt PII locally. The plaintext must never be logged or sent to Verifiabl:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
plaintext = Verifiabl::Issuer.format_pii(
|
|
283
|
+
employee_name: "Jane Doe",
|
|
284
|
+
employer_abn: "53004085616",
|
|
285
|
+
address: "12 Example St, Sydney NSW 2000"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
encrypted = Verifiabl::Issuer.encrypt_pii(plaintext, provider_encryption_key)
|
|
289
|
+
reference = Verifiabl::Issuer.generate_verifiabl_reference
|
|
290
|
+
|
|
291
|
+
scan_url = Verifiabl::Issuer.build_scan_url(
|
|
292
|
+
verifiabl_reference: reference,
|
|
293
|
+
encrypted_pii: encrypted.encrypted_pii,
|
|
294
|
+
environment: :sandbox
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
xmp_payload = Verifiabl::Issuer.build_barcode_payload(
|
|
298
|
+
verifiabl_reference: reference,
|
|
299
|
+
encrypted_pii: encrypted.encrypted_pii
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
svg = Verifiabl::Issuer.build_barcode_svg(
|
|
303
|
+
verifiabl_reference: reference,
|
|
304
|
+
encrypted_pii: encrypted.encrypted_pii,
|
|
305
|
+
environment: :sandbox,
|
|
306
|
+
width: 480
|
|
307
|
+
)
|
|
308
|
+
File.write("barcode.svg", svg.svg)
|
|
309
|
+
|
|
310
|
+
png = Verifiabl::Issuer.build_barcode_png(
|
|
311
|
+
verifiabl_reference: reference,
|
|
312
|
+
encrypted_pii: encrypted.encrypted_pii,
|
|
313
|
+
environment: :sandbox,
|
|
314
|
+
width: 720
|
|
315
|
+
)
|
|
316
|
+
File.binwrite("barcode.png", png.png)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
The renderers use the same official vector frame, rounded finder geometry, and QR error-correction
|
|
320
|
+
ladder as the Node and .NET SDKs. SVG widths are continuously scalable from 480 upward. PNG frames
|
|
321
|
+
are pre-rasterised for deterministic cross-SDK output and therefore support only `480`, `720`,
|
|
322
|
+
`960`, and `1440` pixels. Use `max_error_correction: :q` for greater damage recovery; the default is
|
|
323
|
+
`:m`, and the renderer steps down only when necessary to preserve the three-pixel module floor.
|
|
324
|
+
Each result's `degraded` field (`svg.degraded` or `png.degraded` above) is true when the renderer
|
|
325
|
+
steps down from the requested ceiling or the modules fall below the ideal four-pixel size.
|
|
326
|
+
|
|
327
|
+
`provider_encryption_key` must be exactly 32 bytes and loaded from a KMS or secrets manager. Every
|
|
328
|
+
encryption call creates a fresh AES-256-GCM IV. Ciphertext, IV, and authentication tags are exposed
|
|
329
|
+
as binary Ruby strings (`Encoding::BINARY`) and can be persisted directly in binary database
|
|
330
|
+
columns. The SDK encodes them only at an external boundary: base64url for issuer API requests and
|
|
331
|
+
uppercase, unpadded RFC 4648 Base32 for v2 barcode and XMP output. PDF integrations should store `xmp_payload` under
|
|
332
|
+
XMP namespace `https://verifiabl.io/ns/`, property `payload`. QR v2 is encoded as an explicit byte
|
|
333
|
+
prefix and alphanumeric ciphertext segment. Matrix version, mask, and modules match the Node and
|
|
334
|
+
.NET SDKs.
|
|
335
|
+
|
|
336
|
+
## Development
|
|
337
|
+
|
|
338
|
+
```sh
|
|
339
|
+
bundle install
|
|
340
|
+
bundle exec rake
|
|
341
|
+
(cd gems/verifiabl-issuer && bundle exec gem build verifiabl-issuer.gemspec)
|
|
342
|
+
bundle exec ruby script/packed_gem_check.rb
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
[MIT](./LICENSE)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Verifiabl
|
|
4
|
+
module Issuer
|
|
5
|
+
module Base32
|
|
6
|
+
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Encodes bytes as canonical uppercase, unpadded RFC 4648 Base32.
|
|
11
|
+
def encode(input)
|
|
12
|
+
raise ArgumentError, "input must be a String" unless input.is_a?(String)
|
|
13
|
+
|
|
14
|
+
output = +""
|
|
15
|
+
accumulator = 0
|
|
16
|
+
bits = 0
|
|
17
|
+
input.b.each_byte { |byte| accumulator, bits = append_byte(output, accumulator, bits, byte) }
|
|
18
|
+
output << ALPHABET[(accumulator << (5 - bits)) & 0x1f] if bits.positive?
|
|
19
|
+
output
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def append_byte(output, accumulator, bits, byte)
|
|
23
|
+
accumulator = (accumulator << 8) | byte
|
|
24
|
+
bits += 8
|
|
25
|
+
while bits >= 5
|
|
26
|
+
bits -= 5
|
|
27
|
+
output << ALPHABET[(accumulator >> bits) & 0x1f]
|
|
28
|
+
end
|
|
29
|
+
[accumulator & ((1 << bits) - 1), bits]
|
|
30
|
+
end
|
|
31
|
+
private_class_method :append_byte
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|