oydid 0.4.4 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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