oydid 0.5.6 → 0.6.1

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.
data/lib/oydid/didcomm.rb CHANGED
@@ -63,6 +63,29 @@ class Oydid
63
63
  when 'ed25519-priv'
64
64
  private_key = RbNaCl::Signatures::Ed25519::SigningKey.new(digest)
65
65
  token = JWT.encode payload, private_key, 'ED25519'
66
+ when 'p256-priv'
67
+ group = OpenSSL::PKey::EC::Group.new('prime256v1')
68
+ pub_key = group.generator.mul(OpenSSL::BN.new(digest, 2))
69
+ pub_oct = pub_key.to_bn.to_s(2)
70
+
71
+ parameters = OpenSSL::ASN1::ObjectId("prime256v1")
72
+ parameters.tag = 0
73
+ parameters.tagging = :EXPLICIT
74
+ parameters.tag_class = :CONTEXT_SPECIFIC
75
+
76
+ public_key_bitstring = OpenSSL::ASN1::BitString(pub_oct)
77
+ public_key_bitstring.tag = 1
78
+ public_key_bitstring.tagging = :EXPLICIT
79
+ public_key_bitstring.tag_class = :CONTEXT_SPECIFIC
80
+
81
+ ec_private_key_asn1 = OpenSSL::ASN1::Sequence([
82
+ OpenSSL::ASN1::Integer(1),
83
+ OpenSSL::ASN1::OctetString(digest),
84
+ parameters,
85
+ public_key_bitstring
86
+ ])
87
+ ec_key = OpenSSL::PKey.read(ec_private_key_asn1.to_der)
88
+ token = JWT.encode(payload, ec_key, 'ES256')
66
89
  else
67
90
  token = nil
68
91
  error = "unsupported key codec"
@@ -117,4 +140,39 @@ class Oydid
117
140
  }.to_json)
118
141
  return retVal.parsed_response["access_token"]
119
142
  end
143
+
144
+ # other helpers -----------------------------
145
+ def self.build_jwks(content, input_did, options)
146
+
147
+ tmp_did_hash = input_did.delete_prefix("did:oyd:") rescue ""
148
+ tmp_did10 = tmp_did_hash[0,10] + "_private_key.enc" rescue ""
149
+ privateKey, msg = getPrivateKey(options[:doc_enc], options[:doc_pwd], options[:doc_key], tmp_did10, options)
150
+ if privateKey.nil?
151
+ return [nil, "private document key not available: " + msg.to_s]
152
+ end
153
+
154
+ code, length, digest = multi_decode(privateKey).first.unpack('SCa*')
155
+ case Multicodecs[code].name
156
+ when 'ed25519-priv'
157
+ signing_key = Ed25519::SigningKey.new(digest)
158
+ else
159
+ return [nil, "unsupported key codec: " + Multicodecs[code].name.to_s]
160
+ end
161
+
162
+ jwk = content
163
+ jwk['iss'] = input_did
164
+ jwk['sub'] = input_did
165
+ jwk['iat'] = Time.now.to_i
166
+ jwk['exp'] = Time.now.to_i + 120
167
+ jwk['jti'] = SecureRandom.uuid
168
+
169
+ algorithm = 'EdDSA'
170
+ headers = {
171
+ alg: algorithm,
172
+ typ: 'JWT',
173
+ kid: input_did + '#key-doc' }
174
+
175
+ jwks = JWT.encode(jwk, signing_key, algorithm, headers)
176
+ return [jwks, nil]
177
+ end
120
178
  end
data/lib/oydid/log.rb CHANGED
@@ -289,10 +289,13 @@ class Oydid
289
289
  end
290
290
  doc = doc.first["doc"]
291
291
  if el["op"] == 2 # CREATE
