ppldid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +1 -0
  3. data/LICENSE +201 -0
  4. data/README.md +2 -0
  5. data/VERSION +1 -0
  6. data/lib/oydid/basic.rb +278 -0
  7. data/lib/oydid/didcomm.rb +120 -0
  8. data/lib/oydid/log.rb +394 -0
  9. data/lib/oydid.rb +738 -0
  10. data/spec/input/basic/arrays.json +8 -0
  11. data/spec/input/basic/french.json +6 -0
  12. data/spec/input/basic/sample2_get_location.doc +1 -0
  13. data/spec/input/basic/sample2_retrieve_document.doc +1 -0
  14. data/spec/input/basic/sample3_retrieve_document.doc +1 -0
  15. data/spec/input/basic/sample4_retrieve_document.doc +1 -0
  16. data/spec/input/basic/sample5_retrieve_document.doc +1 -0
  17. data/spec/input/basic/sample_dec.doc +1 -0
  18. data/spec/input/basic/sample_enc.doc +1 -0
  19. data/spec/input/basic/sample_get_location.doc +1 -0
  20. data/spec/input/basic/sample_hash.doc +1 -0
  21. data/spec/input/basic/sample_invalid2_readkey.doc +1 -0
  22. data/spec/input/basic/sample_invalid2_verify.doc +1 -0
  23. data/spec/input/basic/sample_invalid3_readkey.doc +1 -0
  24. data/spec/input/basic/sample_invalid3_verify.doc +1 -0
  25. data/spec/input/basic/sample_invalid_privkey.doc +1 -0
  26. data/spec/input/basic/sample_invalid_readkey.doc +1 -0
  27. data/spec/input/basic/sample_invalid_sign.doc +1 -0
  28. data/spec/input/basic/sample_invalid_verify.doc +1 -0
  29. data/spec/input/basic/sample_key.doc +1 -0
  30. data/spec/input/basic/sample_readkey.doc +1 -0
  31. data/spec/input/basic/sample_retrieve_document.doc +1 -0
  32. data/spec/input/basic/sample_sign.doc +1 -0
  33. data/spec/input/basic/sample_valid_privkey.doc +1 -0
  34. data/spec/input/basic/sample_verify.doc +1 -0
  35. data/spec/input/basic/structures.json +8 -0
  36. data/spec/input/basic/unicode.json +3 -0
  37. data/spec/input/basic/values.json +5 -0
  38. data/spec/input/basic/wierd.json +11 -0
  39. data/spec/input/basic/zQmaBZTghn.doc +1 -0
  40. data/spec/input/log/sample0_dag2array.doc +1 -0
  41. data/spec/input/log/sample0_dag_did.doc +1 -0
  42. data/spec/input/log/sample1_dag_did.doc +1 -0
  43. data/spec/input/log/sample1_dag_update.doc +1 -0
  44. data/spec/input/log/sample2_dag_did.doc +1 -0
  45. data/spec/input/log/sample2_dag_update.doc +1 -0
  46. data/spec/input/log/sample2_retrieve_log.doc +1 -0
  47. data/spec/input/log/sample3_dag_did.doc +1 -0
  48. data/spec/input/log/sample3_dag_update.doc +1 -0
  49. data/spec/input/log/sample3_retrieve_log.doc +1 -0
  50. data/spec/input/log/sample4_dag_did.doc +1 -0
  51. data/spec/input/log/sample4_dag_update.doc +1 -0
  52. data/spec/input/log/sample4_retrieve_log.doc +1 -0
  53. data/spec/input/log/sample5_dag_update.doc +1 -0
  54. data/spec/input/log/sample5_retrieve_log.doc +1 -0
  55. data/spec/input/log/sample6_dag_update.doc +1 -0
  56. data/spec/input/log/sample6_retrieve_log.doc +1 -0
  57. data/spec/input/log/sample7_dag_update.doc +1 -0
  58. data/spec/input/log/sample7_retrieve_log.doc +1 -0
  59. data/spec/input/log/sample8_dag_update.doc +1 -0
  60. data/spec/input/log/sample_addhash.doc +1 -0
  61. data/spec/input/log/sample_dag_update.doc +1 -0
  62. data/spec/input/log/sample_match_log.doc +1 -0
  63. data/spec/input/log/sample_op1_addhash.doc +1 -0
  64. data/spec/input/log/sample_retrieve_log.doc +1 -0
  65. data/spec/input/main/sample0_read.doc +1 -0
  66. data/spec/output/basic/arrays.json +1 -0
  67. data/spec/output/basic/french.json +1 -0
  68. data/spec/output/basic/sample2_get_location.doc +1 -0
  69. data/spec/output/basic/sample2_retrieve_document.doc +1 -0
  70. data/spec/output/basic/sample3_retrieve_document.doc +1 -0
  71. data/spec/output/basic/sample4_retrieve_document.doc +1 -0
  72. data/spec/output/basic/sample5_retrieve_document.doc +1 -0
  73. data/spec/output/basic/sample_dec.doc +1 -0
  74. data/spec/output/basic/sample_enc.doc +1 -0
  75. data/spec/output/basic/sample_get_location.doc +1 -0
  76. data/spec/output/basic/sample_hash.doc +1 -0
  77. data/spec/output/basic/sample_invalid2_readkey.doc +1 -0
  78. data/spec/output/basic/sample_invalid2_verify.doc +1 -0
  79. data/spec/output/basic/sample_invalid3_readkey.doc +1 -0
  80. data/spec/output/basic/sample_invalid3_verify.doc +1 -0
  81. data/spec/output/basic/sample_invalid_privkey.doc +1 -0
  82. data/spec/output/basic/sample_invalid_readkey.doc +1 -0
  83. data/spec/output/basic/sample_invalid_sign.doc +1 -0
  84. data/spec/output/basic/sample_invalid_verify.doc +1 -0
  85. data/spec/output/basic/sample_key.doc +1 -0
  86. data/spec/output/basic/sample_readkey.doc +1 -0
  87. data/spec/output/basic/sample_retrieve_document.doc +1 -0
  88. data/spec/output/basic/sample_sign.doc +1 -0
  89. data/spec/output/basic/sample_valid_privkey.doc +1 -0
  90. data/spec/output/basic/sample_verify.doc +1 -0
  91. data/spec/output/basic/structures.json +1 -0
  92. data/spec/output/basic/unicode.json +1 -0
  93. data/spec/output/basic/values.json +1 -0
  94. data/spec/output/basic/wierd.json +1 -0
  95. data/spec/output/log/sample0_dag2array.doc +1 -0
  96. data/spec/output/log/sample0_dag_did.doc +1 -0
  97. data/spec/output/log/sample1_dag_did.doc +1 -0
  98. data/spec/output/log/sample1_dag_update.doc +1 -0
  99. data/spec/output/log/sample2_dag_did.doc +1 -0
  100. data/spec/output/log/sample2_dag_update.doc +1 -0
  101. data/spec/output/log/sample2_retrieve_log.doc +1 -0
  102. data/spec/output/log/sample3_dag_did.doc +1 -0
  103. data/spec/output/log/sample3_dag_update.doc +1 -0
  104. data/spec/output/log/sample3_retrieve_log.doc +1 -0
  105. data/spec/output/log/sample4_dag_did.doc +1 -0
  106. data/spec/output/log/sample4_dag_update.doc +1 -0
  107. data/spec/output/log/sample4_retrieve_log.doc +1 -0
  108. data/spec/output/log/sample5_dag_update.doc +1 -0
  109. data/spec/output/log/sample5_retrieve_log.doc +1 -0
  110. data/spec/output/log/sample6_dag_update.doc +1 -0
  111. data/spec/output/log/sample6_retrieve_log.doc +1 -0
  112. data/spec/output/log/sample7_dag_update.doc +1 -0
  113. data/spec/output/log/sample7_retrieve_log.doc +1 -0
  114. data/spec/output/log/sample8_dag_update.doc +1 -0
  115. data/spec/output/log/sample_addhash.doc +1 -0
  116. data/spec/output/log/sample_dag_update.doc +1 -0
  117. data/spec/output/log/sample_match_log.doc +1 -0
  118. data/spec/output/log/sample_op1_addhash.doc +1 -0
  119. data/spec/output/log/sample_retrieve_log.doc +1 -0
  120. data/spec/output/main/sample0_read.doc +1 -0
  121. data/spec/oydid_spec.rb +170 -0
  122. data/spec/spec_helper.rb +31 -0
  123. metadata +420 -0
