class VChainClient::Client
Constants
- CLIENT_LIB_VERSION
- DATA_POINT_VERSION
- FIELD_TYPE_TEST_DOCUMENT
- FIELD_TYPE_TEST_DOCUMENT_HASHED
- FIELD_TYPE_TRAVEL_DOCUMENT
- FIELD_TYPE_TRAVEL_DOCUMENT_HASHED
Public Class Methods
cut(arr)
click to toggle source
# File lib/vchain_client.rb, line 68 def self.cut(arr) arr.each { |k, v| if arr["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED if k == "type" || k == "country_code" || k == "number" || k == "surname" || k == "given_names" || k == "birthdate" || k == "nationality" || k == "sex" || k == "birthplace" || k == "issue_date" || k == "expiry_date" || k == "authority" arr[k] = v else arr.delete(k) end elsif arr["type"] == FIELD_TYPE_TEST_DOCUMENT_HASHED if k == "type" || k == "a" || k == "b" || k == "c" || k == "d" || k == "e" arr[k] = v else arr.delete(k) end end } end
full_hash(arr)
click to toggle source
# File lib/vchain_client.rb, line 52 def self.full_hash(arr) output = {} arr.each { |k, v| if k != "names_parts" && k != "names" if k != "doc_hash" output[Digest::SHA512.hexdigest(k)] = Digest::SHA512.hexdigest(v) else output[k] = Digest::SHA512.hexdigest(v) end end } return output end
generateBlockstackCommand(config, role = "verificator", validator_sig_v2 = nil, validator_sig_v3 = nil)
click to toggle source
# File lib/vchain_client.rb, line 1569 def self.generateBlockstackCommand(config, role = "verificator", validator_sig_v2 = nil, validator_sig_v3 = nil) OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?) blockstack_id = config["blockstack"]["client_id"] #A1 ECC pubkey ecc_public_key_location = config["ecc_public_key_location"] ecc_pub_key = File.read(ecc_public_key_location) ecc_pub_key.slice! "-----BEGIN PUBLIC KEY-----\n" ecc_pub_key.slice! "\n-----END PUBLIC KEY-----\n" ecc_pub_key_aligned = ecc_pub_key.gsub(/\n/, "") #A2 vchain_client_id vchain_client_id = config["client_id"] #A4 validator_vchain_id validator_vchain_id = "da93b5f7-2295-4435-a67a-4fc226eca3ac" #A5 validator_blockstack_id validator_blockstack_id = "vchain_core_01.id" #A6 vchain_role vchain_role = role #A7 sig_version sig_version = "3" priv_key_path = config["ecc_private_key_location"] priv_key = File.read(priv_key_path) #A9 RSA pubkey rsa_public_key_location = config["rsa_public_key_location"] rsa_pub_key = File.read(rsa_public_key_location) rsa_pub_key.slice! "-----BEGIN PUBLIC KEY-----\n" rsa_pub_key.slice! "\n-----END PUBLIC KEY-----\n" rsa_pub_key_aligned = rsa_pub_key.gsub(/\n/, "") whole_sign_v2 = vchain_client_id + vchain_role + blockstack_id + ecc_pub_key + rsa_pub_key + "2" whole_sign_v3 = vchain_client_id + vchain_role + blockstack_id + ecc_pub_key_aligned + rsa_pub_key_aligned + sig_version ec = OpenSSL::PKey::EC.new(priv_key) digest = OpenSSL::Digest::SHA256.new whole_signature_v2 = ec.sign(digest, whole_sign_v2) whole_signature_v3 = ec.sign(digest, whole_sign_v3) #A8 client_sig v2 client_sig_v2 = Base64.encode64(whole_signature_v2).gsub(/\n/, "") #A10 client_sig v3 client_sig_v3 = Base64.encode64(whole_signature_v3).gsub(/\n/, "") if validator_sig_v2 == nil || validator_sig_v3 == nil puts "blockstack_id = " + blockstack_id puts "vchain_client_id = " + vchain_client_id puts "ecc_pub_key = "+ ecc_pub_key_aligned puts "rsa_pub_key = "+ rsa_pub_key_aligned puts "validator_vchain_id = "+ validator_vchain_id puts "vchain_role = " + vchain_role puts "client_sig_v2 = " + client_sig_v2 puts "client_sig_v3 = " + client_sig_v3 else puts "BLOCKSTACK_DEBUG=1 blockstack update "+ blockstack_id +" '$ORIGIN "+ blockstack_id +" $TTL 3600 A1 TXT \""+ ecc_pub_key_aligned +"\" A2 TXT \""+ vchain_client_id +"\" A3 TXT \""+ validator_sig_v2 +"\" A4 TXT \""+ validator_vchain_id +"\" A5 TXT \""+ validator_blockstack_id +"\" A6 TXT \""+ vchain_role +"\" A7 TXT \""+ sig_version +"\" A8 TXT \""+ client_sig_v2 +"\" A9 TXT \""+ rsa_pub_key_aligned +"\" A10 TXT \""+ client_sig_v3 +"\" A11 TXT \""+ validator_sig_v3 +"\" _tcp._http URI 10 1 \"http://example.com\" '" end end
get_credentials_fields(type)
click to toggle source
# File lib/vchain_client.rb, line 122 def self.get_credentials_fields(type) if type == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED return ["type", "number", "given_names", "surname", "birthdate"] elsif type == FIELD_TYPE_TEST_DOCUMENT_HASHED return ["type", "a", "b", "c"] end if @log.error? @log.error("[get_credentials_fields] unknown document type: '"+ type +"'") end return nil end
get_credentials_hash(document)
click to toggle source
# File lib/vchain_client.rb, line 178 def self.get_credentials_hash(document) if document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED what_to_hash = document["type"] + document["number"] + document["given_names"] + document["surname"] + document["birthdate"] return Digest::SHA512.hexdigest(what_to_hash) elsif document["type"] == FIELD_TYPE_TEST_DOCUMENT_HASHED what_to_hash = document["type"] + document["a"] + document["b"] + document["c"] return Digest::SHA512.hexdigest(what_to_hash) end if @log.error? @log.error("[get_credentials_hash] unknown document type: '"+ document["type"] +"'") end return nil end
get_doc_hash(arr)
click to toggle source
# File lib/vchain_client.rb, line 88 def self.get_doc_hash(arr) if arr["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED what_to_hash = "" what_to_hash += arr["type"] if arr.key?("type") what_to_hash += arr["country_code"] if arr.key?("country_code") what_to_hash += arr["number"] if arr.key?("number") what_to_hash += arr["surname"] if arr.key?("surname") what_to_hash += arr["given_names"] if arr.key?("given_names") what_to_hash += arr["birthdate"] if arr.key?("birthdate") what_to_hash += arr["nationality"] if arr.key?("nationality") what_to_hash += arr["sex"] if arr.key?("sex") what_to_hash += arr["birthplace"] if arr.key?("birthplace") what_to_hash += arr["issue_date"] if arr.key?("issue_date") what_to_hash += arr["expiry_date"] if arr.key?("expiry_date") what_to_hash += arr["authority"] if arr.key?("authority") return Digest::SHA512.hexdigest(what_to_hash) elsif arr["type"] == FIELD_TYPE_TEST_DOCUMENT_HASHED what_to_hash = "" what_to_hash += arr["type"] if arr.key?("type") what_to_hash += arr["a"] if arr.key?("a") what_to_hash += arr["b"] if arr.key?("b") what_to_hash += arr["c"] if arr.key?("c") what_to_hash += arr["d"] if arr.key?("d") what_to_hash += arr["e"] if arr.key?("e") return Digest::SHA512.hexdigest(what_to_hash) end end
get_similar_credential_sets(type)
click to toggle source
# File lib/vchain_client.rb, line 138 def self.get_similar_credential_sets(type) if type == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED output = [] # given_names output.push(["type", "number", "surname", "birthdate"]) # surname output.push(["type", "number", "given_names", "birthdate"]) # number output.push(["type", "given_names", "surname", "birthdate"]) # birthdate output.push(["type", "number", "given_names", "surname"]) # given_names + number output.push(["type", "surname", "birthdate"]) # surname + number output.push(["type", "given_names", "birthdate"]) # given_names + birthdate output.push(["type", "number", "surname"]) # surname + birthdate output.push(["type", "number", "given_names"]) # given_names + surname output.push(["type", "number", "birthdate"]) return output elsif type == FIELD_TYPE_TEST_DOCUMENT_HASHED output = [] output.push(["type", "a", "b"]) output.push(["type", "a", "c"]) output.push(["type", "b", "c"]) return output end if @log.error? @log.error("[get_credentials_fields] unknown document type: '"+ type +"'") end return nil end
hash_doc(arr)
click to toggle source
# File lib/vchain_client.rb, line 40 def self.hash_doc(arr) arr.each { |k, v| if k != "names_parts" if k != "surname" && k != "given_names" arr[k] = Digest::SHA512.hexdigest(v.downcase) else arr[k] = Digest::SHA512.hexdigest(v) end end } end
new(config)
click to toggle source
# File lib/vchain_client.rb, line 34 def initialize(config) @config = config @log = Log4r::Logger["vchain_client"] end
Public Instance Methods
add_batch_data_points(input_arr)
click to toggle source
# File lib/vchain_client.rb, line 322 def add_batch_data_points(input_arr) cryptoHelper = VChainClient::Crypto.new(@config) time = Time.now.getutc timestamp = time.to_i batch = [] index = 0 input_arr.each { |hashed_document| if !hashed_document.key?("point_type") if @log.error? @log.error("CLIENT LIB VERSION: "+ CLIENT_LIB_VERSION) @log.error("[add_batch_data_points] point_type not set for "+ index +" element in input array") end return false end if !hashed_document.key?("doc_hash") if @log.error? @log.error("CLIENT LIB VERSION: "+ CLIENT_LIB_VERSION) @log.error("[add_batch_data_points] doc_hash not set for "+ index +" element in input array") end return false end if !hashed_document.key?("credentials_hash") if @log.error? @log.error("CLIENT LIB VERSION: "+ CLIENT_LIB_VERSION) @log.error("[add_batch_data_points] credentials_hash not set for "+ index +" element in input array") end return false end point_type = hashed_document["point_type"] hashed_document.delete("point_type") doc_hash = hashed_document["doc_hash"] hashed_document.delete("doc_hash") credentials_hash = hashed_document["credentials_hash"] hashed_document.delete("credentials_hash") weight = 1 if hashed_document.key?("weight") weight = hashed_document["weight"] end hashed_document.delete("weight") if weight > 1 weight = 1 elsif weight < 0 weight = 0 end point_signatures = nil begin point_signatures = cryptoHelper.signDataPoint(point_type, hashed_document, doc_hash, credentials_hash, weight, timestamp) rescue => e if @log.error? @log.error("[add_batch_data_points] Crypto.signDataPoint raised exception") @log.error("#{e.class}, #{e.message}") @log.error("-> point_type: "+ point_type) @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> document:") @log.error(hashed_document) end raise e end if point_signatures == nil if @log.error? @log.error("[add_batch_data_points] failed to Crypto.signDataPoint") @log.error("-> point_type: "+ point_type) @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> document:") @log.error(hashed_document) end return false end if @log.debug? @log.debug("[add_batch_data_points] point_signatures:") @log.debug(point_signatures) end batch_element = { "data" => Hash[hashed_document.sort], "point_type" => point_type, "weight" => weight, "signatures" => point_signatures } batch.push(batch_element) index += 1 } client_id = @config["client_id"] api_url = @config["api"]["url"] + "v0.4/batchAddDataPoint/" whole_signature = nil begin whole_signature = cryptoHelper.signBatchRequest(batch, timestamp) rescue => e if @log.error? @log.error("[add_batch_data_points] Crypto.signRequest raised exception:") @log.error("#{e.class}: #{e.message}") end raise e else if whole_signature == nil if @log.error? @log.error("[add_batch_data_points] failed to sign request") end return false end if @log.debug? @log.debug("[add_batch_data_points] Crypto.signRequest went well, whole_signature:") @log.debug(whole_signature) end send_data = {} send_data["signature"] = whole_signature send_data["timestamp"] = timestamp.to_s send_data["data"] = [] batch.each { |doc| send_doc = {} send_doc["data"] = doc["data"] send_doc["weight"] = doc["weight"].to_s send_doc["type"] = doc["point_type"] send_doc["point_signatures"] = doc["signatures"] send_data["data"].push(send_doc) } cyphered_data = cryptoHelper.encodeCypher(send_data.to_json) encoded_key = cryptoHelper.encodeRSA(cyphered_data["key"]) encoded_iv = cryptoHelper.encodeRSA(cyphered_data["iv"]) doc_to_send = { "key" => Base64.encode64(encoded_key), "iv" => Base64.encode64(encoded_iv), "payload" => Base64.encode64(cyphered_data["payload"]), "client_id" => client_id } if @log.debug? @log.debug("[add_batch_data_points] raw sent data:") @log.debug(send_data.to_json) @log.debug("[add_batch_data_points] sent data:") @log.debug(doc_to_send) end begin res = RestClient::Resource.new api_url, :timeout => nil, :open_timeout => nil req = res.post doc_to_send.to_json, :content_type => 'application/json' if req.code != 200 if @log.error? @log.error("[add_batch_data_points] response with code "+ req.code.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> raw sent data:") @log.error(send_data) @log.error("-> sent data:") @log.error(doc_to_send) end return false end if @log.debug? @log.debug("[add_batch_data_points] response code is 200:") @log.debug(req.body) end return true rescue => e if @log.error? @log.error("[add_batch_data_points] RestClient.post raised exception") @log.error("#{e.class}, #{e.message}") @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> raw sent data:") @log.error(send_data) @log.error("-> sent data:") @log.error(doc_to_send) end raise e end end end
add_data_point(point_type, input, weight = 1)
click to toggle source
# File lib/vchain_client.rb, line 552 def add_data_point(point_type, input, weight = 1) client_id = @config["client_id"] api_url = @config["api"]["url"] + "v0.4/addDataPoint/" time = Time.now.getutc timestamp = time.to_i document = input if input["type"] == FIELD_TYPE_TRAVEL_DOCUMENT # work with names before hashing surnames_arr = document["surname"] given_names_arr = document["given_names"] if !surnames_arr.kind_of?(Array) surnames_arr = surnames_arr.split end if !given_names_arr.kind_of?(Array) given_names_arr = given_names_arr.split end hashed_surname = "" sep = "" surnames_arr.each { |surname_part| hashed_surname += sep hashed_surname += Digest::SHA512.hexdigest(surname_part.downcase) sep = " " } hashed_given_names = "" sep = "" given_names_arr.each { |given_names_part| hashed_given_names += sep hashed_given_names += Digest::SHA512.hexdigest(given_names_part.downcase) sep = " " } document["given_names"] = hashed_given_names document["surname"] = hashed_surname end document = VChainClient::Client.hash_doc(document) document = VChainClient::Client.cut(document) doc_hash = VChainClient::Client.get_doc_hash(document) credentials_hash = VChainClient::Client.get_credentials_hash(document) if weight > 1 weight = 1 elsif weight < 0 weight = 0 end if @log.debug? @log.debug("CLIENT LIB VERSION: "+ CLIENT_LIB_VERSION) @log.debug("[verify] will call "+ api_url +" using vchain_client_id "+ client_id) @log.debug("-> point_type: "+ point_type) @log.debug("-> weight: "+ weight.to_s) @log.debug("-> timestamp: "+ timestamp.to_s) @log.debug("-> hashed input:") @log.debug(document) end cryptoHelper = VChainClient::Crypto.new(@config) point_signatures = nil begin point_signatures = cryptoHelper.signDataPoint(point_type, document, doc_hash, credentials_hash, weight, timestamp) rescue => e if @log.error? @log.error("[verify] Crypto.signDataPoint raised exception") @log.error("#{e.class}, #{e.message}") @log.error("-> point_type: "+ point_type) @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> document:") @log.error(document) end raise e end if point_signatures == nil if @log.error? @log.error("[verify] failed to Crypto.signDataPoint") @log.error("-> point_type: "+ point_type) @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> document:") @log.error(document) end return false end if @log.debug? @log.debug("[verify] point_signatures:") @log.debug(point_signatures) end document = Hash[document.sort] if @log.debug? @log.debug("[verify] hashed input after sort:") @log.debug(document) end whole_signature = nil begin whole_signature = cryptoHelper.signRequest(document, point_type, weight, timestamp) rescue => e if @log.error? @log.error("[verify] Crypto.signRequest raised exception:") @log.error("#{e.class}: #{e.message}") @log.error("-> point_type: #{point_type}") @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> document:") @log.error(document) end raise e else if whole_signature == nil if @log.error? @log.error("[verify] failed to sign request") @log.error("-> point_type: #{point_type}") @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> document:") @log.error(document) end return false end if @log.debug? @log.debug("[verify] Crypto.signRequest went well, whole_signature:") @log.debug(whole_signature) end end send_data = {} send_data["data"] = document send_data["weight"] = weight.to_s send_data["timestamp"] = timestamp.to_s send_data["type"] = point_type send_data["point_signatures"] = point_signatures send_data["signature"] = whole_signature cyphered_data = cryptoHelper.encodeCypher(send_data.to_json) encoded_key = cryptoHelper.encodeRSA(cyphered_data["key"]) encoded_iv = cryptoHelper.encodeRSA(cyphered_data["iv"]) doc_to_send = { "key" => Base64.encode64(encoded_key), "iv" => Base64.encode64(encoded_iv), "payload" => Base64.encode64(cyphered_data["payload"]), "client_id" => client_id } if @log.debug? @log.debug("[verify] send_data:") @log.debug(doc_to_send) end begin req = RestClient.post(api_url, doc_to_send.to_json, {'Content-Type' => 'application/json'}) if req.code != 200 if @log.error? @log.error("[verify] response with code "+ req.code.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> point_type: #{point_type}") @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> raw sent data:") @log.error(send_data) @log.error("-> sent data:") @log.error(doc_to_send) end return false end if @log.debug? @log.debug("[verify] response code is 200:") @log.debug(req.body) end return true rescue => e if @log.error? @log.error("[verify] RestClient.post raised exception") @log.error("#{e.class}, #{e.message}") @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> point_type: #{point_type}") @log.error("-> weight: "+ weight.to_s) @log.error("-> timestamp: "+ timestamp.to_s) @log.error("-> raw sent data:") @log.error(send_data) @log.error("-> sent data:") @log.error(doc_to_send) end raise e end end
build_merkle_tree(tree, timestamp)
click to toggle source
# File lib/vchain_client.rb, line 199 def build_merkle_tree(tree, timestamp) previous_parent = nil tree.each { |tree_level| if previous_parent != nil if previous_parent != tree_level["left"] && previous_parent != tree_level["right"] if @log.error? @log.error("[build_merkle_tree] wrong tree consequence - no parent in next level") end return nil end end what_to_hash = tree_level["left"] if tree_level["right"] != nil what_to_hash += tree_level["right"] else what_to_hash += tree_level["left"] end what_to_hash += timestamp.to_s level_hash = Digest::SHA256.hexdigest(what_to_hash); if level_hash != tree_level["parent"] if @log.error? @log.error("[build_merkle_tree] level hash not equal to its parent") end return nil end previous_parent = level_hash } return previous_parent end
check(input, is_already_hashed = false, preffered_decision_algo = nil)
click to toggle source
# File lib/vchain_client.rb, line 840 def check(input, is_already_hashed = false, preffered_decision_algo = nil) cryptoHelper = VChainClient::Crypto.new(@config) client_id = @config["client_id"] api_url = @config["api"]["url"] + "v0.4/check/"; document = input names = {} if (!is_already_hashed && document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT) || (is_already_hashed && document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED) names = document["names"] document.delete("names") end if (!is_already_hashed) document = VChainClient::Client.hash_doc(document) end document = VChainClient::Client.cut(document) if document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED document["names"] = [] names.each { |name| name_hash = name if (!is_already_hashed) name_hash = Digest::SHA512.hexdigest(name.downcase) end document["names"].push(name_hash) } end sent_document = document.clone cyphered_data = cryptoHelper.encodeCypher(document.to_json) encoded_key = cryptoHelper.encodeRSA(cyphered_data["key"]) encoded_iv = cryptoHelper.encodeRSA(cyphered_data["iv"]) doc_to_send = { "key" => Base64.encode64(encoded_key), "iv" => Base64.encode64(encoded_iv), "payload" => Base64.encode64(cyphered_data["payload"]), "client_id" => client_id } if @log.debug? @log.debug("[check] will call "+ api_url +" using vchain_client_id "+ client_id) @log.debug("-> is_already_hashed: #{is_already_hashed}") @log.debug("-> hashed input:") @log.debug(document) @log.debug("-> sending:") @log.debug(doc_to_send) end req = nil begin res = nil if names.length > 4 res = RestClient::Resource.new api_url, :timeout => nil, :open_timeout => nil else res = RestClient::Resource.new api_url end req = res.post doc_to_send.to_json, :content_type => 'application/json' if req.code != 200 if @log.error? @log.error("[check] response with code "+ req.code.to_s) @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> is_already_hashed: #{is_already_hashed}") @log.error("-> sent data:") @log.error(document) end return false end rescue => e if @log.error? @log.error("[check] RestClient.post raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("-> client_id: #{client_id}") @log.error("-> api_url: #{api_url}") @log.error("-> is_already_hashed: #{is_already_hashed}") @log.error("-> sent data:") @log.error(document) end raise e end res = JSON.parse req.body if @log.debug? @log.debug("response:") @log.debug(req.body) end if res["status"].downcase == "error" return { "status" => "error", "reason" => res["error_reason_code"] } else # success result res_key = cryptoHelper.decodeRSA(Base64.decode64(res["key"])) res_iv = cryptoHelper.decodeRSA(Base64.decode64(res["iv"])) res_docs = cryptoHelper.decodeCypher(Base64.decode64(res["docs"]), res_key, res_iv) res_data_points = cryptoHelper.decodeCypher(Base64.decode64(res["data_points"]), res_key, res_iv) res_names = cryptoHelper.decodeCypher(Base64.decode64(res["names"]), res_key, res_iv) res = { "docs" => JSON.parse(res_docs), "data_points" => JSON.parse(res_data_points), "names" => JSON.parse(res_names) } validated_data_points = self.validate_data_points(res["data_points"], res["docs"]) if validated_data_points.length == 0 # not found result = {} sent_document.each { |field, value| result[field] = 0 } return { "status" => "success", "result" => { "decision_made" => true, "decision" => "NO_DATA" } } end # # decision # decisionAlgo = nil if preffered_decision_algo == nil # falling to default algo decisionAlgo = VChainClient::VectorBasedDecisionAlgorithm.new(@config) end decision = decisionAlgo.make_decision(sent_document, res, validated_data_points) # result output return { "status" => "success", "result" => decision } end end
generate_batch_check(input_arr)
click to toggle source
# File lib/vchain_client.rb, line 798 def generate_batch_check(input_arr) if !input_arr.kind_of?(Array) if @log.error? @log.error("[generate_batch_check] input should be array") @log.error("-> input_arr:") @log.error(input_arr) end return false end output = [] input_arr.each { |raw_document| names = {} if raw_document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT names = raw_document["names"] raw_document.delete("names") end out_document = VChainClient::Client.hash_doc(raw_document) out_document = VChainClient::Client.cut(out_document) if out_document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT_HASHED out_document["names"] = [] names.each { |name| name_hash = Digest::SHA512.hexdigest(name.downcase) out_document["names"].push(name_hash) } end output.push(out_document) } return output end
generate_batch_data_points(input_arr)
click to toggle source
# File lib/vchain_client.rb, line 239 def generate_batch_data_points(input_arr) output = [] input_arr.each { |raw_document| if !raw_document.key?("point_type") if @log.error? @log.error("CLIENT LIB VERSION: "+ CLIENT_LIB_VERSION) @log.error("[generate_batch_data_points] point_type not set for element in input array") end return false end point_type = raw_document["point_type"] raw_document.delete("point_type") weight = 1 if raw_document.key?("weight") weight = raw_document["weight"] raw_document.delete("weight") end if raw_document["type"] == FIELD_TYPE_TRAVEL_DOCUMENT # dealing with names before hashing surnames_arr = raw_document["surname"] given_names_arr = raw_document["given_names"] if !surnames_arr.kind_of?(Array) surnames_arr = surnames_arr.split end if !given_names_arr.kind_of?(Array) given_names_arr = given_names_arr.split end hashed_surname = "" sep = "" surnames_arr.each { |surname_part| hashed_surname += sep hashed_surname += Digest::SHA512.hexdigest(surname_part.downcase) sep = " " } hashed_given_names = "" sep = "" given_names_arr.each { |given_names_part| hashed_given_names += sep hashed_given_names += Digest::SHA512.hexdigest(given_names_part.downcase) sep = " " } raw_document["given_names"] = hashed_given_names raw_document["surname"] = hashed_surname end out_document = VChainClient::Client.hash_doc(raw_document) out_document = VChainClient::Client.cut(out_document) doc_hash = VChainClient::Client.get_doc_hash(out_document) out_document["doc_hash"] = doc_hash credentials_hash = VChainClient::Client.get_credentials_hash(out_document) out_document["credentials_hash"] = credentials_hash if weight > 1 weight = 1 elsif weight < 0 weight = 0 end out_document["weight"] = weight out_document["point_type"] = point_type output.push(out_document) } return output end
validate_data_points(data_points, documents)
click to toggle source
# File lib/vchain_client.rb, line 1022 def validate_data_points(data_points, documents) output = {} blockchainConnection = VChainClient::BlockchainConnection.new(@config) blockstackClient = VChainClient::BlockstackClient.new(@config) cryptoHelper = VChainClient::Crypto.new(@config) documents_index = {} documents.each { |document| hashed_doc = VChainClient::Client.full_hash(document) hashed_doc["original_doc_hash"] = VChainClient::Client.get_doc_hash(document) documents_index[hashed_doc["doc_hash"]] = hashed_doc } data_points.each { |data_point| if @config.key?("do_not_check") && @config["do_not_check"] == true if !output.key?(data_point["doc_hash"]) output[data_point["doc_hash"]] = {} end if !output[data_point["doc_hash"]].key?(data_point["field_hash"]) output[data_point["doc_hash"]][data_point["field_hash"]] = 0 end # add weight coming from the data_point output[data_point["doc_hash"]][data_point["field_hash"]] += (1 * data_point["weight"].to_f) next end if !data_point.key?("blockchain_reciepts") if @log.error? @log.error("[check] not a valid data point - no blockchain_reciepts key") @log.error(data_point) end next end if data_point["blockchain_reciepts"].length <= 0 if @log.error? @log.error("[check] not a valid data point - blockchain_reciepts is empty") @log.error(data_point) end next end if !documents_index.key?(data_point["doc_hash"]) if @log.error? @log.error("[check] not a valid data point - no such doc_hash in docs returned") @log.error(data_point) end next end document = documents_index[data_point["doc_hash"]] doc_hash = Digest::SHA512.hexdigest(document["original_doc_hash"]) # 1a. check doc_hash if doc_hash != data_point["doc_hash"] if @log.error? @log.error("[check] not a valid data point - doc_hash mismatch ("+ data_point["doc_hash"] +")") end next end # 1b. check field_hash if !document.key?(data_point["field_hash"]) if @log.error? @log.error("[check] not a valid data point - field_hash mismatch ("+ data_point["field_hash"] +")") end next end # 1c. check data_hash data_hash = document[data_point["field_hash"]] if data_hash != data_point["data_hash"] if @log.error? @log.error("[check] not a valid data point - data_hash mismatch ("+ data_hash +")") end next end # 1d. check data_point_hash checksum_to_hash = data_point["field_hash"] + data_point["data_hash"] + data_point["doc_hash"] + data_point["credentials_hash"] + data_point["type"] + data_point["issuer_sig"] + data_point["issuer_id"] + data_point["validator_sig"] + data_point["validator_id"] + data_point["weight"] + data_point["timestamp"] + data_point["version"] checksum = Digest::SHA512.hexdigest(checksum_to_hash) if checksum != data_point["data_point_hash"] if @log.error? @log.error("[check] not a valid data point - checksum mismatch ("+ checksum +")") end next end # 2. check verification_hash to be in target_proof's # first element reciepts_validated = 0 data_point["blockchain_reciepts"].each { |reciept| if !reciept.key?("target_proof") if @log.error? @log.error("[check] not a valid blockchain reciept - no target_proof") @log.error(reciept) end break end if reciept["target_proof"].length <= 0 if @log.error? @log.error("[check] not a valid blockchain reciept - target_proof length is <= 0") @log.error(reciept) end break end if reciept["target_proof"][0]["left"] != data_point["data_point_hash"] && reciept["target_proof"][0]["right"] != data_point["data_point_hash"] if @log.error? @log.error("[check] not a valid blockchain reciept - no target element in target_proof.0") @log.error(reciept) end break end # verification_hash is in tree # and in right position # now, # 3. check tree convergence to root hash # and compare this computed root hash # with merkle_tree_root_hash from response computed_tree_root_hash = self.build_merkle_tree(reciept["target_proof"], reciept["timestamp"]) if computed_tree_root_hash == nil if @log.error? @log.error("[check] not a valid blockchain reciept - failed to compute tree root hash") @log.error(reciept) end break end if computed_tree_root_hash != reciept["merkle_tree_root_hash"] if @log.error? @log.error("[check] not a valid blockchain reciept - merkle tree root hash mismatch ("+ computed_tree_root_hash +", "+ reciept["merkle_tree_root_hash"] +")") @log.error(reciept) end break end last_proof_index = reciept["target_proof"].length - 1 reciept_stored_last_parent = reciept["target_proof"][last_proof_index]["parent"] if reciept_stored_last_parent != computed_tree_root_hash if @log.error? @log.error("[check] not a valid blockchain reciept - last stored parent != computed_tree_root_hash ("+ reciept_stored_last_parent +", "+ computed_tree_root_hash +")") @log.error(reciept) end break end # 4. check OP_RETURN in Bitcoin's tx, # compare it to computed root hash of a tree # retrieve some info from tx to verify signature tx = nil begin tx = blockchainConnection.getTx(reciept["blockchain_txid"]) rescue => e if @log.error? @log.error("[check] BlockchainConnection.getTx raised exception:") @log.error("#{e.class}, #{e.message}") end raise e end if tx == nil if @log.error? @log.error("[check] not a valid blockchain reciept - failed to retrieve TX from Blockchain") @log.error(reciept) end break end if tx["block_hash"] != reciept["blockchain_block_hash"] if @log.error? @log.error("[check] not a valid blockchain reciept - block_hash mismatch") @log.error(tx) @log.error(reciept) end break end if tx["block_timestamp"] != reciept["blockchain_timestamp"] if @log.error? @log.error("[check] not a valid blockchain reciept - timestamp mismatch") @log.error(tx) @log.error(reciept) end break end if tx["op_return"] != computed_tree_root_hash if @log.error? @log.error("[check] not a valid blockchain reciept - op_return mismatch") @log.error(computed_tree_root_hash) @log.error(tx) @log.error(reciept) end break end blockchain_txid = reciept["blockchain_txid"]; blockchain_block_hash = tx["block_hash"]; blockchain_timestamp = tx["block_timestamp"]; # 5. check tree signature: # a) federative server record in Blockstack (recursive) # b) tree_signature # a) federative server record in Blockstack (recursive) begin if !blockstackClient.checkFederativeServer(reciept["federative_server_id"]) if @log.error? @log.error("[check] not a valid blockchain reciept - failed to check federative server") @log.error(reciept) end break end rescue => e if @log.error? @log.error("[check] Blockstack.checkFederativeServer raised exception:") @log.error("#{e.class}, #{e.message}") @log.error(reciept) end raise e end # b) check tree signature federative_server_pubkey = nil begin federative_server_pubkey = blockstackClient.getPublicKeyECC(reciept["federative_server_id"]) rescue => e if @log.error? @log.error("[check] Blockstack.getPublicKeyECC raised exception:") @log.error("#{e.class}, #{e.message}") @log.error(reciept) end raise e end if federative_server_pubkey == nil if @log.error? @log.error("[check] not a valid blockchain reciept - failed to retrieve public key for federative server '"+ reciept["federative_server_id"] +"'") @log.error(reciept) end break end begin if !cryptoHelper.checkTreeSignature(computed_tree_root_hash, blockchain_txid, blockchain_block_hash, blockchain_timestamp, reciept["federative_server_id"], reciept["federative_server_version"], Base64.decode64(reciept["tree_signature"]), federative_server_pubkey) if @log.error? @log.error("[check] not a valid blockchain reciept - failed to verify tree signature") @log.error(reciept) @log.error("--> computed_tree_root_hash: #{computed_tree_root_hash}") @log.error("--> blockchain_txid: #{blockchain_txid}") @log.error("--> blockchain_block_hash: #{blockchain_block_hash}") @log.error("--> blockchain_timestamp: #{blockchain_timestamp}") @log.error("--> federative_server_id: "+ reciept["federative_server_id"]) @log.error("--> federative_server_version: "+ reciept["federative_server_version"]) @log.error("--> tree_signature: "+ Base64.decode64(reciept["tree_signature"])) @log.error("--> federative_server_pubkey: #{federative_server_pubkey}") end break end rescue => e if @log.error? @log.error("[check] Blockstack.checkTreeSignature raised exception:") @log.error("#{e.class}, #{e.message}") @log.error(reciept) @log.error("--> computed_tree_root_hash: #{computed_tree_root_hash}") @log.error("--> blockchain_txid: #{blockchain_txid}") @log.error("--> blockchain_block_hash: #{blockchain_block_hash}") @log.error("--> blockchain_timestamp: #{blockchain_timestamp}") @log.error("--> federative_server_id: "+ reciept["federative_server_id"]) @log.error("--> federative_server_version: "+ reciept["federative_server_version"]) @log.error("--> tree_signature: "+ Base64.decode64(reciept["tree_signature"])) @log.error("--> federative_server_pubkey: #{federative_server_pubkey}") end raise e end reciepts_validated += 1 } if reciepts_validated != data_point["blockchain_reciepts"].length if @log.error? @log.error("[check] not a valid verification - not every reciept were validated") end next end # 6. check verification signatures: # a) check verificator record in Blockstack (recursive) # b) check validator record in Blockstack (recursive) # c) verificator_sig # d) validtor_sig # a) check verificator record in Blockstack (recursive) begin if !blockstackClient.checkVerificator(data_point["issuer_id"]) if @log.error? @log.error("[check] not a valid verification - failed to check verificator record") @log.error("--> verificator_id: "+ data_point["issuer_id"]) end next end rescue => e if @log.error? @log.error("[check] Blockstack.checkVerificator raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> verificator_id: "+ data_point["issuer_id"]) end raise e end # b) check validator record in Blockstack (recursive) begin if !blockstackClient.checkValidator(data_point["validator_id"]) if @log.error? @log.error("[check] not a valid verification - failed to check validator record") @log.error("--> validator_id: "+ data_point["validator_id"]) end next end rescue => e if @log.error? @log.error("[check] Blockstack.checkValidator raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> validator_id: "+ data_point["validator_id"]) end raise e end # c) check verificator's signature verificator_pubkey = nil begin verificator_pubkey = blockstackClient.getPublicKeyECC(data_point["issuer_id"]) rescue => e if @log.error? @log.error("[check] Blockstack.getPublicKeyECC raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> verificator_id: "+ data_point["issuer_id"]) end raise e end if verificator_pubkey == nil if @log.error? @log.error("[check] failed to retrieve public key for verificator") @log.error(document) @log.error("--> verificator_id: "+ data_point["issuer_id"]) end next end if @log.debug? @log.debug("verificator pubkey:") @log.debug(verificator_pubkey) end begin if !cryptoHelper.checkVerificationSignature(data_point["field_hash"], data_point["data_hash"], data_point["doc_hash"], data_point["credentials_hash"], data_point["type"], data_point["weight"], data_point["timestamp"], data_point["issuer_id"], verificator_pubkey, Base64.decode64(data_point["issuer_sig"]), data_point["version"]) if @log.error? @log.error("[check] not a valid verification - failed to check verificator signature") @log.error("--> field_hash: "+ data_point["field_hash"]) @log.error("--> data_hash: "+ data_point["data_hash"]) @log.error("--> doc_hash: "+ data_point["doc_hash"]) @log.error("--> credentials_hash: "+ data_point["credentials_hash"]) @log.error("--> type: "+ data_point["type"]) @log.error("--> weight: "+ data_point["weight"].to_s) @log.error("--> timestamp: "+ data_point["timestamp"].to_s) @log.error("--> verificator_id: "+ data_point["issuer_id"]) @log.error("--> verificator_pubkey: "+ verificator_pubkey) @log.error("--> verificator_sig: "+ data_point["issuer_sig"]) end next end rescue => e if @log.error? @log.error("[check] Crypto.checkVerificationSignature raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> field_hash: "+ data_point["field_hash"]) @log.error("--> data_hash: "+ data_point["data_hash"]) @log.error("--> doc_hash: "+ data_point["doc_hash"]) @log.error("--> credentials_hash: "+ data_point["credentials_hash"]) @log.error("--> type: "+ data_point["type"]) @log.error("--> weight: "+ data_point["weight"].to_s) @log.error("--> timestamp: "+ data_point["timestamp"].to_s) @log.error("--> verificator_id: "+ data_point["issuer_id"]) @log.error("--> verificator_pubkey: "+ verificator_pubkey) @log.error("--> verificator_sig: "+ data_point["issuer_sig"]) end raise e end # d) check validator's signature validator_pubkey = nil begin validator_pubkey = blockstackClient.getPublicKeyECC(data_point["validator_id"]) rescue => e if @log.error? @log.error("[check] Blockstack.getPublicKeyECC raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> validator_id: "+ data_point["validator_id"]) end raise e end if validator_pubkey == nil if @log.error? @log.error("[check] failed to retrieve pubic key for validator") @log.error("--> validator_id: "+ data_point["validator_id"]) end next end if @log.debug? @log.debug("validator pubkey:") @log.debug(validator_pubkey) end begin if !cryptoHelper.checkVerificationSignature(data_point["field_hash"], data_point["data_hash"], data_point["doc_hash"], data_point["credentials_hash"], data_point["type"], data_point["weight"], data_point["timestamp"], data_point["validator_id"], validator_pubkey, Base64.decode64(data_point["validator_sig"]), data_point["version"]) if @log.error? @log.error("[check] not a valid verification - failed to check validator signature") @log.error("--> field_hash: "+ data_point["field_hash"]) @log.error("--> data_hash: "+ data_point["data_hash"]) @log.error("--> doc_hash: "+ data_point["doc_hash"]) @log.error("--> credentials_hash: "+ data_point["credentials_hash"]) @log.error("--> type: "+ data_point["type"]) @log.error("--> weight: "+ data_point["weight"].to_s) @log.error("--> timestamp: "+ data_point["timestamp"].to_s) @log.error("--> validator_id: "+ data_point["validator_id"]) @log.error("--> validator_pubkey: "+ validator_pubkey) @log.error("--> validator_sig: "+ Base64.encode64(data_point["validator_sig"])) end next end rescue => e if @log.error? @log.error("[check] Crypto.checkVerificationSignature raised exception:") @log.error("#{e.class}, #{e.message}") @log.error("--> field_hash: "+ data_point["field_hash"]) @log.error("--> data_hash: "+ data_point["data_hash"]) @log.error("--> doc_hash: "+ data_point["doc_hash"]) @log.error("--> credentials_hash: "+ data_point["credentials_hash"]) @log.error("--> type: "+ data_point["type"]) @log.error("--> weight: "+ data_point["weight"].to_s) @log.error("--> timestamp: "+ data_point["timestamp"].to_s) @log.error("--> validator_id: "+ data_point["validator_id"]) @log.error("--> validator_pubkey: "+ validator_pubkey) @log.error("--> validator_sig: "+ Base64.encode64(data_point["validator_sig"])) end raise e end # 7. timestamps checking # TODO if !output.key?(data_point["doc_hash"]) output[data_point["doc_hash"]] = {} end if !output[data_point["doc_hash"]].key?(data_point["field_hash"]) output[data_point["doc_hash"]][data_point["field_hash"]] = 0 end # add weight coming from the data_point output[data_point["doc_hash"]][data_point["field_hash"]] += (1 * data_point["weight"].to_f) } return output end