292
- if !match_log_did?(el, doc)
293
- currentDID["error"] = 1
294
- currentDID["message"] = "Signatures in log don't match"
295
- return currentDID
292
+ # signature for CREATE is optional (due to CMSM)
293
+ if !el["sig"].nil?
294
+ if !match_log_did?(el, doc)
295
+ currentDID["error"] = 1
296
+ currentDID["message"] = "Signatures in log don't match"
297
+ return currentDID
298
+ end
296
299
  end
297
300
  end
298
301
  currentDID["did"] = doc_did
@@ -331,13 +334,20 @@ class Oydid
331
334
  did_hash = did_hash.split(LOCATION_PREFIX).first.split(CGI.escape LOCATION_PREFIX).first
332
335
  did10 = did_hash[0,10]
333
336
  doc = retrieve_document_raw(doc_did, did10 + ".doc", doc_location, {})
337
+ doc = doc.first["doc"]
338
+
339
+ if !Oydid.match_log_did?(el, doc)
340
+ currentDID["error"] = 1
341
+ currentDID["message"] = "Signatures in log don't match"
342
+ return currentDID
343
+ end
344
+
334
345
  # since it retrieves a DID that previously existed, this test is not necessary
335
346
  # if doc.first.nil?
336
347
  # currentDID["error"] = 2
337
348
  # currentDID["message"] = doc.last.to_s
338
349
  # return currentDID
339
350
  # end
340
- doc = doc.first["doc"]
341
351
  term = doc["log"]
342
352
  log_location = term.split(LOCATION_PREFIX).last.split(CGI.escape LOCATION_PREFIX).last rescue ""
343
353
  if log_location.to_s == "" || log_location == term
@@ -512,7 +522,7 @@ class Oydid
512
522
  did_orig = "did:oyd:" + did_orig
513
523
  end
514
524
  case rotate_DID_method
515
- when "did:ebsi"
525
+ when "did:ebsi", "did:cheqd"
516
526
  public_resolver = DEFAULT_PUBLIC_RESOLVER
517
527
  rotate_DID_Document = HTTParty.get(public_resolver + rotate_DID)
518
528
  rotate_ddoc = JSON.parse(rotate_DID_Document.parsed_response)
data/lib/oydid/vc.rb CHANGED
@@ -65,11 +65,102 @@ class Oydid
65
65
  return [retVal.parsed_response, ""]
66
66
  end
67
67
 
