oydid 0.4.4 → 0.5.4

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/oydid/basic.rb +149 -33
  4. data/lib/oydid/didcomm.rb +9 -9
  5. data/lib/oydid/log.rb +14 -7
  6. data/lib/oydid/vc.rb +308 -0
  7. data/lib/oydid.rb +305 -112
  8. data/spec/input/basic/sample_b16_dec.doc +1 -0
  9. data/spec/input/basic/sample_b17_edec.doc +1 -0
  10. data/spec/input/basic/sample_b32_dec.doc +1 -0
  11. data/spec/input/basic/sample_b58_enc.doc +1 -0
  12. data/spec/input/basic/sample_b64_dec.doc +1 -0
  13. data/spec/input/basic/sample_b64_enc.doc +1 -0
  14. data/spec/input/basic/sample_blake2b-16_b16_hash.doc +1 -0
  15. data/spec/input/basic/sample_blake2b-32_b32_hash.doc +1 -0
  16. data/spec/input/basic/sample_blake2b-64_b58_hash.doc +1 -0
  17. data/spec/input/basic/sample_invalid2_readkey.doc +1 -1
  18. data/spec/input/basic/sample_invalid3_readkey.doc +1 -1
  19. data/spec/input/basic/sample_readkey.doc +1 -1
  20. data/spec/input/basic/sample_sha2-256_b58_hash.doc +1 -0
  21. data/spec/input/basic/sample_sha2-512_b58_hash.doc +1 -0
  22. data/spec/input/basic/sample_sha3-224_b64_hash.doc +1 -0
  23. data/spec/output/basic/sample_b16_dec.doc +1 -0
  24. data/spec/output/basic/sample_b16_enc.doc +1 -0
  25. data/spec/output/basic/sample_b17_edec.doc +2 -0
  26. data/spec/output/basic/sample_b17_enc.doc +1 -0
  27. data/spec/output/basic/sample_b32_dec.doc +1 -0
  28. data/spec/output/basic/sample_b32_enc.doc +1 -0
  29. data/spec/output/basic/sample_b58_dec.doc +1 -0
  30. data/spec/output/basic/sample_b64_dec.doc +1 -0
  31. data/spec/output/basic/sample_b64_enc.doc +1 -0
  32. data/spec/output/basic/sample_blake2b-16_b16_hash.doc +1 -0
  33. data/spec/output/basic/sample_blake2b-32_b32_hash.doc +1 -0
  34. data/spec/output/basic/sample_blake2b-64_b58_hash.doc +1 -0
  35. data/spec/output/basic/sample_sha2-512_b58_hash.doc +1 -0
  36. data/spec/output/basic/sample_sha3-224_b64_hash.doc +1 -0
  37. data/spec/oydid_spec.rb +95 -13
  38. metadata +72 -19
  39. /data/spec/input/basic/{sample_enc.doc → sample_b16_enc.doc} +0 -0
  40. /data/spec/input/basic/{sample_hash.doc → sample_b17_enc.doc} +0 -0
  41. /data/spec/{output/basic/sample_dec.doc → input/basic/sample_b32_enc.doc} +0 -0
  42. /data/spec/input/basic/{sample_dec.doc → sample_b58_dec.doc} +0 -0
  43. /data/spec/output/basic/{sample_enc.doc → sample_b58_enc.doc} +0 -0
  44. /data/spec/output/basic/{sample_hash.doc → sample_sha2-256_b58_hash.doc} +0 -0
