oydid 0.6.7 → 0.8.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/VERSION +1 -1
- data/lib/oydid/basic.rb +29 -1
- data/lib/oydid/log.rb +27 -7
- data/lib/oydid.rb +319 -92
- data/spec/fixtures/multi_hash_legacy.json +1766 -0
- data/spec/oydid_spec.rb +130 -0
- metadata +5 -3
data/spec/oydid_spec.rb
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
require_relative 'spec_helper'
|
|
2
|
+
require 'tmpdir'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'digest'
|
|
2
5
|
|
|
3
6
|
describe "OYDID handling" do
|
|
4
7
|
# basic functions - base58btc encoding
|
|
@@ -105,6 +108,133 @@ describe "OYDID handling" do
|
|
|
105
108
|
expect(Oydid.multi_hash(data, {digest: "blake2b-64"}).first).to eq expected
|
|
106
109
|
end
|
|
107
110
|
end
|
|
111
|
+
|
|
112
|
+
# intermediate BLAKE2b digest sizes (17-23 bytes) ---------------------------
|
|
113
|
+
# Needed so a did:oyd fits into the 50 character URL limit of the EU Digital
|
|
114
|
+
# Product Passport registry: sha2-256 yields a 55 character identifier,
|
|
115
|
+
# blake2b-16 is only 128 bit and therefore too weak for a 30 year lifetime.
|
|
116
|
+
BLAKE2B_EXTRA_SIZES = (17..23).to_a
|
|
117
|
+
|
|
118
|
+
# Backwards compatibility: recorded before the intermediate sizes were added.
|
|
119
|
+
# multi_hash must stay byte-identical for every digest that existed before,
|
|
120
|
+
# otherwise DIDs already in the wild stop resolving.
|
|
121
|
+
legacy_hashes = JSON.parse(File.read(File.expand_path("../fixtures/multi_hash_legacy.json", __FILE__)))
|
|
122
|
+
legacy_hashes.group_by { |e| [e["digest"], e["encode"]] }.each do |(digest, encode), entries|
|
|
123
|
+
it "keeps multi_hash byte-identical for #{digest} in #{encode}" do
|
|
124
|
+
entries.each do |entry|
|
|
125
|
+
message = [entry["message"]].pack("H*")
|
|
126
|
+
expect(Oydid.multi_hash(message, {digest: digest, encode: encode}).first).to eq entry["expected"]
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
it "keeps get_digest working for #{digest} in #{encode}" do
|
|
130
|
+
entries.each do |entry|
|
|
131
|
+
expect(Oydid.get_digest(entry["expected"]).first).to eq entry["name"]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
BLAKE2B_EXTRA_SIZES.each do |size|
|
|
137
|
+
digest = "blake2b-#{size}"
|
|
138
|
+
|
|
139
|
+
it "advertises #{digest} as supported" do
|
|
140
|
+
expect(Oydid::SUPPORTED_DIGESTS).to include digest
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "round-trips #{digest} through multi_hash and get_digest" do
|
|
144
|
+
hash = Oydid.multi_hash('{"a":1}', {digest: digest, encode: "base58btc"}).first
|
|
145
|
+
expect(hash).not_to be_nil
|
|
146
|
+
expect(Oydid.get_digest(hash).first).to eq digest
|
|
147
|
+
# the raw digest really has the requested size
|
|
148
|
+
decoded, _ = Oydid.multi_decode(hash)
|
|
149
|
+
expect(decoded.bytesize).to eq size + 2
|
|
150
|
+
expect(decoded[1].ord).to eq size
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "encodes #{digest} in every supported encoding" do
|
|
154
|
+
Oydid::SUPPORTED_ENCODINGS.each do |encoding|
|
|
155
|
+
hash = Oydid.multi_hash("payload", {digest: digest, encode: encoding}).first
|
|
156
|
+
expect(hash).not_to be_nil
|
|
157
|
+
expect(Oydid.get_digest(hash).first).to eq digest
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# base58btc length varies with the leading byte, so pin the maximum over a
|
|
163
|
+
# few hundred random inputs rather than a single sample
|
|
164
|
+
{17 => 27, 18 => 28, 19 => 29, 20 => 31, 21 => 32, 22 => 34, 23 => 35}.each do |size, expected_length|
|
|
165
|
+
it "produces a base58btc identifier of at most #{expected_length} characters for blake2b-#{size}" do
|
|
166
|
+
lengths = 500.times.map do |i|
|
|
167
|
+
Oydid.multi_hash(SecureRandom.hex(24) + i.to_s, {digest: "blake2b-#{size}", encode: "base58btc"}).first.length
|
|
168
|
+
end
|
|
169
|
+
expect(lengths.max).to eq expected_length
|
|
170
|
+
expect("did:oyd:".length + lengths.max).to eq expected_length + 8
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "assigns the acceptance size for the DPP use case" do
|
|
175
|
+
identifier = Oydid.multi_hash(Oydid.canonical({"a" => 1}), {digest: "blake2b-18", encode: "base58btc"}).first
|
|
176
|
+
expect(identifier.length).to eq 28
|
|
177
|
+
expect(("did:oyd:" + identifier).length).to eq 36
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "assigns collision-free single byte codes to all supported digests" do
|
|
181
|
+
codes = Oydid::SUPPORTED_DIGESTS.map do |digest|
|
|
182
|
+
hash = Oydid.multi_hash("collision check", {digest: digest, encode: "base58btc"}).first
|
|
183
|
+
Oydid.multi_decode(hash).first[0].ord
|
|
184
|
+
end
|
|
185
|
+
expect(codes.length).to eq Oydid::SUPPORTED_DIGESTS.length
|
|
186
|
+
expect(codes.uniq.length).to eq codes.length
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "keeps the new codes out of the range used by the pre-existing digests" do
|
|
190
|
+
# 0x02/0x04/0x08 are blake2b-16/-32/-64, 0x12-0x17 are the SHA digests
|
|
191
|
+
expect(Oydid::BLAKE2B_EXTRA_CODES.values.sort).to eq (0x0B..0x11).to_a
|
|
192
|
+
expect(Oydid::BLAKE2B_EXTRA_CODES.keys.sort).to eq BLAKE2B_EXTRA_SIZES
|
|
193
|
+
expect(Oydid::BLAKE2B_EXTRA_CODES.values & [0x02, 0x04, 0x08, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]).to be_empty
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "keeps the defaults unchanged" do
|
|
197
|
+
expect(Oydid::DEFAULT_DIGEST).to eq "sha2-256"
|
|
198
|
+
expect(Oydid::LOG_HASH_OPTIONS).to eq({:digest => "sha2-256", :encode => "base58btc"})
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
BLAKE2B_EXTRA_SIZES.each do |size|
|
|
202
|
+
it "creates and reads a DID with blake2b-#{size}" do
|
|
203
|
+
Dir.mktmpdir do |dir|
|
|
204
|
+
Dir.chdir(dir) do
|
|
205
|
+
result, msg = Oydid.create({"hello" => "dpp"}, {
|
|
206
|
+
digest: "blake2b-#{size}", encode: "base58btc",
|
|
207
|
+
doc_location: "local", location: "local",
|
|
208
|
+
return_secrets: true, key_type: "ed25519", silent: true})
|
|
209
|
+
expect(msg).to eq ""
|
|
210
|
+
expect(result).not_to be_nil
|
|
211
|
+
did = result["did"]
|
|
212
|
+
identifier = did.delete_prefix("did:oyd:").split(Oydid::LOCATION_PREFIX).first
|
|
213
|
+
expect(Oydid.get_digest(identifier).first).to eq "blake2b-#{size}"
|
|
214
|
+
|
|
215
|
+
read_result, read_msg = Oydid.read(did, {doc_location: "local", log_location: "local", silent: true})
|
|
216
|
+
expect(read_msg).to eq ""
|
|
217
|
+
expect(read_result).not_to be_nil
|
|
218
|
+
expect(read_result["doc"]["doc"]).to eq({"hello" => "dpp"})
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "does not shadow sha1, which shares code 0x11 with blake2b-23" do
|
|
225
|
+
# 0x11 is unused among the digests oydid produces, but the multicodec
|
|
226
|
+
# registry maps it to sha1. The length byte keeps the two apart: sha1 is
|
|
227
|
+
# always 20 bytes, blake2b-23 always 23.
|
|
228
|
+
sha1_multihash = Oydid.multi_encode([0x11, 20, Digest::SHA1.digest("test")].pack("CCa20"),
|
|
229
|
+
{encode: "base58btc"}).first
|
|
230
|
+
expect(Oydid.get_digest(sha1_multihash).first).to eq "sha1"
|
|
231
|
+
expect(Oydid.get_digest(Oydid.multi_hash("test", {digest: "blake2b-23"}).first).first).to eq "blake2b-23"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "still rejects an unsupported digest" do
|
|
235
|
+
expect(Oydid.multi_hash("data", {digest: "blake2b-24"}).last).to eq "unsupported digest: 'blake2b-24'"
|
|
236
|
+
end
|
|
237
|
+
|
|
108
238
|
Dir.glob(File.expand_path("../input/basic/*.json", __FILE__)).each do |input|
|
|
109
239
|
it "converts #{input.split('/').last}" do
|
|
110
240
|
expected = File.read(input.sub('input', 'output'))
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oydid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christoph Fabianek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-08-
|
|
10
|
+
date: 2026-08-22 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: simple_dag
|
|
@@ -270,6 +270,7 @@ files:
|
|
|
270
270
|
- spec/fixtures/http/oydid-log-zQmVwM.json
|
|
271
271
|
- spec/fixtures/http/oydid-log-zQmZEP.json
|
|
272
272
|
- spec/fixtures/http/oydid-log-zQmaBZ.json
|
|
273
|
+
- spec/fixtures/multi_hash_legacy.json
|
|
273
274
|
- spec/input/basic/arrays.json
|
|
274
275
|
- spec/input/basic/french.json
|
|
275
276
|
- spec/input/basic/sample2_get_location.doc
|
|
@@ -432,7 +433,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
432
433
|
- !ruby/object:Gem::Version
|
|
433
434
|
version: '0'
|
|
434
435
|
requirements: []
|
|
435
|
-
rubygems_version: 4.0.
|
|
436
|
+
rubygems_version: 4.0.19
|
|
436
437
|
specification_version: 4
|
|
437
438
|
summary: Own Your Decentralized Identifier for Ruby.
|
|
438
439
|
test_files:
|
|
@@ -445,6 +446,7 @@ test_files:
|
|
|
445
446
|
- spec/fixtures/http/oydid-log-zQmVwM.json
|
|
446
447
|
- spec/fixtures/http/oydid-log-zQmZEP.json
|
|
447
448
|
- spec/fixtures/http/oydid-log-zQmaBZ.json
|
|
449
|
+
- spec/fixtures/multi_hash_legacy.json
|
|
448
450
|
- spec/input/basic/arrays.json
|
|
449
451
|
- spec/input/basic/french.json
|
|
450
452
|
- spec/input/basic/sample2_get_location.doc
|