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