data/lib/oydid/vc.rb ADDED
@@ -0,0 +1,308 @@
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[:holder_privateKey].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
+ if content["@context"].nil?
73
+ vercred["@context"] = ["https://www.w3.org/ns/credentials/v2"]
74
+ else
75
+ vercred["@context"] = content["@context"]
76
+ end
77
+ if vercred["@context"].to_s == "" || vercred["@context"].to_s == "{}" || vercred["@context"].to_s == "[]"
78
+ return [nil, "invalid '@context'"]
79
+ end
80
+ if content["type"].nil?
81
+ vercred["type"] = ["VerifiableCredential"]
82
+ else
83
+ vercred["type"] = content["type"]
84
+ end
85
+ if vercred["type"].to_s == "" || vercred["type"].to_s == "{}" || vercred["type"].to_s == "[]"
86
+ return [nil, "invalid 'type'"]
87
+ end
88
+ if content["issuer"].nil?
89
+ vercred["issuer"] = options[:issuer]
90
+ else
91
+ vercred["issuer"] = content["issuer"]
92
+ end
93
+ if vercred["issuer"].to_s == "" || vercred["issuer"].to_s == "{}" || vercred["issuer"].to_s == "[]"
94
+ return [nil, "invalid 'issuer'"]
95
+ end
96
+ if options[:ts].nil?
97
+ vercred["issuanceDate"] = Time.now.utc.iso8601
98
+ else
99
+ vercred["issuanceDate"] = Time.at(options[:ts]).utc.iso8601
100
+ end
101
+ if content["credentialSubject"].nil?
102
+ vercred["credentialSubject"] = {"id": options[:holder]}.merge(content)
103
+ else
104
+ vercred["credentialSubject"] = content["credentialSubject"]
105
+ end
106
+ if vercred["credentialSubject"].to_s == "" || vercred["credentialSubject"].to_s == "{}" || vercred["credentialSubject"].to_s == "[]"
107
+ return [nil, "invalid 'credentialSubject'"]
108
+ end
109
+ if content["proof"].nil?
110
+ proof = {}
111
+ proof["type"] = "Ed25519Signature2020"
112
+ proof["verificationMethod"] = options[:issuer].to_s
113
+ proof["proofPurpose"] = "assertionMethod"
114
+ proof["proofValue"] = sign(vercred["credentialSubject"].to_json_c14n, options[:issuer_privateKey], []).first
115
+ vercred["proof"] = proof
116
+ else
117
+ vercred["proof"] = content["proof"]
118
+ end
119
+ if vercred["proof"].to_s == "" || vercred["proof"].to_s == "{}" || vercred["proof"].to_s == "[]"
120
+ return [nil, "invalid 'proof'"]
121
+ end
122
+
123
+ # specify the identifier of the credential
124
+ vercred["identifier"] = hash(vercred.to_json)
125
+ return [vercred, ""]
126
+ end
127
+
128
+ def self.create_vc_proof(content, options)
129
+ proof = {}
130
+ proof["type"] = "Ed25519Signature2020"
131
+ proof["verificationMethod"] = options[:issuer].to_s
132
+ proof["proofPurpose"] = "assertionMethod"
133
+ proof["proofValue"] = sign(content.to_json_c14n, options[:issuer_privateKey], []).first
134
+
135
+ return [proof, ""]
136
+ end
137
+
138
+ def self.publish_vc(vc, options)
139
+ vc = vc.transform_keys(&:to_s)
140
+ identifier = vc["identifier"] rescue nil
141
+ if identifier.nil?
142
+ return [nil, "invalid format (missing identifier"]
143
+ exit
144
+ end
145
+ if vc["credentialSubject"].is_a?(Array)
146
+ cs = vc["credentialSubject"].last.transform_keys(&:to_s) rescue nil
147
+ else
148
+ cs = vc["credentialSubject"].transform_keys(&:to_s) rescue nil
149
+ end
150
+ holder = cs["id"] rescue nil
151
+ if holder.nil?
152
+ return [nil, "invalid format (missing holder)"]
153
+ exit
154
+ end
155
+
156
+ vc_location = ""
157
+ if !options[:location].nil?
158
+ vc_location = options[:location]
159
+ end
160
+ if vc_location.to_s == ""
161
+ vc_location = DEFAULT_LOCATION
162
+ end
163
+ vc["identifier"] = vc_location.sub(/(\/)+$/,'') + "/credentials/" + identifier
164
+
165
+ # build object to post
166
+ vc_data = {
167
+ "identifier": identifier,
168
+ "vc": vc,
169
+ "holder": holder
170
+ }
171
+ vc_url = vc_location.sub(/(\/)+$/,'') + "/credentials"
172
+ retVal = HTTParty.post(vc_url,
173
+ headers: { 'Content-Type' => 'application/json' },
174
+ body: vc_data.to_json )
175
+ if retVal.code != 200
176
+ err_msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vc_url.to_s
177
+ return [nil, err_msg]
178
+ end
179
+ return [vc["identifier"], ""]
180
+ end
181
+
182
+ def self.read_vp(identifier, options)
183
+ vp_location = ""
184
+ if !options[:location].nil?
185
+ vp_location = options[:location]
186
+ end
187
+ if vp_location.to_s == ""
188
+ vp_location = DEFAULT_LOCATION
189
+ end
190
+ vp_url = vp_location.sub(/(\/)+$/,'') + "/presentations/" + identifier
191
+ retVal = HTTParty.get(vp_url)
192
+ if retVal.code != 200
193
+ msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vp_url.to_s
194
+ return [nil, msg]
195
+ end
196
+ return [retVal.parsed_response, ""]
197
+ end
198
+
199
+ def self.create_vp(content, options)
200
+ verpres = {}
201
+ # set the context, which establishes the special terms used
202
+ verpres["@context"] = ["https://www.w3.org/ns/credentials/v2"]
203
+ verpres["type"] = ["VerifiablePresentation"]
204
+ verpres["verifiableCredential"] = [content].flatten
205
+
206
+ proof = {}
207
+ proof["type"] = "Ed25519Signature2020"
208
+ if options[:ts].nil?
209
+ proof["created"] = Time.now.utc.iso8601
210
+ else
211
+ proof["created"] = Time.at(options[:ts]).utc.iso8601
212
+ end
213
+ proof["verificationMethod"] = options[:holder].to_s
214
+ proof["proofPurpose"] = "authentication"
215
+
216
+ # private_key = generate_private_key(options[:issuer_privateKey], "ed25519-priv", []).first
217
+ proof["proofValue"] = sign([content].flatten.to_json_c14n, options[:holder_privateKey], []).first
218
+ verpres["proof"] = proof
219
+
220
+ # specify the identifier of the credential
221
+ verpres["identifier"] = hash(verpres.to_json)
222
+ return [verpres, ""]
223
+ end
224
+
225
+ def self.publish_vp(vp, options)
226
+ vc = vp.transform_keys(&:to_s)
227
+ identifier = vp["identifier"] rescue nil
228
+ if identifier.nil?
229
+ return [nil, "invalid format (missing identifier"]
230
+ exit
231
+ end
232
+
233
+ proof = vp["proof"].transform_keys(&:to_s) rescue nil
234
+ holder = proof["verificationMethod"] rescue nil
235
+ if holder.nil?
236
+ return [nil, "invalid format (missing holder)"]
237
+ exit
238
+ end
239
+
240
+ vp_location = ""
241
+ if !options[:location].nil?
242
+ vp_location = options[:location]
243
+ end
244
+ if vp_location.to_s == ""
245
+ vp_location = DEFAULT_LOCATION
246
+ end
247
+ vp["identifier"] = vp_location.sub(/(\/)+$/,'') + "/presentations/" + identifier
248
+
249
+ # build object to post
250
+ vp_data = {
251
+ "identifier": identifier,
252
+ "vp": vp,
253
+ "holder": holder
254
+ }
255
+ vp_url = vp_location.sub(/(\/)+$/,'') + "/presentations"
256
+ retVal = HTTParty.post(vp_url,
257
+ headers: { 'Content-Type' => 'application/json' },
258
+ body: vp_data.to_json )
259
+ if retVal.code != 200
260
+ err_msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vp_url.to_s
261
+ return [nil, err_msg]
262
+ end
263
+ return [vp["identifier"], ""]
264
+ end
265
+
266
+ def self.verify_vp(content, options)
267
+ retVal = {}
268
+ if content["identifier"].nil?
269
+ return [nil, "invalid VP (unknown identifier"]
270
+ exit
271
+ end
272
+ retVal[:identifier] = content["identifier"].to_s
273
+
274
+ proofValue = content["proof"]["proofValue"].to_s rescue nil
275
+ if proofValue.nil?
276
+ return [nil, "invalid VP (unknown proofValue"]
277
+ exit
278
+ end
279
+
280
+ vercred = content["verifiableCredential"].to_json_c14n rescue nil
281
+ if vercred.nil?
282
+ return [nil, "invalid VP (unknown verifiableCredential"]
283
+ exit
284
+ end
285
+
286
+ holder = content["proof"]["verificationMethod"].to_s rescue nil
287
+ if holder.nil?
288
+ return [nil, "invalid VP (unknown holder"]
289
+ exit
290
+ end
291
+ pubKey, msg = getPubKeyFromDID(holder)
292
+ if pubKey.nil?
293
+ return [nil, "cannot verify public key"]
294
+ exit
295
+ end
296
+ result, msg = verify(vercred, proofValue, pubKey)
297
+ if result.to_s == ""
298
+ return [nil, msg]
299
+ exit
300
+ end
301
+ if result
302
+ return [retVal, ""]
303
+ else
304
+ return [nil, "signature verification failed"]
305
+ end
306
+ end
307
+
308
+ end