68
+ def self.vc_proof_prep(vc, proof)
69
+ cntxt = vc["@context"].dup
70
+ if !cntxt.is_a?(Array)
71
+ cntxt = [cntxt]
72
+ end
73
+ cntxt << ED25519_SECURITY_SUITE unless cntxt.include?(ED25519_SECURITY_SUITE)
74
+ vc["@context"] = cntxt.dup
75
+ vc.delete("proof")
76
+ vc = JSON::LD::API.compact(JSON.parse(vc.to_json), JSON.parse(cntxt.to_json))
77
+ graph = RDF::Graph.new << JSON::LD::Reader.new(vc.to_json)
78
+ norm_graph = graph.dump(:normalize).to_s
79
+ if norm_graph.strip == ""
80
+ return [nil, nil, "empty VC"]
81
+ end
82
+ hash1 = Multibases.pack("base16", RbNaCl::Hash.sha256(norm_graph)).to_s[1..]
83
+
84
+ remove_context = false
85
+ if proof["@context"].nil?
86
+ proof["@context"] = cntxt.dup
87
+ remove_context = true
88
+ else
89
+ cntxt = proof["@context"]
90
+ end
91
+ if proof["created"].nil?
92
+ proof["created"] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
93
+ end
94
+ proof.delete("proofValue")
95
+ proof = JSON::LD::API.compact(JSON.parse(proof.to_json), JSON.parse(cntxt.to_json))
96
+ graph = RDF::Graph.new << JSON::LD::Reader.new(proof.to_json)
97
+ norm_graph = graph.dump(:normalize).to_s
98
+ if norm_graph.strip == ""
99
+ return [nil, nil, "empty proof"]
100
+ end
101
+ hash2 = Multibases.pack("base16", RbNaCl::Hash.sha256(norm_graph)).to_s[1..]
102
+ if remove_context
103
+ proof.delete("@context")
104
+ end
105
+ vc["proof"] = proof
106
+
107
+ return [vc, hash2+hash1, nil]
108
+ end
109
+
110
+ # Verifiable Credential hash
111
+ # vc = {"@context", "type", "issuer", "issuanceDate", "credentialSubject"}
112
+ # but no "proof"!
113
+ # proof = {"type", "verificationMethod", "proofPurpose", "created"}
114
+ # but no "proofValue"
115
+ # private_key_encoded (string) "z..."
116
+ # https://www.w3.org/TR/vc-di-eddsa/#representation-ed25519signature2020
117
+ def self.vc_proof(vc, proof, private_key_encoded, options)
118
+ vc, vc_hash, errmsg = vc_proof_prep(vc, proof)
119
+ if vc.nil?
120
+ return [nil, errmsg]
121
+ end
122
+ code, length, digest = multi_decode(private_key_encoded).first.unpack('SCa*')
123
+ case Multicodecs[code].name
124
+ when 'ed25519-priv'
125
+ signing_key = Ed25519::SigningKey.new(digest)
126
+ vc["proof"]["proofValue"] = multi_encode(signing_key.sign([vc_hash].pack('H*')).bytes, options).first
127
+ when 'p256-priv'
128
+ vc["proof"]["proofValue"] = sign("message", private_key_encoded, options)
129
+ else
130
+ return [nil, "unsupported key codec"]
131
+ end
132
+ return [vc, nil]
133
+
134
+ end
135
+
68
136
  def self.create_vc(content, options)
69
- vercred = {}
137
+ if options[:issuer_privateKey].to_s == ""
138
+ return [nil, "missing issuer private key"]
139
+ end
140
+ code, length, digest = multi_decode(options[:issuer_privateKey]).first.unpack('SCa*')
141
+ case options[:vc_type].to_s
142
+ when 'Ed25519Signature2020'
143
+ if Multicodecs[code].name != 'ed25519-priv'
144
+ return [nil, "combination of credential type '" + options[:vc_type].to_s + "' and key type '" + Multicodecs[code].name.to_s + "' not supported"]
145
+ end
146
+ when 'JsonWebSignature2020'
147
+ if Multicodecs[code].name != 'p256-priv'
148
+ return [nil, "combination of credential type '" + options[:vc_type].to_s + "' and key type '" + Multicodecs[code].name.to_s + "' not supported"]
149
+ end
150
+ else
151
+ return [nil, "unsupported credential type '" + options[:vc_type].to_s + "'"]
152
+ end
153
+ vercred = content
70
154
  # set the context, which establishes the special terms used
71
155
  if content["@context"].nil?
72
- vercred["@context"] = ["https://www.w3.org/ns/credentials/v2"]
156
+ case options[:vc_type].to_s
157
+ when "Ed25519Signature2020"
158
+ vercred["@context"] = ["https://www.w3.org/ns/credentials/v2"]
159
+ when "JsonWebSignature2020"
160
+ vercred["@context"] = ["https://www.w3.org/2018/credentials/v1"]
161
+ else
162
+ return [nil, "invalid credential type '" + options[:vc_type].to_s + "'"]
163
+ end
73
164
  else
74
165
  vercred["@context"] = content["@context"]
75
166
  end
@@ -93,7 +184,7 @@ class Oydid
93
184
  return [nil, "invalid 'issuer'"]
94
185
  end
95
186
  if options[:ts].nil?
96
- vercred["issuanceDate"] = Time.now.utc.iso8601
187
+ vercred["issuanceDate"] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
97
188
  else
98
189
  vercred["issuanceDate"] = Time.at(options[:ts]).utc.iso8601