data/lib/oydid/log.rb ADDED
@@ -0,0 +1,394 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ class Oydid
5
+ # log functions -----------------------------
6
+ def self.add_hash(log)
7
+ log.map do |item|
8
+ i = item.dup
9
+ i.delete("previous")
10
+ item["entry-hash"] = hash(canonical(item))
11
+ if item.transform_keys(&:to_s)["op"] == 1
12
+ item["sub-entry-hash"] = hash(canonical(i))
13
+ end
14
+ item
15
+ end
16
+ end
17
+
18
+ # check if signature matches current document
19
+ # check if signature in log is correct
20
+ def self.match_log_did?(log, doc)
21
+ message = log["doc"].to_s
22
+ signature = log["sig"].to_s
23
+ public_keys = doc["key"].to_s
24
+ public_key = public_keys.split(":")[0] rescue ""
25
+ return verify(message, signature, public_key).first
26
+ end
27
+
28
+ def self.retrieve_log(did_hash, log_file, log_location, options)
29
+ if log_location == ""
30
+ log_location = DEFAULT_LOCATION
31
+ end
32
+ if !(log_location == "" || log_location == "local")
33
+ if !log_location.start_with?("http")
34
+ log_location = "https://" + log_location
35
+ end
36
+ end
37
+
38
+ case log_location
39
+ when /^http/
40
+ log_location = log_location.sub("%3A%2F%2F","://")
41
+ retVal = HTTParty.get(log_location + "/log/" + did_hash)
42
+ if retVal.code != 200
43
+ msg = retVal.parsed_response("error").to_s rescue
44
+ "invalid response from " + log_location.to_s + "/log/" + did_hash.to_s
45
+
46
+ return [nil, msg]
47
+ end
48
+ if options.transform_keys(&:to_s)["trace"]
49
+ if options[:silent].nil? || !options[:silent]
50
+ puts "GET log for " + did_hash + " from " + log_location
51
+ end
52
+ end
53
+ retVal = JSON.parse(retVal.to_s) rescue nil
54
+ return [retVal, ""]
55
+ when "", "local"
56
+ doc = JSON.parse(read_private_storage(log_file)) rescue {}
57
+ if doc == {}
58
+ return [nil, "cannot read file '" + log_file + "'"]
59
+ end
60
+ return [doc, ""]
61
+ end
62
+ end
63
+
64
+ def self.dag_did(logs, options)
65
+ dag = DAG.new
66
+ dag_log = []
67
+ log_hash = []
68
+
69
+ # calculate hash values for each entry and build vertices
70
+ i = 0
71
+ create_entries = 0
72
+ create_index = nil
73
+ terminate_indices = []
74
+ logs.each do |el|
75
+ if el["op"].to_i == 2
76
+ create_entries += 1
77
+ create_index = i
78
+ end
79
+ if el["op"].to_i == 0
80
+ terminate_indices << i
81
+ end
82
+ log_hash << Oydid.hash(Oydid.canonical(el))
83
+ dag_log << dag.add_vertex(id: i)
84
+ i += 1
85
+ end unless logs.nil?
86
+
87
+ if create_entries != 1
88
+ return [nil, nil, nil, "wrong number of CREATE entries (" + create_entries.to_s + ") in log" ]
89
+ end
90
+ if terminate_indices.length == 0
91
+ return [nil, nil, nil, "missing TERMINATE entries" ]
92
+ end
93
+
94
+ # create edges between vertices
95
+ i = 0
96
+ logs.each do |el|
97
+ el["previous"].each do |p|
98
+ position = log_hash.find_index(p)
99
+ if !position.nil?
100
+ dag.add_edge from: dag_log[position], to: dag_log[i]
101
+ end
102
+ end unless el["previous"] == []
103
+ i += 1
104
+ end unless logs.nil?
105
+
106
+ # identify tangling TERMINATE entry
107
+ i = 0
108
+ terminate_entries = 0
109
+ terminate_overall = 0
110
+ terminate_index = nil
111
+ logs.each do |el|
112
+ if el["op"].to_i == 0
113
+ if dag.vertices[i].successors.length == 0
114
+ terminate_entries += 1
115
+ terminate_index = i
116
+ end
117
+ terminate_overall += 1
118
+ end
119
+ i += 1
120
+ end unless logs.nil?
121
+
122
+ if terminate_entries != 1 && !options[:log_complete]
123
+ if options[:silent].nil? || !options[:silent]
124
+ return [nil, nil, nil, "cannot resolve DID" ]
125
+ end
126
+ end
127
+ return [dag, create_index, terminate_index, ""]
128
+ end
129
+
130
+ def self.dag2array(dag, log_array, index, result, options)
131
+ if options.transform_keys(&:to_s)["trace"]
132
+ if options[:silent].nil? || !options[:silent]
133
+ puts " vertex " + index.to_s + " at " + log_array[index]["ts"].to_s + " op: " + log_array[index]["op"].to_s + " doc: " + log_array[index]["doc"].to_s
134
+ end
135
+ end
136
+ result << log_array[index]
137
+ dag.vertices[index].successors.each do |s|
138
+ # check if successor has predecessor that is not self (i.e. REVOKE with TERMINATE)
139
+ s.predecessors.each do |p|
140
+ if p[:id] != index
141
+ if options.transform_keys(&:to_s)["trace"]
142
+ if options[:silent].nil? || !options[:silent]
143
+ puts " vertex " + p[:id].to_s + " at " + log_array[p[:id]]["ts"].to_s + " op: " + log_array[p[:id]]["op"].to_s + " doc: " + log_array[p[:id]]["doc"].to_s
144
+ end
145
+ end
146
+ result << log_array[p[:id]]
147
+ end
148
+ end unless s.predecessors.length < 2
149
+ dag2array(dag, log_array, s[:id], result, options)
150
+ end unless dag.vertices[index].successors.count == 0
151
+ result
152
+ end
153
+
154
+ def self.dag_update(currentDID, options)
155
+ i = 0
156
+ doc_location = ""
157
+ initial_did = currentDID["did"].to_s.dup
158
+ initial_did = initial_did.delete_prefix("did:ppld:")
159
+ if initial_did.include?(LOCATION_PREFIX)
160
+ tmp = initial_did.split(LOCATION_PREFIX)
161
+ initial_did = tmp[0]
162
+ doc_location = tmp[1]
163
+ end
164
+ current_public_doc_key = ""
165
+ verification_output = false
166
+ currentDID["log"].each do |el|
167
+ case el["op"]
168
+ when 2,3 # CREATE, UPDATE
169
+ currentDID["doc_log_id"] = i
170
+ doc_did = el["doc"]
171
+ did_hash = doc_did.delete_prefix("did:ppld:")
172
+ did_hash = did_hash.split(LOCATION_PREFIX).first
173
+ did10 = did_hash[0,10]
174
+ doc = retrieve_document_raw(doc_did, did10 + ".doc", doc_location, {})
175
+ if doc.first.nil?
176
+ currentDID["error"] = 2
177
+ msg = doc.last.to_s
178
+ if msg == ""
179
+ msg = "cannot retrieve " + doc_did.to_s
180
+ end
181
+ currentDID["message"] = msg
182
+ return currentDID
183
+ end
184
+ doc = doc.first["doc"]
185
+ if el["op"] == 2 # CREATE
186
+ if !match_log_did?(el, doc)
187
+ currentDID["error"] = 1
188
+ currentDID["message"] = "Signatures in log don't match"
189
+ return currentDID
190
+ end
191
+ end
192
+ currentDID["did"] = doc_did
193
+ currentDID["doc"] = doc
194
+ # since hash is guaranteed during retrieve_document this check is not necessary
195
+ # if hash(canonical(doc)) != did_hash
196
+ # currentDID["error"] = 1
197
+ # currentDID["message"] = "DID identifier and DID document don't match"
198
+ # if did_hash == initial_did
199
+ # verification_output = true
200
+ # end
201
+ # if verification_output
202
+ # currentDID["verification"] += "identifier: " + did_hash.to_s + "\n"
203
+ # currentDID["verification"] += "⛔ does not match DID Document:" + "\n"
204
+ # currentDID["verification"] += JSON.pretty_generate(doc) + "\n"
205
+ # currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
206
+ # end
207
+ # return currentDID
208
+ # end
209
+ if did_hash == initial_did
210
+ verification_output = true
211
+ end
212
+ if verification_output
213
+ currentDID["verification"] += "identifier: " + did_hash.to_s + "\n"
214
+ currentDID["verification"] += "✅ is hash of DID Document:" + "\n"
215
+ currentDID["verification"] += JSON.pretty_generate(doc) + "\n"
216
+ currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
217
+ end
218
+ current_public_doc_key = currentDID["doc"]["key"].split(":").first rescue ""
219
+
220
+ when 0 # TERMINATE
221
+ currentDID["termination_log_id"] = i
222
+
223
+ doc_did = currentDID["did"]
224
+ did_hash = doc_did.delete_prefix("did:ppld:")
225
+ did_hash = did_hash.split(LOCATION_PREFIX).first
226
+ did10 = did_hash[0,10]
227
+ doc = retrieve_document_raw(doc_did, did10 + ".doc", doc_location, {})
228
+ # since it retrieves a DID that previously existed, this test is not necessary
229
+ # if doc.first.nil?
230
+ # currentDID["error"] = 2
231
+ # currentDID["message"] = doc.last.to_s
232
+ # return currentDID
233
+ # end
234
+ doc = doc.first["doc"]
235
+ term = doc["log"]
236
+ log_location = term.split(LOCATION_PREFIX)[1] rescue ""
237
+ if log_location.to_s == ""
238
+ log_location = DEFAULT_LOCATION
239
+ end
240
+ term = term.split(LOCATION_PREFIX).first
241
+ if hash(canonical(el)) != term
242
+ currentDID["error"] = 1
243
+ currentDID["message"] = "Log reference and record don't match"
244
+ if verification_output
245
+ currentDID["verification"] += "'log' reference in DID Document: " + term.to_s + "\n"
246
+ currentDID["verification"] += "⛔ does not match TERMINATE log record:" + "\n"
247
+ currentDID["verification"] += JSON.pretty_generate(el) + "\n"
248
+ currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
249
+ end
250
+ return currentDID
251
+ end
252
+ if verification_output
253
+ currentDID["verification"] += "'log' reference in DID Document: " + term.to_s + "\n"
254
+ currentDID["verification"] += "✅ is hash of TERMINATE log record:" + "\n"
255
+ currentDID["verification"] += JSON.pretty_generate(el) + "\n"
256
+ currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
257
+ end
258
+
259
+ # check if there is a revocation entry
260
+ revocation_record = {}
261
+ revoc_term = el["doc"]
262
+ revoc_term = revoc_term.split(LOCATION_PREFIX).first
263
+ revoc_term_found = false
264
+ log_array, msg = retrieve_log(did_hash, did10 + ".log", log_location, options)
265
+ log_array.each do |log_el|
266
+ log_el_structure = log_el.dup
267
+ if log_el["op"].to_i == 1 # TERMINATE
268
+ log_el_structure.delete("previous")
269
+ end
270
+ if hash(canonical(log_el_structure)) == revoc_term
271
+ revoc_term_found = true
272
+ revocation_record = log_el.dup
273
+ if verification_output
274
+ currentDID["verification"] += "'doc' reference in TERMINATE log record: " + revoc_term.to_s + "\n"
275
+ currentDID["verification"] += "✅ is hash of REVOCATION log record (without 'previous' attribute):" + "\n"
276
+ currentDID["verification"] += JSON.pretty_generate(log_el) + "\n"
277
+ currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
278
+ end
279
+ break
280
+ end
281
+ end unless log_array.nil?
282
+ # this should actually be covered by retrieve_log in the block above
283
+ # (actually I wasn't able to craft a test case covering this part...)
284
+ # if !options.transform_keys(&:to_s)["log_location"].nil?
285
+ # log_array, msg = retrieve_log(revoc_term, did10 + ".log", options.transform_keys(&:to_s)["log_location"], options)
286
+ # log_array.each do |log_el|
287
+ # if log_el["op"] == 1 # TERMINATE
288
+ # log_el_structure = log_el.delete("previous")
289
+ # else
290
+ # log_el_structure = log_el
291
+ # end
292
+ # if hash(canonical(log_el_structure)) == revoc_term
293
+ # revoc_term_found = true
294
+ # revocation_record = log_el.dup
295
+ # if verification_output
296
+ # currentDID["verification"] += "'doc' reference in TERMINATE log record: " + revoc_term.to_s + "\n"
297
+ # currentDID["verification"] += "✅ is hash of REVOCATION log record (without 'previous' attribute):" + "\n"
298
+ # currentDID["verification"] += JSON.pretty_generate(log_el) + "\n"
299
+ # currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#calculate_hash)" + "\n\n"
300
+ # end
301
+ # break
302
+ # end
303
+ # end
304
+ # end
305
+
306
+ if revoc_term_found
307
+ update_term_found = false
308
+ log_array.each do |log_el|
309
+ if log_el["op"].to_i == 3
310
+ if log_el["previous"].include?(hash(canonical(revocation_record)))
311
+ update_term_found = true
312
+ message = log_el["doc"].to_s
313
+
314
+ signature = log_el["sig"]
315
+ public_key = current_public_doc_key.to_s
316
+ signature_verification = verify(message, signature, public_key).first
317
+ if signature_verification
318
+ if verification_output
319
+ currentDID["verification"] += "found UPDATE log record:" + "\n"
320
+ currentDID["verification"] += JSON.pretty_generate(log_el) + "\n"
321
+ currentDID["verification"] += "✅ public key from last DID Document: " + current_public_doc_key.to_s + "\n"
322
+ currentDID["verification"] += "verifies 'doc' reference of new DID Document: " + log_el["doc"].to_s + "\n"
323
+ currentDID["verification"] += log_el["sig"].to_s + "\n"
324
+ currentDID["verification"] += "of next DID Document (Details: https://ownyourdata.github.io/ppldid/#verify_signature)" + "\n"
325
+
326
+ next_doc_did = log_el["doc"].to_s
327
+ next_doc_location = doc_location
328
+ next_did_hash = next_doc_did.delete_prefix("did:ppld:")
329
+ next_did_hash = next_did_hash.split(LOCATION_PREFIX).first
330
+ next_did10 = next_did_hash[0,10]
331
+ next_doc = retrieve_document_raw(next_doc_did, next_did10 + ".doc", next_doc_location, {})
332
+ if next_doc.first.nil?
333
+ currentDID["error"] = 2
334
+ currentDID["message"] = next_doc.last
335
+ return currentDID
336
+ end
337
+ next_doc = next_doc.first["doc"]
338
+ if public_key == next_doc["key"].split(":").first
339
+ currentDID["verification"] += "⚠️ no key rotation in updated DID Document" + "\n"
340
+ end
341
+ currentDID["verification"] += "\n"
342
+ end
343
+ else
344
+ currentDID["error"] = 1
345
+ currentDID["message"] = "Signature does not match"
346
+ if verification_output
347
+ new_doc_did = log_el["doc"].to_s
348
+ new_doc_location = doc_location
349
+ new_did_hash = new_doc_did.delete_prefix("did:ppld:")
350
+ new_did_hash = new_did_hash.split(LOCATION_PREFIX).first
351
+ new_did10 = new_did_hash[0,10]
352
+ new_doc = retrieve_document(new_doc_did, new_did10 + ".doc", new_doc_location, {}).first
353
+ currentDID["verification"] += "found UPDATE log record:" + "\n"
354
+ currentDID["verification"] += JSON.pretty_generate(log_el) + "\n"
355
+ currentDID["verification"] += "⛔ public key from last DID Document: " + current_public_doc_key.to_s + "\n"
356
+ currentDID["verification"] += "does not verify 'doc' reference of new DID Document: " + log_el["doc"].to_s + "\n"
357
+ currentDID["verification"] += log_el["sig"].to_s + "\n"
358
+ currentDID["verification"] += "next DID Document (Details: https://ownyourdata.github.io/ppldid/#verify_signature)" + "\n"
359
+ currentDID["verification"] += JSON.pretty_generate(new_doc) + "\n\n"
360
+ end
361
+ return currentDID
362
+ end
363
+ break
364
+ end
365
+ end
366
+ end
367
+
368
+ else
369
+ if verification_output
370
+ currentDID["verification"] += "Revocation reference in log record: " + revoc_term.to_s + "\n"
371
+ currentDID["verification"] += "✅ cannot find revocation record searching at" + "\n"
372
+ currentDID["verification"] += "- " + log_location + "\n"
373
+ if !options.transform_keys(&:to_s)["log_location"].nil?
374
+ currentDID["verification"] += "- " + options.transform_keys(&:to_s)["log_location"].to_s + "\n"
375
+ end
376
+ currentDID["verification"] += "(Details: https://ownyourdata.github.io/ppldid/#retrieve_log)" + "\n\n"
377
+ end
378
+ break
379
+ end
380
+ when 1 # revocation log entry
381
+ # do nothing
382
+ else
383
+ currentDID["error"] = 2
384
+ currentDID["message"] = "FATAL ERROR: op code '" + el["op"].to_s + "' not implemented"
385
+ return currentDID
386
+
387
+ end
388
+ i += 1
389
+ end unless currentDID["log"].nil?
390
+
391
+ return currentDID
392
+ end
393
+
394
+ end