oydid 0.9.4 → 0.9.6
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/didcomm.rb +75 -7
- data/lib/oydid/log.rb +15 -0
- data/lib/oydid.rb +9 -0
- data/spec/oydid_spec.rb +143 -0
- metadata +22 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2852f618ab2af5a4076ece7807b931168d061345496397dfc3f409faf25da10
|
|
4
|
+
data.tar.gz: 53bc6f370b519b0c72eedbf0bac1fb742fd5288b3696e93c080137531e8eedb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8cc1a99558914d4c968db056cfbd2aa50835ecbc22dc981e54f4eee24c95dbc9fc0e4c7e7c2378c4fe96672255ce54ae9ce91d6d3e3a6e1f2fe96a7d4a26b3fb
|
|
7
|
+
data.tar.gz: 420f07f1d3a5145dedfdfb1d65fe8cf83c979bcd99283e48437b2291e04007e3532ea3ea5ff53a9cc8a35fb155989989d0bac094fc8051e9d083641a83a54ff1
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.6
|
data/lib/oydid/didcomm.rb
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
class Oydid
|
|
5
5
|
|
|
6
|
+
# jwt-eddsa normalises the algorithm name to 'EdDSA' in the JOSE header, but
|
|
7
|
+
# dcsm writes an explicit 'ED25519' header. Accept both spellings of the same
|
|
8
|
+
# Ed25519 signature algorithm when verifying.
|
|
9
|
+
ED25519_ALGS = ['EdDSA', 'ED25519'].freeze
|
|
10
|
+
|
|
6
11
|
# DIDComm Plain Message ---------------------
|
|
7
12
|
def self.dcpm(payload, options)
|
|
8
13
|
dcDoc = {}
|
|
@@ -24,7 +29,9 @@ class Oydid
|
|
|
24
29
|
code, length, digest = multi_decode(private_key_encoded).first.unpack('SCa*')
|
|
25
30
|
case Multicodecs[code].name
|
|
26
31
|
when 'ed25519-priv'
|
|
27
|
-
|
|
32
|
+
# jwt-eddsa signs with an Ed25519::SigningKey from the ed25519 gem;
|
|
33
|
+
# an RbNaCl signing key is rejected with JWT::EncodeError.
|
|
34
|
+
private_key = Ed25519::SigningKey.new(digest)
|
|
28
35
|
token = JWT.encode payload, private_key, 'ED25519', { typ: 'JWM', kid: options[:sign_did].to_s, alg: 'ED25519' }
|
|
29
36
|
else
|
|
30
37
|
token = nil
|
|
@@ -33,18 +40,67 @@ class Oydid
|
|
|
33
40
|
return [token, error]
|
|
34
41
|
end
|
|
35
42
|
|
|
43
|
+
# w3c() mixes string keys at the top level with symbol keys inside a
|
|
44
|
+
# verification method, so read every attribute through this helper.
|
|
45
|
+
def self.dd_attr(hash, key)
|
|
46
|
+
return nil unless hash.is_a?(Hash)
|
|
47
|
+
hash[key].nil? ? hash[key.to_sym] : hash[key]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The 'authentication' section of a DID document may embed a verification
|
|
51
|
+
# method or - which is how did:oyd resolves it - reference one by id. Only
|
|
52
|
+
# keys listed there may be used to authenticate the DID subject, so a
|
|
53
|
+
# document without an authentication section is rejected rather than
|
|
54
|
+
# silently falling back to some other key.
|
|
55
|
+
def self.authentication_key(didDocument)
|
|
56
|
+
verification_methods = dd_attr(didDocument, "verificationMethod") || []
|
|
57
|
+
entry = (dd_attr(didDocument, "authentication") || []).first
|
|
58
|
+
vm = case entry
|
|
59
|
+
when Hash then entry
|
|
60
|
+
when String then verification_methods.find { |v| dd_attr(v, "id").to_s == entry }
|
|
61
|
+
end
|
|
62
|
+
key = dd_attr(vm, "publicKeyMultibase")
|
|
63
|
+
if key.to_s == ""
|
|
64
|
+
return [nil, "no authentication key in DID document"]
|
|
65
|
+
end
|
|
66
|
+
return [key, ""]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A DID may be written with or without the default location and with the
|
|
70
|
+
# location separator in either spelling, so compare the normalised forms.
|
|
71
|
+
# A fragment ("#key-doc") names a key within the DID, not another subject.
|
|
72
|
+
def self.same_did?(one, other)
|
|
73
|
+
percent_encode(one.to_s.split("#").first.to_s) ==
|
|
74
|
+
percent_encode(other.to_s.split("#").first.to_s)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Verifying a token proves that whoever controls the DID named in its own
|
|
78
|
+
# 'kid' header signed it - not that the expected party did. Callers that
|
|
79
|
+
# read the result as an authorisation have to say which DID they expect,
|
|
80
|
+
# which is what options[:expect_did] is for. Checked before resolving, so a
|
|
81
|
+
# token from a foreign DID costs no network round trip.
|
|
36
82
|
def self.dcsm_verify(token, options)
|
|
37
83
|
error = ""
|
|
38
84
|
decoded_payload = JWT.decode token, nil, false
|
|
39
85
|
pubkey_did = decoded_payload.last["kid"]
|
|
86
|
+
if options[:expect_did].to_s != "" && !same_did?(pubkey_did, options[:expect_did])
|
|
87
|
+
return [nil, "token was signed by " + pubkey_did.to_s +
|
|
88
|
+
", expected " + options[:expect_did].to_s]
|
|
89
|
+
end
|
|
40
90
|
result, msg = Oydid.read(pubkey_did, options)
|
|
41
|
-
|
|
91
|
+
if result.nil?
|
|
92
|
+
return [nil, msg.to_s == "" ? "cannot resolve " + pubkey_did.to_s : msg.to_s]
|
|
93
|
+
end
|
|
94
|
+
public_key_encoded, error = authentication_key(Oydid.w3c(result, options))
|
|
95
|
+
if public_key_encoded.nil?
|
|
96
|
+
return [nil, error]
|
|
97
|
+
end
|
|
42
98
|
begin
|
|
43
99
|
code, length, digest = multi_decode(public_key_encoded).first.unpack('CCa*')
|
|
44
100
|
case Multicodecs[code].name
|
|
45
101
|
when 'ed25519-pub'
|
|
46
|
-
public_key =
|
|
47
|
-
payload = JWT.decode token.to_s, public_key, true, {
|
|
102
|
+
public_key = Ed25519::VerifyKey.new(digest)
|
|
103
|
+
payload = JWT.decode token.to_s, public_key, true, { algorithms: ED25519_ALGS }
|
|
48
104
|
else
|
|
49
105
|
payload = nil
|
|
50
106
|
error = "unsupported key codec"
|
|
@@ -61,7 +117,7 @@ class Oydid
|
|
|
61
117
|
code, length, digest = multi_decode(private_key_encoded).first.unpack('SCa*')
|
|
62
118
|
case Multicodecs[code].name
|
|
63
119
|
when 'ed25519-priv'
|
|
64
|
-
private_key =
|
|
120
|
+
private_key = Ed25519::SigningKey.new(digest)
|
|
65
121
|
token = JWT.encode payload, private_key, 'ED25519'
|
|
66
122
|
when 'p256-priv'
|
|
67
123
|
group = OpenSSL::PKey::EC::Group.new('prime256v1')
|
|
@@ -98,8 +154,8 @@ class Oydid
|
|
|
98
154
|
code, length, digest = Oydid.multi_decode(public_key_encoded).first.unpack('CCa*')
|
|
99
155
|
case Multicodecs[code].name
|
|
100
156
|
when 'ed25519-pub'
|
|
101
|
-
public_key =
|
|
102
|
-
payload = JWT.decode token.to_s, public_key, true, {
|
|
157
|
+
public_key = Ed25519::VerifyKey.new(digest)
|
|
158
|
+
payload = JWT.decode token.to_s, public_key, true, { algorithms: ED25519_ALGS }
|
|
103
159
|
else
|
|
104
160
|
payload = nil
|
|
105
161
|
error = "unsupported key codec"
|
|
@@ -108,12 +164,24 @@ class Oydid
|
|
|
108
164
|
end
|
|
109
165
|
|
|
110
166
|
# signing for JWS ---------------------------
|
|
167
|
+
# An empty HMAC key is no key at all: OpenSSL::HMAC.digest('SHA256', '', data)
|
|
168
|
+
# returns a valid digest, so anyone can recompute the signature. jwt >= 3.2.0
|
|
169
|
+
# refuses it (CVE-2026-45363), earlier versions happily accepted forged
|
|
170
|
+
# tokens - most notably via the CLI, where a missing --hmac_secret arrives
|
|
171
|
+
# here as "". Reject it ourselves so the behaviour does not depend on which
|
|
172
|
+
# jwt version a consumer resolves.
|
|
111
173
|
def self.msg_sign(payload, hmac_secret)
|
|
174
|
+
if hmac_secret.to_s == ""
|
|
175
|
+
return [nil, "HMAC secret must not be empty"]
|
|
176
|
+
end
|
|
112
177
|
token = JWT.encode payload, hmac_secret, 'HS256'
|
|
113
178
|
return [token, ""]
|
|
114
179
|
end
|
|
115
180
|
|
|
116
181
|
def self.msg_verify_jws(token, hmac_secret)
|
|
182
|
+
if hmac_secret.to_s == ""
|
|
183
|
+
return [nil, "HMAC secret must not be empty"]
|
|
184
|
+
end
|
|
117
185
|
begin
|
|
118
186
|
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
|
|
119
187
|
return [decoded_token, ""]
|
data/lib/oydid/log.rb
CHANGED
|
@@ -90,6 +90,21 @@ class Oydid
|
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# collapse byte-identical log entries (keep first), keyed on the entry hash
|
|
94
|
+
# that `previous` references resolve to. A replayed CREATE or tangling
|
|
95
|
+
# TERMINATE would otherwise trip dag_did's CREATE/terminate counts and make
|
|
96
|
+
# the DID unresolvable - reachable through the unauthenticated append path
|
|
97
|
+
# with no signing key. Genuinely distinct fork entries (different hash) are
|
|
98
|
+
# kept and still fail closed as ambiguous.
|
|
99
|
+
def self.dedup_log(logs)
|
|
100
|
+
return logs unless logs.is_a?(Array)
|
|
101
|
+
seen = {}
|
|
102
|
+
logs.select do |el|
|
|
103
|
+
key = (canonical(el.slice("ts","op","doc","sig","previous")) rescue el.inspect)
|
|
104
|
+
seen.key?(key) ? false : (seen[key] = true)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
93
108
|
def self.dag_did(logs, options)
|
|
94
109
|
dag = DAG.new
|
|
95
110
|
dag_log = []
|
data/lib/oydid.rb
CHANGED
|
@@ -142,6 +142,14 @@ class Oydid
|
|
|
142
142
|
log_hash = hash_split[0]
|
|
143
143
|
log_location = hash_split[1]
|
|
144
144
|
end
|
|
145
|
+
# D11: the log reference may carry its location as %40 (the
|
|
146
|
+
# W3C-conform form of @); split on both, exactly as the DID split
|
|
147
|
+
# above does.
|
|
148
|
+
if log_hash.include?(CGI.escape LOCATION_PREFIX)
|
|
149
|
+
hash_split = log_hash.split(CGI.escape LOCATION_PREFIX)
|
|
150
|
+
log_hash = hash_split[0]
|
|
151
|
+
log_location = hash_split[1]
|
|
152
|
+
end
|
|
145
153
|
end
|
|
146
154
|
if log_location == ""
|
|
147
155
|
log_location = DEFAULT_LOCATION
|
|
@@ -152,6 +160,7 @@ class Oydid
|
|
|
152
160
|
if log_array.nil?
|
|
153
161
|
return [nil, msg]
|
|
154
162
|
else
|
|
163
|
+
log_array = dedup_log(log_array)
|
|
155
164
|
if options[:trace]
|
|
156
165
|
puts " .. Log retrieved"
|
|
157
166
|
end
|
data/spec/oydid_spec.rb
CHANGED
|
@@ -646,6 +646,37 @@ describe "OYDID handling" do
|
|
|
646
646
|
end
|
|
647
647
|
end
|
|
648
648
|
|
|
649
|
+
# SECURITY regression: the append endpoint is unauthenticated, so a byte-identical
|
|
650
|
+
# log entry can be replayed with no signing key. A duplicated CREATE trips dag_did's
|
|
651
|
+
# "wrong number of CREATE entries" and a duplicated tangling TERMINATE its terminate
|
|
652
|
+
# count, making the DID unresolvable. dedup_log collapses byte-identical replays
|
|
653
|
+
# (keep-first) at ingestion; genuinely distinct fork entries survive and still fail
|
|
654
|
+
# closed as ambiguous.
|
|
655
|
+
describe "dedup_log collapses byte-identical replayed entries" do
|
|
656
|
+
let(:create_e) { { "ts" => 1, "op" => 2, "doc" => "zCreate", "sig" => "z", "previous" => [] } }
|
|
657
|
+
let(:term_e) { { "ts" => 1, "op" => 0, "doc" => "zTerm", "sig" => "z", "previous" => [] } }
|
|
658
|
+
|
|
659
|
+
it "removes a byte-identical duplicate, keeping the first" do
|
|
660
|
+
deduped = Oydid.dedup_log([create_e, term_e, create_e.dup])
|
|
661
|
+
expect(deduped.length).to eq 2
|
|
662
|
+
expect(deduped.count { |e| e["op"] == 2 }).to eq 1
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
it "lets a de-duplicated log resolve where the raw one fails" do
|
|
666
|
+
logs = [create_e, term_e, create_e.dup]
|
|
667
|
+
expect(Oydid.dag_did(logs, { silent: true }).last).to match(/wrong number of CREATE/)
|
|
668
|
+
dag, create_index, _t, msg = Oydid.dag_did(Oydid.dedup_log(logs), { silent: true })
|
|
669
|
+
expect(msg).to eq ""
|
|
670
|
+
expect(dag).not_to be_nil
|
|
671
|
+
expect(create_index).to eq 0
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it "keeps genuinely distinct entries (a real fork is not collapsed)" do
|
|
675
|
+
fork = create_e.merge("doc" => "zCreate2")
|
|
676
|
+
expect(Oydid.dedup_log([create_e, fork, term_e]).length).to eq 3
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
649
680
|
# a broken repository (5xx) has to stay distinguishable from a DID that is
|
|
650
681
|
# not stored there - otherwise the caller reports "not found" and thereby
|
|
651
682
|
# claims the identifier never existed
|
|
@@ -1155,4 +1186,116 @@ describe "OYDID handling" do
|
|
|
1155
1186
|
end
|
|
1156
1187
|
end
|
|
1157
1188
|
end
|
|
1189
|
+
# These four defects together made the whole DIDComm branch unusable and, in
|
|
1190
|
+
# the HMAC case, forgeable. Nothing in the suite touched it before.
|
|
1191
|
+
describe "DIDComm signing" do
|
|
1192
|
+
let(:priv) { Oydid.generate_private_key("didcomm-spec-pwd", "ed25519-priv", {}).first }
|
|
1193
|
+
let(:pub) { Oydid.public_key(priv, {}).first }
|
|
1194
|
+
|
|
1195
|
+
# jwt < 3.2.0 verified an HS256 token against an empty key, so anybody could
|
|
1196
|
+
# forge one (CVE-2026-45363). The CLI reaches here with "" whenever
|
|
1197
|
+
# --hmac_secret is omitted, so the gem refuses the empty key itself.
|
|
1198
|
+
it "refuses to sign with an empty HMAC secret" do
|
|
1199
|
+
token, msg = Oydid.msg_sign({ "a" => 1 }, "")
|
|
1200
|
+
|
|
1201
|
+
expect(token).to be_nil
|
|
1202
|
+
expect(msg).to eq("HMAC secret must not be empty")
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
it "refuses to verify a token forged with an empty HMAC key" do
|
|
1206
|
+
header = Base64.urlsafe_encode64('{"alg":"HS256"}').delete("=")
|
|
1207
|
+
payload = Base64.urlsafe_encode64('{"sub":"attacker"}').delete("=")
|
|
1208
|
+
digest = OpenSSL::HMAC.digest("SHA256", "", "#{header}.#{payload}")
|
|
1209
|
+
forged = "#{header}.#{payload}.#{Base64.urlsafe_encode64(digest).delete('=')}"
|
|
1210
|
+
|
|
1211
|
+
decoded, msg = Oydid.msg_verify_jws(forged, "")
|
|
1212
|
+
|
|
1213
|
+
expect(decoded).to be_nil
|
|
1214
|
+
expect(msg).to eq("HMAC secret must not be empty")
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
it "still round-trips an HMAC signature with a real secret" do
|
|
1218
|
+
token, = Oydid.msg_sign({ "a" => 1 }, "s3cr3t")
|
|
1219
|
+
decoded, msg = Oydid.msg_verify_jws(token, "s3cr3t")
|
|
1220
|
+
|
|
1221
|
+
expect(msg).to eq("")
|
|
1222
|
+
expect(decoded.first).to eq({ "a" => 1 })
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
# jwt-eddsa signs with Ed25519::SigningKey; handing it the RbNaCl key raised
|
|
1226
|
+
# JWT::EncodeError, so "oydid jws" could not produce a token at all.
|
|
1227
|
+
it "signs a DIDComm message with the Ed25519 document key" do
|
|
1228
|
+
token, msg = Oydid.dcsm({ "a" => 1 }, priv, { sign_did: "did:oyd:zSpec" })
|
|
1229
|
+
|
|
1230
|
+
expect(msg).to eq("")
|
|
1231
|
+
_, _, digest = Oydid.multi_decode(pub).first.unpack("CCa*")
|
|
1232
|
+
decoded = JWT.decode(token, Ed25519::VerifyKey.new(digest), true,
|
|
1233
|
+
{ algorithms: Oydid::ED25519_ALGS })
|
|
1234
|
+
expect(decoded.first).to eq({ "a" => 1 })
|
|
1235
|
+
expect(decoded.last["kid"]).to eq("did:oyd:zSpec")
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
# w3c() lists "authentication" as a reference and uses symbol keys inside a
|
|
1239
|
+
# verification method - the old code indexed a String with "publicKeyMultibase".
|
|
1240
|
+
describe "authentication_key" do
|
|
1241
|
+
let(:didDocument) do
|
|
1242
|
+
{ "authentication" => ["did:oyd:zSpec#key-doc"],
|
|
1243
|
+
"verificationMethod" => [
|
|
1244
|
+
{ id: "did:oyd:zSpec#key-rev", publicKeyMultibase: "z6MkRev" },
|
|
1245
|
+
{ id: "did:oyd:zSpec#key-doc", publicKeyMultibase: "z6MkDoc" }
|
|
1246
|
+
] }
|
|
1247
|
+
end
|
|
1248
|
+
|
|
1249
|
+
it "dereferences a referenced verification method" do
|
|
1250
|
+
key, msg = Oydid.authentication_key(didDocument)
|
|
1251
|
+
|
|
1252
|
+
expect(key).to eq("z6MkDoc")
|
|
1253
|
+
expect(msg).to eq("")
|
|
1254
|
+
end
|
|
1255
|
+
|
|
1256
|
+
it "accepts an embedded verification method" do
|
|
1257
|
+
key, = Oydid.authentication_key(
|
|
1258
|
+
{ "authentication" => [{ "publicKeyMultibase" => "z6MkEmbedded" }] })
|
|
1259
|
+
|
|
1260
|
+
expect(key).to eq("z6MkEmbedded")
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
# a DID created without --authentication has no such section; report it
|
|
1264
|
+
# instead of raising NoMethodError on nil
|
|
1265
|
+
it "reports a document without an authentication section" do
|
|
1266
|
+
key, msg = Oydid.authentication_key(didDocument.reject { |k, _| k == "authentication" })
|
|
1267
|
+
|
|
1268
|
+
expect(key).to be_nil
|
|
1269
|
+
expect(msg).to eq("no authentication key in DID document")
|
|
1270
|
+
end
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
# dcsm_verify takes the public key from the token's own kid header, so a
|
|
1274
|
+
# green result on its own says nothing about *who* signed. --expect-did is
|
|
1275
|
+
# what turns it into an authorisation check.
|
|
1276
|
+
describe "expect_did" do
|
|
1277
|
+
let(:token) { Oydid.dcsm({ "a" => 1 }, priv, { sign_did: "did:oyd:zSigner" }).first }
|
|
1278
|
+
|
|
1279
|
+
it "treats the two spellings of the location separator as one DID" do
|
|
1280
|
+
expect(Oydid.same_did?("did:oyd:zAbc%40example.com", "did:oyd:zAbc@example.com")).to be true
|
|
1281
|
+
end
|
|
1282
|
+
|
|
1283
|
+
it "ignores a key fragment" do
|
|
1284
|
+
expect(Oydid.same_did?("did:oyd:zAbc#key-doc", "did:oyd:zAbc")).to be true
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
it "separates different DIDs" do
|
|
1288
|
+
expect(Oydid.same_did?("did:oyd:zAbc", "did:oyd:zDef")).to be false
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1291
|
+
# webmock lets no unstubbed request through, so this also shows that the
|
|
1292
|
+
# mismatch is caught before the DID is resolved
|
|
1293
|
+
it "refuses a token signed by another DID without resolving it" do
|
|
1294
|
+
payload, msg = Oydid.dcsm_verify(token, { expect_did: "did:oyd:zSomeoneElse" })
|
|
1295
|
+
|
|
1296
|
+
expect(payload).to be_nil
|
|
1297
|
+
expect(msg).to eq("token was signed by did:oyd:zSigner, expected did:oyd:zSomeoneElse")
|
|
1298
|
+
end
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1158
1301
|
end
|
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.9.
|
|
4
|
+
version: 0.9.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christoph Fabianek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-04 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: simple_dag
|
|
@@ -41,16 +41,22 @@ dependencies:
|
|
|
41
41
|
name: jwt
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.2'
|
|
47
|
+
- - "<"
|
|
45
48
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
49
|
+
version: '4'
|
|
47
50
|
type: :runtime
|
|
48
51
|
prerelease: false
|
|
49
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
53
|
requirements:
|
|
51
|
-
- - "
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '3.2'
|
|
57
|
+
- - "<"
|
|
52
58
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
59
|
+
version: '4'
|
|
54
60
|
- !ruby/object:Gem::Dependency
|
|
55
61
|
name: jwt-eddsa
|
|
56
62
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -97,16 +103,22 @@ dependencies:
|
|
|
97
103
|
name: json
|
|
98
104
|
requirement: !ruby/object:Gem::Requirement
|
|
99
105
|
requirements:
|
|
100
|
-
- - "
|
|
106
|
+
- - ">="
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: 2.19.9
|
|
109
|
+
- - "<"
|
|
101
110
|
- !ruby/object:Gem::Version
|
|
102
|
-
version:
|
|
111
|
+
version: '3'
|
|
103
112
|
type: :runtime
|
|
104
113
|
prerelease: false
|
|
105
114
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
115
|
requirements:
|
|
107
|
-
- - "
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: 2.19.9
|
|
119
|
+
- - "<"
|
|
108
120
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
121
|
+
version: '3'
|
|
110
122
|
- !ruby/object:Gem::Dependency
|
|
111
123
|
name: json-ld
|
|
112
124
|
requirement: !ruby/object:Gem::Requirement
|