99
190
  end
@@ -101,47 +192,117 @@ class Oydid
101
192
  vercred["credentialSubject"] = {"id": options[:holder]}.merge(content)
102
193
  else
103
194
  vercred["credentialSubject"] = content["credentialSubject"]
195
+ if vercred["credentialSubject"]["id"].nil?
196
+ if options[:holder].nil?
197
+ return [nil, "missing 'id' (of holder) in 'credentialSubject'"]
198
+ end
199
+ vercred["credentialSubject"]["id"] = options[:holder]
200
+ end
104
201
  end
105
202
  if vercred["credentialSubject"].to_s == "" || vercred["credentialSubject"].to_s == "{}" || vercred["credentialSubject"].to_s == "[]"
106
203
  return [nil, "invalid 'credentialSubject'"]
107
204
  end
108
- if content["proof"].nil?
109
- proof = {}
110
- proof["type"] = "Ed25519Signature2020"
111
- proof["verificationMethod"] = options[:issuer].to_s
112
- proof["proofPurpose"] = "assertionMethod"
113
- proof["proofValue"] = sign(vercred["credentialSubject"].transform_keys(&:to_s).to_json_c14n, options[:issuer_privateKey], []).first
114
- vercred["proof"] = proof
205
+
206
+ case options[:vc_type].to_s
207
+ when 'Ed25519Signature2020'
208
+ if content["proof"].nil?
209
+ proof = {}
210
+ proof["type"] = "Ed25519Signature2020"
211
+ proof["verificationMethod"] = options[:issuer].to_s + "#key-doc"
212
+ proof["proofPurpose"] = "assertionMethod"
213
+ id_vc = vercred.dup
214
+ id_vc["proof"] = proof
215
+ identifier_str = multi_hash(canonical(id_vc), options).first
216
+ if options[:vc_location].nil?
217
+ vercred["identifier"] = identifier_str
218
+ else
219
+ vc_location = options[:vc_location].to_s
220
+ if !vc_location.start_with?("http")
221
+ vc_location = "https://" + token_url
222
+ end
223
+ if !vc_location.end_with?('/')
224
+ vc_location += '/'
225
+ end
226
+ if !vc_location.end_with?('credentials/')
227
+ vc_location += 'credentials/'
228
+ end
229
+ vercred["id"] = vc_location + identifier_str
230
+ end
231
+
232
+ vercred, errmsg = vc_proof(vercred, proof, options[:issuer_privateKey], options)
233
+ if vercred.nil?
234
+ return [nil, errmsg]
235
+ end
236
+ # proof["proofValue"] = sign(vercred["credentialSubject"].transform_keys(&:to_s).to_json_c14n, options[:issuer_privateKey], []).first
237
+ else
238
+ id_vc = vercred.dup
239
+ content["proof"].delete("proofValue")
240
+ id_vc["proof"] = content["proof"]
241
+ identifier_str = multi_hash(canonical(id_vc), options).first
242
+ if options[:vc_location].nil?
243
+ vercred["identifier"] = identifier_str
244
+ else
245
+ vercred["id"] = options[:vc_location].to_s + identifier_str
246
+ end
247
+
248
+ vercred, errmsg = vc_proof(vercred, content["proof"], options[:issuer_privateKey], options)
249
+ if vercred.nil?
250
+ return [nil, errmsg]
251
+ end
252
+ end
253
+ if vercred["proof"].to_s == "" || vercred["proof"].to_s == "{}" || vercred["proof"].to_s == "[]"
254
+ return [nil, "invalid 'proof'"]
255
+ end
256
+
257
+ when 'JsonWebSignature2020'
258
+ jwt_vc = {}
259
+ jwt_vc["vc"] = vercred.dup
260
+ jwt_vc["exp"] = (Time.now + (3 * 30 * 24 * 60 * 60)).to_i
261
+ jwt_vc["iss"] = vercred["issuer"]
262
+ jwt_vc["nbf"] =
263
+ if options[:ts].nil?
264
+ jwt_vc["nbf"] = Time.now.utc.to_i
265
+ else
266
+ jwt_vc["nbf"] = options[:ts].to_i
267
+ end
268
+ identifier_str = multi_hash(canonical(vercred), options).first
269
+ jwt_vc["jti"] = identifier_str
270
+ jwt_vc["sub"] = options[:holder]
271
+
272
+ vercred = jwt_vc.dup
115
273
  else
