oydid 0.4.3 → 0.5.3
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 +204 -26
- data/lib/oydid/didcomm.rb +25 -6
- data/lib/oydid/log.rb +15 -7
- data/lib/oydid/vc.rb +264 -0
- data/lib/oydid.rb +286 -111
- data/spec/input/basic/sample_b16_dec.doc +1 -0
- data/spec/input/basic/{sample_enc.doc → sample_b16_enc.doc} +0 -0
- data/spec/input/basic/sample_b17_edec.doc +1 -0
- data/spec/input/basic/{sample_hash.doc → sample_b17_enc.doc} +0 -0
- data/spec/input/basic/sample_b32_dec.doc +1 -0
- data/spec/{output/basic/sample_dec.doc → input/basic/sample_b32_enc.doc} +0 -0
- data/spec/input/basic/{sample_dec.doc → sample_b58_dec.doc} +0 -0
- data/spec/input/basic/sample_b58_enc.doc +1 -0
- data/spec/input/basic/sample_b64_dec.doc +1 -0
- data/spec/input/basic/sample_b64_enc.doc +1 -0
- data/spec/input/basic/sample_blake2b-16_b16_hash.doc +1 -0
- data/spec/input/basic/sample_blake2b-32_b32_hash.doc +1 -0
- data/spec/input/basic/sample_blake2b-64_b58_hash.doc +1 -0
- data/spec/input/basic/sample_invalid2_readkey.doc +1 -1
- data/spec/input/basic/sample_invalid3_readkey.doc +1 -1
- data/spec/input/basic/sample_readkey.doc +1 -1
- data/spec/input/basic/sample_sha2-256_b58_hash.doc +1 -0
- data/spec/input/basic/sample_sha2-512_b58_hash.doc +1 -0
- data/spec/input/basic/sample_sha3-224_b64_hash.doc +1 -0
- data/spec/output/basic/sample_b16_dec.doc +1 -0
- data/spec/output/basic/sample_b16_enc.doc +1 -0
- data/spec/output/basic/sample_b17_edec.doc +2 -0
- data/spec/output/basic/sample_b17_enc.doc +1 -0
- data/spec/output/basic/sample_b32_dec.doc +1 -0
- data/spec/output/basic/sample_b32_enc.doc +1 -0
- data/spec/output/basic/sample_b58_dec.doc +1 -0
- data/spec/output/basic/{sample_enc.doc → sample_b58_enc.doc} +0 -0
- data/spec/output/basic/sample_b64_dec.doc +1 -0
- data/spec/output/basic/sample_b64_enc.doc +1 -0
- data/spec/output/basic/sample_blake2b-16_b16_hash.doc +1 -0
- data/spec/output/basic/sample_blake2b-32_b32_hash.doc +1 -0
- data/spec/output/basic/sample_blake2b-64_b58_hash.doc +1 -0
- data/spec/output/basic/{sample_hash.doc → sample_sha2-256_b58_hash.doc} +0 -0
- data/spec/output/basic/sample_sha2-512_b58_hash.doc +1 -0
- data/spec/output/basic/sample_sha3-224_b64_hash.doc +1 -0
- data/spec/oydid_spec.rb +95 -13
- metadata +72 -19
data/lib/oydid/vc.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class Oydid
|
5
|
+
def self.read_vc(identifier, options)
|
6
|
+
vc_location = ""
|
7
|
+
if !options[:location].nil?
|
8
|
+
vc_location = options[:location]
|
9
|
+
end
|
10
|
+
if vc_location.to_s == ""
|
11
|
+
vc_location = DEFAULT_LOCATION
|
12
|
+
end
|
13
|
+
vc_url = vc_location.sub(/(\/)+$/,'') + "/credentials/" + identifier
|
14
|
+
|
15
|
+
holder = options[:holder].to_s rescue nil
|
16
|
+
if holder.to_s == ""
|
17
|
+
msg = "missing holder information"
|
18
|
+
return [nil, msg]
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
private_key = options[:doc_enc].to_s rescue nil
|
23
|
+
if private_key.to_s == ""
|
24
|
+
msg = "missing private document key information"
|
25
|
+
return [nil, msg]
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
# authenticate against repository
|
30
|
+
init_url = vc_location + "/oydid/init"
|
31
|
+
sid = SecureRandom.hex(20).to_s
|
32
|
+
response = HTTParty.post(init_url,
|
33
|
+
headers: { 'Content-Type' => 'application/json' },
|
34
|
+
body: { "session_id": sid,
|
35
|
+
"public_key": Oydid.public_key(private_key, options).first }.to_json ).parsed_response rescue {}
|
36
|
+
if response["challenge"].nil?
|
37
|
+
msg = "missing challenge for repository authentication"
|
38
|
+
return [nil, msg]
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
challenge = response["challenge"].to_s
|
42
|
+
|
43
|
+
# sign challenge and request token
|
44
|
+
token_url = vc_location + "/oydid/token"
|
45
|
+
response = HTTParty.post(token_url,
|
46
|
+
headers: { 'Content-Type' => 'application/json' },
|
47
|
+
body: { "session_id": sid,
|
48
|
+
"signed_challenge": Oydid.sign(challenge, private_key, options).first }.to_json).parsed_response rescue {}
|
49
|
+
access_token = response["access_token"].to_s rescue nil
|
50
|
+
if access_token.nil?
|
51
|
+
msg = "invalid repository authentication (access_token)"
|
52
|
+
return [nil, msg]
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
|
56
|
+
retVal = HTTParty.get(vc_url,
|
57
|
+
headers: {'Authorization' => 'Bearer ' + access_token})
|
58
|
+
if retVal.code != 200
|
59
|
+
if retVal.code == 401
|
60
|
+
msg = "unauthorized (valid Bearer token required)"
|
61
|
+
else
|
62
|
+
msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vc_url.to_s
|
63
|
+
end
|
64
|
+
return [nil, msg]
|
65
|
+
end
|
66
|
+
return [retVal.parsed_response, ""]
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.create_vc(content, options)
|
70
|
+
vercred = {}
|
71
|
+
# set the context, which establishes the special terms used
|
72
|
+
vercred["@context"] = ["https://www.w3.org/2018/credentials/v1"]
|
73
|
+
vercred["type"] = ["VerifiableCredential"]
|
74
|
+
vercred["issuer"] = options[:issuer]
|
75
|
+
if options[:ts].nil?
|
76
|
+
vercred["issuanceDate"] = Time.now.utc.iso8601
|
77
|
+
else
|
78
|
+
vercred["issuanceDate"] = Time.at(options[:ts]).utc.iso8601
|
79
|
+
end
|
80
|
+
vercred["credentialSubject"] = {"id": options[:holder]}.merge(content)
|
81
|
+
|
82
|
+
proof = {}
|
83
|
+
proof["type"] = "Ed25519Signature2020"
|
84
|
+
proof["verificationMethod"] = options[:issuer].to_s
|
85
|
+
proof["proofPurpose"] = "assertionMethod"
|
86
|
+
|
87
|
+
# private_key = generate_private_key(options[:issuer_privateKey], "ed25519-priv", []).first
|
88
|
+
proof["proofValue"] = sign(content.to_json_c14n, options[:issuer_privateKey], []).first
|
89
|
+
|
90
|
+
vercred["proof"] = proof
|
91
|
+
|
92
|
+
# specify the identifier of the credential
|
93
|
+
vercred["identifier"] = hash(vercred.to_json)
|
94
|
+
return [vercred, ""]
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.publish_vc(vc, options)
|
98
|
+
vc = vc.transform_keys(&:to_s)
|
99
|
+
identifier = vc["identifier"] rescue nil
|
100
|
+
if identifier.nil?
|
101
|
+
return [nil, "invalid format (missing identifier"]
|
102
|
+
exit
|
103
|
+
end
|
104
|
+
|
105
|
+
cs = vc["credentialSubject"].transform_keys(&:to_s) rescue nil
|
106
|
+
holder = cs["id"] rescue nil
|
107
|
+
if holder.nil?
|
108
|
+
return [nil, "invalid format (missing holder)"]
|
109
|
+
exit
|
110
|
+
end
|
111
|
+
|
112
|
+
vc_location = ""
|
113
|
+
if !options[:location].nil?
|
114
|
+
vc_location = options[:location]
|
115
|
+
end
|
116
|
+
if vc_location.to_s == ""
|
117
|
+
vc_location = DEFAULT_LOCATION
|
118
|
+
end
|
119
|
+
vc["identifier"] = vc_location.sub(/(\/)+$/,'') + "/credentials/" + identifier
|
120
|
+
|
121
|
+
# build object to post
|
122
|
+
vc_data = {
|
123
|
+
"identifier": identifier,
|
124
|
+
"vc": vc,
|
125
|
+
"holder": holder
|
126
|
+
}
|
127
|
+
vc_url = vc_location.sub(/(\/)+$/,'') + "/credentials"
|
128
|
+
retVal = HTTParty.post(vc_url,
|
129
|
+
headers: { 'Content-Type' => 'application/json' },
|
130
|
+
body: vc_data.to_json )
|
131
|
+
if retVal.code != 200
|
132
|
+
err_msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vc_url.to_s
|
133
|
+
return [nil, err_msg]
|
134
|
+
end
|
135
|
+
return [vc["identifier"], ""]
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.read_vp(identifier, options)
|
139
|
+
vp_location = ""
|
140
|
+
if !options[:location].nil?
|
141
|
+
vp_location = options[:location]
|
142
|
+
end
|
143
|
+
if vp_location.to_s == ""
|
144
|
+
vp_location = DEFAULT_LOCATION
|
145
|
+
end
|
146
|
+
vp_url = vp_location.sub(/(\/)+$/,'') + "/presentations/" + identifier
|
147
|
+
retVal = HTTParty.get(vp_url)
|
148
|
+
if retVal.code != 200
|
149
|
+
msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vp_url.to_s
|
150
|
+
return [nil, msg]
|
151
|
+
end
|
152
|
+
return [retVal.parsed_response, ""]
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.create_vp(content, options)
|
156
|
+
verpres = {}
|
157
|
+
# set the context, which establishes the special terms used
|
158
|
+
verpres["@context"] = ["https://www.w3.org/2018/credentials/v1"]
|
159
|
+
verpres["type"] = ["VerifiablePresentation"]
|
160
|
+
verpres["verifiableCredential"] = [content].flatten
|
161
|
+
|
162
|
+
proof = {}
|
163
|
+
proof["type"] = "Ed25519Signature2020"
|
164
|
+
if options[:ts].nil?
|
165
|
+
proof["created"] = Time.now.utc.iso8601
|
166
|
+
else
|
167
|
+
proof["created"] = Time.at(options[:ts]).utc.iso8601
|
168
|
+
end
|
169
|
+
proof["verificationMethod"] = options[:holder].to_s
|
170
|
+
proof["proofPurpose"] = "authentication"
|
171
|
+
|
172
|
+
# private_key = generate_private_key(options[:issuer_privateKey], "ed25519-priv", []).first
|
173
|
+
proof["proofValue"] = sign([content].flatten.to_json_c14n, options[:holder_privateKey], []).first
|
174
|
+
verpres["proof"] = proof
|
175
|
+
|
176
|
+
# specify the identifier of the credential
|
177
|
+
verpres["identifier"] = hash(verpres.to_json)
|
178
|
+
return [verpres, ""]
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.publish_vp(vp, options)
|
182
|
+
vc = vp.transform_keys(&:to_s)
|
183
|
+
identifier = vp["identifier"] rescue nil
|
184
|
+
if identifier.nil?
|
185
|
+
return [nil, "invalid format (missing identifier"]
|
186
|
+
exit
|
187
|
+
end
|
188
|
+
|
189
|
+
proof = vp["proof"].transform_keys(&:to_s) rescue nil
|
190
|
+
holder = proof["verificationMethod"] rescue nil
|
191
|
+
if holder.nil?
|
192
|
+
return [nil, "invalid format (missing holder)"]
|
193
|
+
exit
|
194
|
+
end
|
195
|
+
|
196
|
+
vp_location = ""
|
197
|
+
if !options[:location].nil?
|
198
|
+
vp_location = options[:location]
|
199
|
+
end
|
200
|
+
if vp_location.to_s == ""
|
201
|
+
vp_location = DEFAULT_LOCATION
|
202
|
+
end
|
203
|
+
vp["identifier"] = vp_location.sub(/(\/)+$/,'') + "/presentations/" + identifier
|
204
|
+
|
205
|
+
# build object to post
|
206
|
+
vp_data = {
|
207
|
+
"identifier": identifier,
|
208
|
+
"vp": vp,
|
209
|
+
"holder": holder
|
210
|
+
}
|
211
|
+
vp_url = vp_location.sub(/(\/)+$/,'') + "/presentations"
|
212
|
+
retVal = HTTParty.post(vp_url,
|
213
|
+
headers: { 'Content-Type' => 'application/json' },
|
214
|
+
body: vp_data.to_json )
|
215
|
+
if retVal.code != 200
|
216
|
+
err_msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vp_url.to_s
|
217
|
+
return [nil, err_msg]
|
218
|
+
end
|
219
|
+
return [vp["identifier"], ""]
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.verify_vp(content, options)
|
223
|
+
retVal = {}
|
224
|
+
if content["identifier"].nil?
|
225
|
+
return [nil, "invalid VP (unknown identifier"]
|
226
|
+
exit
|
227
|
+
end
|
228
|
+
retVal[:identifier] = content["identifier"].to_s
|
229
|
+
|
230
|
+
proofValue = content["proof"]["proofValue"].to_s rescue nil
|
231
|
+
if proofValue.nil?
|
232
|
+
return [nil, "invalid VP (unknown proofValue"]
|
233
|
+
exit
|
234
|
+
end
|
235
|
+
|
236
|
+
vercred = content["verifiableCredential"].to_json_c14n rescue nil
|
237
|
+
if vercred.nil?
|
238
|
+
return [nil, "invalid VP (unknown verifiableCredential"]
|
239
|
+
exit
|
240
|
+
end
|
241
|
+
|
242
|
+
holder = content["proof"]["verificationMethod"].to_s rescue nil
|
243
|
+
if holder.nil?
|
244
|
+
return [nil, "invalid VP (unknown holder"]
|
245
|
+
exit
|
246
|
+
end
|
247
|
+
pubKey, msg = getPubKeyFromDID(holder)
|
248
|
+
if pubKey.nil?
|
249
|
+
return [nil, "cannot verify public key"]
|
250
|
+
exit
|
251
|
+
end
|
252
|
+
result, msg = verify(vercred, proofValue, pubKey)
|
253
|
+
if result.to_s == ""
|
254
|
+
return [nil, msg]
|
255
|
+
exit
|
256
|
+
end
|
257
|
+
if result
|
258
|
+
return [retVal, ""]
|
259
|
+
else
|
260
|
+
return [nil, "signature verification failed"]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|