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.
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.6.7
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-19 00:00:00.000000000 Z
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.12
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