116
- vercred["proof"] = content["proof"]
117
- end
118
- if vercred["proof"].to_s == "" || vercred["proof"].to_s == "{}" || vercred["proof"].to_s == "[]"
119
- return [nil, "invalid 'proof'"]
274
+ return [nil, "unsupported credential type '" + options[:vc_type].to_s + "'"]
120
275
  end
121
276
 
122
- # specify the identifier of the credential
123
- vercred["identifier"] = hash(vercred.to_json)
124
277
  return [vercred, ""]
125
278
  end
126
279
 
127
280
  def self.create_vc_proof(content, options)
128
281
  if content["id"].nil?
129
- content["id"] = options[:holder]
282
+ content["id"] = options[:issuer]
130
283
  end
131
284
  proof = {}
132
285
  proof["type"] = "Ed25519Signature2020"
133
286
  proof["verificationMethod"] = options[:issuer].to_s
134
287
  proof["proofPurpose"] = "assertionMethod"
135
- proof["proofValue"] = sign(content.to_json_c14n, options[:issuer_privateKey], []).first
136
288
 
137
- return [proof, ""]
289
+ content, errmsg = vc_proof(content, proof, options[:issuer_privateKey], options)
290
+ if content.nil?
291
+ return [nil, errmsg]
292
+ end
293
+ # proof["proofValue"] = sign(content.to_json_c14n, options[:issuer_privateKey], []).first
294
+
295
+ return [content["proof"], ""]
138
296
  end
139
297
 
140
298
  def self.publish_vc(vc, options)
141
299
  vc = vc.transform_keys(&:to_s)
142
300
  identifier = vc["identifier"] rescue nil
143
301
  if identifier.nil?
144
- return [nil, "invalid format (missing identifier"]
302
+ identifier = vc["id"] rescue nil
303
+ end
304
+ if identifier.nil?
305
+ return [nil, "invalid format (missing identifier)"]
145
306
  exit
146
307
  end
147
308
  if vc["credentialSubject"].is_a?(Array)
@@ -162,7 +323,9 @@ class Oydid
162
323
  if vc_location.to_s == ""
163
324
  vc_location = DEFAULT_LOCATION
164
325
  end
165
- vc["identifier"] = vc_location.sub(/(\/)+$/,'') + "/credentials/" + identifier
326
+ if !identifier.start_with?('http')
327
+ identifier = vc_location.sub(/(\/)+$/,'') + "/credentials/" + identifier
328
+ end
166
329
 
167
330
  # build object to post
