oydid 0.5.4 → 0.5.6
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 +255 -12
- data/lib/oydid/didcomm.rb +2 -2
- data/lib/oydid/log.rb +194 -42
- data/lib/oydid/vc.rb +4 -2
- data/lib/oydid.rb +499 -184
- metadata +102 -102
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6e4098a11373a9b648447adbc7f6ec7b89d0efced1bb920080797dfdc5d17de
|
4
|
+
data.tar.gz: bf33e0ddd9abd26a04d92fa945db9183f87000e246f154aa509c55e9b5f4aefd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6175bb5b1d28443b26573754dd92f97094a7c139d032252c541a73dbd5019028466847b9602b380cd1ca814eeb3e7843576f7a86a77808a5268af62fe89dfba7
|
7
|
+
data.tar.gz: a719e0d4c5530340be4ad4a284ffadd46924db95a81c0212fb61e38243cac6a133bf8568ed6407a54d026502048bb06394f5e737f12e0ad588b65c43411bb046
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.6
|
data/lib/oydid/basic.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
class Hash
|
5
|
+
def except(*keys)
|
6
|
+
self.reject { |key, _| keys.include?(key) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
4
10
|
class Oydid
|
5
11
|
|
6
12
|
# basic functions ---------------------------
|
7
|
-
# %w[multibases multihashes rbnacl json].each { |f| require f }
|
13
|
+
# %w[multibases multihashes rbnacl json multicodecs].each { |f| require f }
|
8
14
|
def self.multi_encode(message, options)
|
9
15
|
method = options[:encode] || DEFAULT_ENCODING rescue DEFAULT_ENCODING
|
10
16
|
case method
|
@@ -54,7 +60,11 @@ class Oydid
|
|
54
60
|
end
|
55
61
|
|
56
62
|
def self.get_digest(message)
|
57
|
-
|
63
|
+
decoded_message, error = Oydid.multi_decode(message)
|
64
|
+
if decoded_message.nil?
|
65
|
+
return [nil, error]
|
66
|
+
end
|
67
|
+
retVal = Multihashes.decode decoded_message
|
58
68
|
if retVal[:hash_function].to_s != ""
|
59
69
|
return [retVal[:hash_function].to_s, ""]
|
60
70
|
end
|
@@ -94,7 +104,7 @@ class Oydid
|
|
94
104
|
end
|
95
105
|
|
96
106
|
# key management ----------------------------
|
97
|
-
def self.generate_private_key(input, method = "ed25519-priv", options)
|
107
|
+
def self.generate_private_key(input, method = "ed25519-priv", options = {})
|
98
108
|
begin
|
99
109
|
omc = Multicodecs[method].code
|
100
110
|
rescue
|
@@ -104,15 +114,18 @@ class Oydid
|
|
104
114
|
case Multicodecs[method].name
|
105
115
|
when 'ed25519-priv'
|
106
116
|
if input != ""
|
107
|
-
raw_key = Ed25519::SigningKey.new(RbNaCl::Hash.sha256(input))
|
117
|
+
raw_key = Ed25519::SigningKey.new(RbNaCl::Hash.sha256(input))
|
108
118
|
else
|
109
|
-
raw_key = Ed25519::SigningKey.generate
|
119
|
+
raw_key = Ed25519::SigningKey.generate
|
110
120
|
end
|
121
|
+
raw_key = raw_key.to_bytes
|
122
|
+
length = raw_key.bytesize
|
111
123
|
else
|
112
124
|
return [nil, "unsupported key codec"]
|
113
125
|
end
|
114
|
-
|
126
|
+
|
115
127
|
encoded = multi_encode([omc, length, raw_key].pack("SCa#{length}"), options)
|
128
|
+
# encoded = multi_encode(raw_key.to_bytes, options)
|
116
129
|
if encoded.first.nil?
|
117
130
|
return [nil, encoded.last]
|
118
131
|
else
|
@@ -120,7 +133,7 @@ class Oydid
|
|
120
133
|
end
|
121
134
|
end
|
122
135
|
|
123
|
-
def self.public_key(private_key, options, method = "ed25519-pub")
|
136
|
+
def self.public_key(private_key, options = {}, method = "ed25519-pub")
|
124
137
|
code, length, digest = multi_decode(private_key).first.unpack('SCa*')
|
125
138
|
case Multicodecs[code].name
|
126
139
|
when 'ed25519-priv'
|
@@ -132,6 +145,7 @@ class Oydid
|
|
132
145
|
else
|
133
146
|
return [nil, "unsupported key codec"]
|
134
147
|
end
|
148
|
+
# encoded = multi_encode(public_key.to_bytes, options)
|
135
149
|
length = public_key.to_bytes.bytesize
|
136
150
|
encoded = multi_encode([Multicodecs[method].code, length, public_key].pack("CCa#{length}"), options)
|
137
151
|
if encoded.first.nil?
|
@@ -144,6 +158,29 @@ class Oydid
|
|
144
158
|
end
|
145
159
|
end
|
146
160
|
|
161
|
+
def self.getPrivateKey(enc, pwd, dsk, dfl, options)
|
162
|
+
if enc.to_s == "" # usually read from options[:doc_enc]
|
163
|
+
if pwd.to_s == "" # usually read from options[:doc_pwd]
|
164
|
+
if dsk.to_s == "" # usually read from options[:doc_key]
|
165
|
+
if dfl.to_s == "" # default file name for key
|
166
|
+
return [nil, "no reference"]
|
167
|
+
else
|
168
|
+
privateKey, msg = read_private_key(dfl.to_s, options)
|
169
|
+
end
|
170
|
+
else
|
171
|
+
privateKey, msg = read_private_key(dsk.to_s, options)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
privateKey, msg = generate_private_key(pwd, 'ed25519-priv', options)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
privateKey, msg = decode_private_key(enc.to_s, options)
|
178
|
+
end
|
179
|
+
return [privateKey, msg]
|
180
|
+
end
|
181
|
+
|
182
|
+
# if the identifier is already the public key there is no validation if it is a valid key
|
183
|
+
# (this is a privacy-preserving feature)
|
147
184
|
def self.getPubKeyFromDID(did)
|
148
185
|
identifier = did.split(LOCATION_PREFIX).first.split(CGI.escape LOCATION_PREFIX).first rescue did
|
149
186
|
identifier = identifier.delete_prefix("did:oyd:")
|
@@ -166,6 +203,52 @@ class Oydid
|
|
166
203
|
end
|
167
204
|
end
|
168
205
|
|
206
|
+
# available key_types
|
207
|
+
# * doc - document key
|
208
|
+
# * rev - revocation key
|
209
|
+
def self.getDelegatedPubKeysFromDID(did, key_type = "doc")
|
210
|
+
# retrieve DID
|
211
|
+
did_document, msg = read(did, {})
|
212
|
+
keys, msg = getDelegatedPubKeysFromFullDidDocument(did_document, key_type)
|
213
|
+
if keys.nil?
|
214
|
+
return [nil, msg]
|
215
|
+
else
|
216
|
+
return [keys, ""]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.getDelegatedPubKeysFromFullDidDocument(did_document, key_type = "doc")
|
221
|
+
# get current public key
|
222
|
+
case key_type
|
223
|
+
when "doc"
|
224
|
+
keys = [did_document["doc"]["key"].split(":").first] rescue nil
|
225
|
+
when "rev"
|
226
|
+
keys = [did_document["doc"]["key"].split(":").last] rescue nil
|
227
|
+
else
|
228
|
+
return [nil, "invalid key type: " + key_type]
|
229
|
+
end
|
230
|
+
if keys.nil?
|
231
|
+
return [nil, "cannot retrieve current key"]
|
232
|
+
end
|
233
|
+
|
234
|
+
# travers through log and get active delegation public keys
|
235
|
+
log = did_document["log"]
|
236
|
+
log.each do |item|
|
237
|
+
if item["op"] == 5 # DELEGATE
|
238
|
+
# !!!OPEN: check if log entry is confirmed / referenced in a termination entry
|
239
|
+
item_keys = item["doc"]
|
240
|
+
if key_type == "doc" && item_keys[0..3] == "doc:"
|
241
|
+
keys << item_keys[4-item_keys.length..]
|
242
|
+
elsif key_type == "rev" && item_keys[0..3] == "rev:"
|
243
|
+
keys << item_keys[4-item_keys.length..]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end unless log.nil?
|
247
|
+
|
248
|
+
# return array
|
249
|
+
return [keys.uniq, ""]
|
250
|
+
end
|
251
|
+
|
169
252
|
def self.sign(message, private_key, options)
|
170
253
|
code, length, digest = multi_decode(private_key).first.unpack('SCa*')
|
171
254
|
case Multicodecs[code].name
|
@@ -203,7 +286,7 @@ class Oydid
|
|
203
286
|
end
|
204
287
|
end
|
205
288
|
|
206
|
-
def self.encrypt(message, public_key, options)
|
289
|
+
def self.encrypt(message, public_key, options = {})
|
207
290
|
begin
|
208
291
|
code, length, digest = multi_decode(public_key).first.unpack('CCa*')
|
209
292
|
case Multicodecs[code].name
|
@@ -229,7 +312,7 @@ class Oydid
|
|
229
312
|
end
|
230
313
|
end
|
231
314
|
|
232
|
-
def self.decrypt(message, private_key, options)
|
315
|
+
def self.decrypt(message, private_key, options = {})
|
233
316
|
begin
|
234
317
|
cipher = [JSON.parse(message)["value"]].pack('H*')
|
235
318
|
nonce = [JSON.parse(message)["nonce"]].pack('H*')
|
@@ -250,10 +333,160 @@ class Oydid
|
|
250
333
|
end
|
251
334
|
end
|
252
335
|
|
336
|
+
# key="812b578d2e357270cbbd26a9dd44f93e5c8a3b44462e271348ce8f742dfe08144d06fd1f64d5a15c5b21564695d0dca9d65af322e8f96ef394400fe255d288cf"
|
337
|
+
def jweh(key)
|
338
|
+
pub_key=key[-64..-1]
|
339
|
+
prv_key=key[0..-65]
|
340
|
+
|
341
|
+
hex_pub=pub_key
|
342
|
+
bin_pub=[hex_pub].pack('H*')
|
343
|
+
int_pub=RbNaCl::PublicKey.new(bin_pub)
|
344
|
+
len_pub=int_pub.to_bytes.bytesize
|
345
|
+
enc_pub=Oydid.multi_encode([Multicodecs["x25519-pub"].code,len_pub,int_pub].pack("CCa#{len_pub}"),{}).first
|
346
|
+
|
347
|
+
hex_prv=prv_key
|
348
|
+
bin_prv=[hex_prv].pack('H*')
|
349
|
+
int_prv=RbNaCl::PrivateKey.new(bin_prv)
|
350
|
+
len_prv=int_prv.to_bytes.bytesize
|
351
|
+
enc_prv=Oydid.multi_encode([Multicodecs["ed25519-priv"].code,len_prv,int_prv].pack("SCa#{len_prv}"),{}).first
|
352
|
+
return [enc_pub, enc_prv]
|
353
|
+
end
|
354
|
+
|
355
|
+
# public_key=jweh(key).first
|
356
|
+
# require 'oydid'
|
357
|
+
# message="hallo"
|
358
|
+
# public_key="z6Mv4uEoFYJ369NoE9xUzxG5sm8KpPnvHX6YH6GsYFGSQ32J"
|
359
|
+
# jwe=Oydid.encryptJWE(message, public_key).first
|
360
|
+
def self.encryptJWE(message, public_key, options = {})
|
361
|
+
|
362
|
+
jwe_header = {"enc":"XC20P"}
|
363
|
+
recipient_alg = 'ECDH-ES+XC20PKW'
|
364
|
+
|
365
|
+
# Content Encryption ---
|
366
|
+
# random nonce for XChaCha20-Poly1305: uses a 192-bit nonce (24 bytes)
|
367
|
+
cnt_nnc = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.nonce_bytes)
|
368
|
+
# random key for XChaCha20-Poly1305: uses a 256-bit key (32 bytes)
|
369
|
+
cnt_key = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.key_bytes)
|
370
|
+
# addtional data
|
371
|
+
cnt_aad = jwe_header.to_json
|
372
|
+
# setup XChaCha20-Poly1305 for Authenticated Encryption with Associated Data (AEAD)
|
373
|
+
cnt_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(cnt_key)
|
374
|
+
# encrypt
|
375
|
+
msg_enc = cnt_aead.encrypt(cnt_nnc, message, cnt_aad)
|
376
|
+
cnt_enc = msg_enc[0...-cnt_aead.tag_bytes]
|
377
|
+
cnt_tag = msg_enc[-cnt_aead.tag_bytes .. -1]
|
378
|
+
|
379
|
+
# Key Encryption ---
|
380
|
+
snd_prv = RbNaCl::PrivateKey.generate
|
381
|
+
code, length, digest = Oydid.multi_decode(public_key).first.unpack('CCa*')
|
382
|
+
buffer = RbNaCl::Util.zeros(RbNaCl::Boxes::Curve25519XSalsa20Poly1305::PublicKey::BYTES)
|
383
|
+
RbNaCl::Signatures::Ed25519::VerifyKey.crypto_sign_ed25519_pk_to_curve25519(buffer, digest)
|
384
|
+
shared_secret = RbNaCl::GroupElement.new(buffer).mult(snd_prv.to_bytes)
|
385
|
+
jwe_const = [0, 0, 0, 1] +
|
386
|
+
shared_secret.to_bytes.unpack('C*') +
|
387
|
+
[0,0,0,15] +
|
388
|
+
recipient_alg.bytes +
|
389
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
|
390
|
+
kek = RbNaCl::Hash.sha256(jwe_const.pack('C*'))
|
391
|
+
snd_nnc = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.nonce_bytes)
|
392
|
+
snd_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(kek)
|
393
|
+
snd_enc = snd_aead.encrypt(snd_nnc, cnt_key, nil)
|
394
|
+
snd_key = snd_enc[0...-snd_aead.tag_bytes]
|
395
|
+
snd_aut = snd_enc[-snd_aead.tag_bytes .. -1]
|
396
|
+
|
397
|
+
# create JWE ---
|
398
|
+
jwe_protected = Base64.urlsafe_encode64(jwe_header.to_json).delete("=")
|
399
|
+
jwe_encrypted_key = Base64.urlsafe_encode64(snd_key).delete("=")
|
400
|
+
jwe_init_vector = Base64.urlsafe_encode64(cnt_nnc).delete("=")
|
401
|
+
jwe_cipher_text = Base64.urlsafe_encode64(cnt_enc).delete("=")
|
402
|
+
jwe_auth_tag = Base64.urlsafe_encode64(cnt_tag).delete("=")
|
403
|
+
rcp_nnc_enc = Base64.urlsafe_encode64(snd_nnc).delete("=")
|
404
|
+
rcp_tag_enc = Base64.urlsafe_encode64(snd_aut).delete("=")
|
405
|
+
|
406
|
+
jwe_full = {
|
407
|
+
protected: jwe_protected,
|
408
|
+
iv: jwe_init_vector,
|
409
|
+
ciphertext: jwe_cipher_text,
|
410
|
+
tag: jwe_auth_tag,
|
411
|
+
recipients: [
|
412
|
+
{
|
413
|
+
encrypted_key: jwe_encrypted_key,
|
414
|
+
header: {
|
415
|
+
alg: recipient_alg,
|
416
|
+
iv: rcp_nnc_enc,
|
417
|
+
tag: rcp_tag_enc,
|
418
|
+
epk: {
|
419
|
+
kty: "OKP",
|
420
|
+
crv: "X25519",
|
421
|
+
x: Base64.urlsafe_encode64(snd_prv.public_key.to_bytes).delete("=")
|
422
|
+
}
|
423
|
+
}
|
424
|
+
}
|
425
|
+
]
|
426
|
+
}
|
427
|
+
|
428
|
+
jwe = jwe_protected
|
429
|
+
jwe += "." + jwe_encrypted_key
|
430
|
+
jwe += "." + jwe_init_vector
|
431
|
+
jwe += "." + jwe_cipher_text
|
432
|
+
jwe += "." + jwe_auth_tag
|
433
|
+
|
434
|
+
return [jwe_full, ""]
|
435
|
+
end
|
436
|
+
|
437
|
+
# require 'oydid'
|
438
|
+
# message = '{"protected":"eyJlbmMiOiJYQzIwUCJ9","iv":"G24pK06HSbL0vTlRZMIEBLfa074tJ1tq","ciphertext":"N5bgUr8","tag":"CJeq8cuaercgoBZmrYoGUA","recipients":[{"encrypted_key":"chR8HQh1CRYRU4TdlfBbvon4fcb5PKfWPSo0SgkMC_8","header":{"alg":"ECDH-ES+XC20PKW","iv":"K7lUo8shyJhxC7Nl45VlXes4tbDeZyBL","tag":"2sLCGRv70ESqEAqos3ZhSg","epk":{"kty":"OKP","crv":"X25519","x":"ZpnKcI7Kac6HPwVAGwM0PBweTFKM6wHHVljTHMRWpD4"}}}]}'
|
439
|
+
# message = jwe.to_json
|
440
|
+
# private_key = "z1S5USmDosHvi2giCLHCCgcq3Cd31mrhMcUy2fcfszxxLkD7"
|
441
|
+
# Oydid.decryptJWE(jwe.to_json, private_key)
|
442
|
+
def self.decryptJWE(message, private_key, options = {})
|
443
|
+
|
444
|
+
# JWE parsing
|
445
|
+
jwe_full = JSON.parse(message)
|
446
|
+
snd_pub_enc = jwe_full["recipients"].first["header"]["epk"]["x"]
|
447
|
+
snd_key_enc = jwe_full["recipients"].first["encrypted_key"]
|
448
|
+
snd_nnc_enc = jwe_full["recipients"].first["header"]["iv"]
|
449
|
+
snd_tag_enc = jwe_full["recipients"].first["header"]["tag"]
|
450
|
+
cnt_cip_enc = jwe_full["ciphertext"]
|
451
|
+
cnt_tag_enc = jwe_full["tag"]
|
452
|
+
cnt_nnc_enc = jwe_full["iv"]
|
453
|
+
cnt_aad_enc = jwe_full["protected"]
|
454
|
+
recipient_alg = jwe_full["recipients"].first["header"]["alg"]
|
455
|
+
|
456
|
+
snd_pub = Base64.urlsafe_decode64(snd_pub_enc)
|
457
|
+
snd_nnc = Base64.urlsafe_decode64(snd_nnc_enc)
|
458
|
+
snd_key = Base64.urlsafe_decode64(snd_key_enc)
|
459
|
+
snd_tag = Base64.urlsafe_decode64(snd_tag_enc)
|
460
|
+
cnt_nnc = Base64.urlsafe_decode64(cnt_nnc_enc)
|
461
|
+
cnt_cip = Base64.urlsafe_decode64(cnt_cip_enc)
|
462
|
+
cnt_tag = Base64.urlsafe_decode64(cnt_tag_enc)
|
463
|
+
cnt_aad = Base64.urlsafe_decode64(cnt_aad_enc)
|
464
|
+
|
465
|
+
# Key Decryption
|
466
|
+
code, length, digest = Oydid.multi_decode(private_key).first.unpack('SCa*')
|
467
|
+
buffer = RbNaCl::Util.zeros(RbNaCl::Boxes::Curve25519XSalsa20Poly1305::PublicKey::BYTES)
|
468
|
+
RbNaCl::Signatures::Ed25519::SigningKey.crypto_sign_ed25519_sk_to_curve25519(buffer, digest)
|
469
|
+
shared_secret = RbNaCl::GroupElement.new(snd_pub).mult(buffer)
|
470
|
+
jwe_const = [0, 0, 0, 1] +
|
471
|
+
shared_secret.to_bytes.unpack('C*') +
|
472
|
+
[0,0,0,15] +
|
473
|
+
recipient_alg.bytes +
|
474
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
|
475
|
+
kek = RbNaCl::Hash.sha256(jwe_const.pack('C*'))
|
476
|
+
snd_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(kek)
|
477
|
+
cnt_key = snd_aead.decrypt(snd_nnc, snd_key+snd_tag, nil)
|
478
|
+
|
479
|
+
# Content Decryption
|
480
|
+
cnt_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(cnt_key)
|
481
|
+
cnt_dec = cnt_aead.decrypt(cnt_nnc, cnt_cip+cnt_tag, cnt_aad)
|
482
|
+
|
483
|
+
return [cnt_dec, ""]
|
484
|
+
end
|
485
|
+
|
253
486
|
def self.read_private_key(filename, options)
|
254
487
|
begin
|
255
488
|
f = File.open(filename)
|
256
|
-
key_encoded = f.read
|
489
|
+
key_encoded = f.read.strip
|
257
490
|
f.close
|
258
491
|
rescue
|
259
492
|
return [nil, "cannot read file"]
|
@@ -284,6 +517,9 @@ class Oydid
|
|
284
517
|
when 'ed25519-pub'
|
285
518
|
verify_key = Ed25519::VerifyKey.new(digest)
|
286
519
|
return [verify_key, ""]
|
520
|
+
when 'x25519-pub'
|
521
|
+
pub_key = RbNaCl::PublicKey.new(digest)
|
522
|
+
return [pub_key, ""]
|
287
523
|
else
|
288
524
|
return [nil, "unsupported key codec"]
|
289
525
|
end
|
@@ -331,9 +567,16 @@ class Oydid
|
|
331
567
|
case doc_location
|
332
568
|
when /^http/
|
333
569
|
doc_location = doc_location.sub("%3A%2F%2F","://").sub("%3A", ":")
|
334
|
-
|
570
|
+
option_str = ""
|
571
|
+
if options[:followAlsoKnownAs]
|
572
|
+
option_str = "?followAlsoKnownAs=true"
|
573
|
+
end
|
574
|
+
retVal = HTTParty.get(doc_location + "/doc/" + doc_identifier + option_str)
|
335
575
|
if retVal.code != 200
|
336
|
-
msg = retVal.parsed_response
|
576
|
+
msg = retVal.parsed_response["error"].to_s rescue ""
|
577
|
+
if msg.to_s == ""
|
578
|
+
msg = "invalid response from " + doc_location.to_s + "/doc/" + doc_identifier.to_s
|
579
|
+
end
|
337
580
|
return [nil, msg]
|
338
581
|
end
|
339
582
|
if options.transform_keys(&:to_s)["trace"]
|
data/lib/oydid/didcomm.rb
CHANGED
@@ -102,12 +102,12 @@ class Oydid
|
|
102
102
|
# DID Auth for data container with challenge ---
|
103
103
|
def self.token_from_challenge(host, pwd, options = {})
|
104
104
|
sid = SecureRandom.hex(20).to_s
|
105
|
+
public_key = public_key(generate_private_key(pwd, options).first, options).first
|
105
106
|
retVal = HTTParty.post(host + "/oydid/init",
|
106
107
|
headers: { 'Content-Type' => 'application/json' },
|
107
|
-
body: { "session_id": sid }.to_json )
|
108
|
+
body: { "session_id": sid, "public_key": public_key }.to_json )
|
108
109
|
challenge = retVal.parsed_response["challenge"]
|
109
110
|
signed_challenge = sign(challenge, Oydid.generate_private_key(pwd, options).first, options).first
|
110
|
-
public_key = public_key(generate_private_key(pwd, options).first, options).first
|
111
111
|
retVal = HTTParty.post(host + "/oydid/token",
|
112
112
|
headers: { 'Content-Type' => 'application/json' },
|
113
113
|
body: {
|