oydid 0.4.4 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/oydid/basic.rb +149 -33
- data/lib/oydid/didcomm.rb +9 -9
- data/lib/oydid/log.rb +14 -7
- data/lib/oydid/vc.rb +264 -0
- data/lib/oydid.rb +283 -113
- 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
|