168
331
  vc_data = {
@@ -178,7 +341,7 @@ class Oydid
178
341
  err_msg = retVal.parsed_response("error").to_s rescue "invalid response from " + vc_url.to_s
179
342
  return [nil, err_msg]
180
343
  end
181
- return [vc["identifier"], ""]
344
+ return [retVal["identifier"], ""]
182
345
  end
183
346
 
184
347
  def self.read_vp(identifier, options)
@@ -201,39 +364,68 @@ class Oydid
201
364
  def self.create_vp(content, options)
202
365
  verpres = {}
203
366
  # set the context, which establishes the special terms used
204
- verpres["@context"] = ["https://www.w3.org/ns/credentials/v2"]
367
+ if !content["@context"].nil?
368
+ verpres["@context"] = content["@context"].dup
369
+ else
370
+ verpres["@context"] = ["https://www.w3.org/ns/credentials/v2", ED25519_SECURITY_SUITE]
371
+ end
205
372
  verpres["type"] = ["VerifiablePresentation"]
206
373
  verpres["verifiableCredential"] = [content].flatten
207
374
 
208
375
  proof = {}
209
- proof["type"] = "Ed25519Signature2020"
210
- if options[:ts].nil?
211
- proof["created"] = Time.now.utc.iso8601
376
+ case options[:vc_type].to_s
377
+ when 'Ed25519Signature2020'
378
+ proof['type'] = 'Ed25519Signature2020'
379
+ if !options[:ts].nil?
380
+ proof["created"] = Time.at(options[:ts]).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
381
+ end
382
+ proof["verificationMethod"] = options[:holder].to_s
383
+ proof["proofPurpose"] = "authentication"
384
+ verpres, errmsg = vc_proof(verpres, proof, options[:holder_privateKey], options)
385
+ when 'JsonWebSignature2020'
386
+ verpres["holder"] = options[:holder].to_s
387
+ proof['type'] = 'JsonWebSignature2020'
388
+ if options[:ts].nil?
389
+ proof['created'] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
390
+ else
391
+ proof["created"] = Time.at(options[:ts]).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
392
+ end
393
+ proof["proofPurpose"] = "authentication"
394
+ proof["verificationMethod"] = options[:holder].to_s + '#key-doc'
395
+
396
+ verpres["proof"] = proof
397
+
398
+ options[:issuer] = options[:holder]
399
+ options[:issuer_privateKey] = options[:holder_privateKey]
400
+ jwt, msg = Oydid.jwt_from_vc(verpres, options)
401
+ parts = jwt.split('.')
402
+ detached_jws = "#{parts[0]}..#{parts[2]}"
403
+
404
+ proof['jws'] = detached_jws
405
+ verpres["proof"] = proof
212
406
  else
213
- proof["created"] = Time.at(options[:ts]).utc.iso8601
407
+ return [nil, "unsupported credential type '" + options[:vc_type].to_s + "'"]
214
408
  end
215
- proof["verificationMethod"] = options[:holder].to_s
216
- proof["proofPurpose"] = "authentication"
217
409
 
218
410
  # private_key = generate_private_key(options[:issuer_privateKey], "ed25519-priv", []).first
219
- proof["proofValue"] = sign([content].flatten.to_json_c14n, options[:holder_privateKey], []).first
220
- verpres["proof"] = proof
411
+ # proof["proofValue"] = sign([content].flatten.to_json_c14n, options[:holder_privateKey], []).first
412
+ # verpres["proof"] = proof
221
413
 
222
414
  # specify the identifier of the credential
223
- verpres["identifier"] = hash(verpres.to_json)
415
+ verpres["identifier"] = hash(canonical(verpres.to_json))
224
416
  return [verpres, ""]
225
417
  end
226
418
 
227
419
  def self.publish_vp(vp, options)
228
- vc = vp.transform_keys(&:to_s)
420
+ vp = vp.transform_keys(&:to_s)
229
421
  identifier = vp["identifier"] rescue nil
230
422
  if identifier.nil?
231
- return [nil, "invalid format (missing identifier"]
423
+ return [nil, "invalid format (missing identifier)"]
232
424
  exit
233
425
  end
234
426
 
235
427
  proof = vp["proof"].transform_keys(&:to_s) rescue nil
236
- holder = proof["verificationMethod"] rescue nil
428
+ holder = vp["holder"] rescue nil
237
429
  if holder.nil?
238
430
  return [nil, "invalid format (missing holder)"]
239
431
  exit
@@ -265,46 +457,160 @@ class Oydid
265
457
  return [vp["identifier"], ""]
266
458
  end
267
459
 
268
- def self.verify_vp(content, options)
460
+ def self.verify_vc(content, options)
269
461
  retVal = {}
270
- if content["identifier"].nil?
271
- return [nil, "invalid VP (unknown identifier"]
272
- exit
462
+ vercred = content.to_json_c14n rescue nil
463
+ if vercred.nil?
464
+ return [nil, "invalid verifiableCredential input"]
273
465
  end
274
- retVal[:identifier] = content["identifier"].to_s
275
-
276
- proofValue = content["proof"]["proofValue"].to_s rescue nil
277
- if proofValue.nil?
278
- return [nil, "invalid VP (unknown proofValue"]
466
+ retVal[:id] = content["id"] rescue nil
467
+ if retVal[:id].nil?
468
+ retVal[:id] = content["identifier"] rescue nil
469
+ if retVal[:id].nil?
470
+ return [nil, "invalid VC (missing id)"]
471
+ end
472
+ end
473
+ issuer = content["issuer"].to_s rescue nil
474
+ if issuer.nil?
475
+ return [nil, "invalid VC (unknown issuer)"]
279
476
  exit
280
477
  end
281
-
282
- vercred = content["verifiableCredential"].to_json_c14n rescue nil
283
- if vercred.nil?
284
- return [nil, "invalid VP (unknown verifiableCredential"]
478
+ publicKey, msg = getPubKeyFromDID(issuer)
479
+ if publicKey.nil?
480
+ return [nil, "cannot verify public key"]
285
481
  exit
286
482
  end
483
+ vc, vc_hash, errmsg = vc_proof_prep(JSON.parse(content.to_json), JSON.parse(content["proof"].to_json))
484
+ begin
485
+ pubkey = Oydid.multi_decode(publicKey).first
486
+ code = pubkey.bytes.first
487
+ digest = pubkey[-32..]
488
+ case Multicodecs[code].name
489
+ when 'ed25519-pub'
490
+ verify_key = Ed25519::VerifyKey.new(digest)
491
+ signature_verification = false
492
+ begin
493
+ verify_key.verify(multi_decode(content["proof"]["proofValue"]).first, [vc_hash].pack('H*'))
494
+ signature_verification = true
495
+ rescue Ed25519::VerifyError
496
+ signature_verification = false
497
+ end
498
+ if signature_verification
499
+ return [retVal, nil]
500
+ else
501
+ return [nil, "proof signature does not match VC"]
502
+ end
503
+ else
504
+ return [nil, "unsupported key codec"]
505
+ end
506
+ rescue
507
+ return [nil, "unknown key codec"]
508
+ end
509
+ end
287
510
 
511
+ def self.verify_vp(content, options)
512
+ retVal = {}
513
+ verpres = content.to_json_c14n rescue nil
514
+ if verpres.nil?
515
+ return [nil, "invalid verifiablePresetation input"]
516
+ end
517
+ retVal[:id] = content["id"] rescue nil
518
+ if retVal[:id].nil?
519
+ retVal[:id] = content["identifier"] rescue nil
520
+ if retVal[:id].nil?
521
+ return [nil, "invalid VP (missing id)"]
522
+ end
523
+ end
288
524
  holder = content["proof"]["verificationMethod"].to_s rescue nil
289
525
  if holder.nil?
290
526
  return [nil, "invalid VP (unknown holder"]
291
- exit
292
527
  end
293
- pubKey, msg = getPubKeyFromDID(holder)
294
- if pubKey.nil?
528
+ publicKey, msg = getPubKeyFromDID(holder)
529
+ if publicKey.nil?
295
530
  return [nil, "cannot verify public key"]
296
- exit
297
- end
298
- result, msg = verify(vercred, proofValue, pubKey)
299
- if result.to_s == ""
300
- return [nil, msg]
301
- exit
302
531
  end
303
- if result
304
- return [retVal, ""]
305
- else
306
- return [nil, "signature verification failed"]
532
+ # begin
533
+ key_type = get_keytype(publicKey)
534
+ case key_type
535
+ # pubkey = Oydid.multi_decode(publicKey).first
536
+ # code = pubkey.bytes.first
537
+ # digest = pubkey[-32..]
538
+ # case Multicodecs[code].name
539
+ when 'ed25519-pub'
540
+ pubkey = Oydid.multi_decode(publicKey).first
541
+ code = pubkey.bytes.first
542
+ digest = pubkey[-32..]
543
+ verify_key = Ed25519::VerifyKey.new(digest)
544
+ vp, vp_hash, errmsg = vc_proof_prep(JSON.parse(content.to_json), JSON.parse(content["proof"].to_json))
545
+
546
+ signature_verification = false
547
+ begin
548
+ verify_key.verify(multi_decode(content["proof"]["proofValue"]).first, [vp_hash].pack('H*'))
549
+ signature_verification = true
550
+ rescue Ed25519::VerifyError
551
+ signature_verification = false
552
+ end
553
+ if signature_verification
554
+ return [retVal, nil]
555
+ else
556
+ return [nil, "proof signature does not match VP"]
557
+ end
558
+ when 'p256-pub'
559
+ jws = content["proof"]["jws"]
560
+ head_b64, _, sig_b64 = jws.split('.')
561
+ verpres = JSON.parse(verpres)
562
+ verpres["proof"].delete("jws")
563
+ verpres.delete("identifier")
564
+ encoded_payload = Base64.urlsafe_encode64(verpres.to_json_c14n, padding: false)
565
+ data_to_sign = "#{head_b64}.#{encoded_payload}"
566
+ # puts 'data_to_sign: ' + data_to_sign.to_s
567
+ # puts 'encoded_signature: ' + sig_b64.to_s
568
+ # puts 'publicKey: ' + publicKey.to_s
569
+
570
+ valid = verify(data_to_sign, sig_b64, publicKey).first
571
+ if valid
572
+ return [retVal, nil]
573
+ else
574
+ return [nil, "proof signature does not match VP"]
575
+ end
576
+ else
577
+ return [nil, "unsupported key codec"]
578
+ end
579
+ # rescue
580
+ # return [nil, "unknown key codec"]
581
+ # end
582
+ end
583
+
584
+ def self.jwt_from_vc(vc, options)
585
+ if options[:issuer].to_s == ''
586
+ return [nil, 'missing issuer DID']
587
+ end
588
+ header = {
589
+ alg: 'ES256',
590
+ typ: 'JWT',
591
+ kid: options[:issuer] + '#key-doc'
592
+ }
593
+
594
+ if options[:issuer_privateKey].to_s == ''
595
+ return [nil, 'missing issuer private key']
307
596
  end
597
+ private_key = decode_private_key(options[:issuer_privateKey]).first
598
+
599
+ encoded_header = Base64.urlsafe_encode64(header.to_json, padding: false)
600
+ encoded_payload = Base64.urlsafe_encode64(vc.to_json_c14n, padding: false)
601
+ data_to_sign = "#{encoded_header}.#{encoded_payload}"
602
+ # puts 'data_to_sign: ' + data_to_sign.to_s
603
+ # puts 'privateKey: ' + options[:issuer_privateKey].to_s
604
+
605
+ jwt_digest = OpenSSL::Digest::SHA256.new
606
+ asn1_signature = OpenSSL::ASN1.decode(private_key.dsa_sign_asn1(jwt_digest.digest(data_to_sign)))
607
+ raw_signature = asn1_signature.value.map { |i| i.value.to_s(2).rjust(32, "\x00") }.join()
608
+ encoded_signature = Base64.urlsafe_encode64(raw_signature, padding: false)
609
+ # puts 'encoded_signature: ' + encoded_signature.to_s
610
+
611
+ jwt = "#{encoded_header}.#{encoded_payload}.#{encoded_signature}"
612
+
613
+ return [jwt, nil]
308
614
  end
309
615
 
310
616
  end