hash-merkle-tree 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8e8fac845ce12a9f3566c344b5f6582ddd7c000cf924400287fa29fdcbde8553
4
+ data.tar.gz: 9e872429d89cfe67a2c3540d9a56499d0e15dcfc0529b8792112ff20e6b69eca
5
+ SHA512:
6
+ metadata.gz: 4df0bfb1416ebeb82b258e8acae15571f688e99c7253d5a0cecf0cdfaab428ad9d6c0000379c51257f2fca8161f18018ce927f9b3d1238aa5521dd1ab211a14f
7
+ data.tar.gz: 027ee8861d0501d3696dc7baf8d4011b14721eac444001f0d703bbbcbe1e0101f324a71046f3582ee815ffe4b739d701fe8d39c637eba99ef512fe9fcb543575
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ This repo contains an implementation of "Merkle Hash Trees" (MHT).
2
+ In cryptography and computer science, a hash tree or Merkle Hash Tree
3
+ is a tree in which every "leaf" (node) is labelled with the cryptographic hash of
4
+ a data block, and every node that is not a leaf (called a branch, inner node, or inode)
5
+ is labelled with the cryptographic hash of the labels of its child nodes.
6
+ Specifically, it implements the variant described in
7
+ [RFC6962](http://tools.ietf.org/html/rfc6962)
8
+
9
+
10
+ ## Basic Usage
11
+
12
+ Assume that you have such an object, named
13
+ `data`, and we'll look at how to use the MHT.
14
+
15
+ data = %w[a b c d e f g h i j k l m n o p q r s t u v w x y z]
16
+
17
+ We'll create a new MHT:
18
+
19
+ tree = Merkle::HashTree.new(data, Digest::SHA256)
20
+
21
+ The `Merkle::HashTree` constructor takes exactly two arguments: an object that
22
+ implements the data access interface we'll talk about later, and a class (or
23
+ object) which implements the same `digest` method signature as the core
24
+ `Digest::Base` class. Typically, this will simply be a `Digest` subclass,
25
+ such as `Digest::MD5`, `Digest::SHA1`, or (as in the example above)
26
+ `Digest::SHA256`. This second argument is the way that the MHT calculates
27
+ hashes in the tree -- it simply calls the `#digest` method on whatever you
28
+ pass in as the second argument, passing in a string and expecting raw octets
29
+ out the other end.
30
+
31
+ Once we have our MHT object, we can start to do things with it.
32
+ For example, we can get the hash of the "head" of the tree:
33
+
34
+
35
+ tree.head_build # => "<some long string of octets>"
36
+
37
+
38
+ We can also ask for a "consistency proof", we need to specify an index(n) of the last
39
+ element of sub_tree (as a result sub_tree will be `data[0...n]`):
40
+
41
+
42
+ consistency_proof = tree.consistency_proof_build(10) # => ["<hash>", "<hash>", ... ]
43
+
44
+
45
+ You can test it checking sum with hash head of the tree. Or use:
46
+
47
+
48
+ tree.consistency_proof_build_head(consistency_proof) # => "<some long string of octets>"
49
+
50
+
51
+ There are also such things as "audit proofs", which you get by specifying a single leaf index:
52
+
53
+
54
+ audit_proof = tree.audit_proof_build(10) # => [ { value: "<hash>", position: "left" }, { value: "<hash>", position: "right" }, ... ]
55
+
56
+
57
+ In this example, the audit proof will return a list of hashes
58
+ that demonstrate that leaf `10` is in the tree and hasn't been removed or altered.
59
+ You can test it checking sum with hash head of the tree. Or use:
60
+
61
+
62
+ tree.audit_proof_build_head(audit_proof, data[10]) # => "<some long string of octets>"
63
+
64
+
65
+
66
+
data/lib/algorithm.rb ADDED
@@ -0,0 +1,95 @@
1
+ module Merkle
2
+ module Algorithm
3
+ class << self
4
+ def merkle_tree(array, digest, &block)
5
+ # Define it here since we have to add &block and digest params to every method call (DRY..)
6
+ merkle_tree_method = -> (arg) { merkle_tree(arg, digest, &block) }
7
+
8
+ case array.size
9
+ when 1
10
+ return array.first
11
+ when 2
12
+ # This yeild for define audit_proof method. To build audit_proof_elements
13
+ # we have to know pair of <searching> elements, so case array.size = 2 is what actually need.
14
+ # Basicly we just need to handle this case and we will have a new audit_proof algorithm!
15
+ yield(array) if block_given?
16
+
17
+ return node_hash(digest, array.first, array.last)
18
+ else
19
+ cons = array.size.even? ? 2 : 1
20
+
21
+ merkle_tree_method.call(
22
+ [
23
+ merkle_tree_method.call(array[0...(array.size - cons)]),
24
+ merkle_tree_method.call(array[(array.size - cons)..])
25
+ ]
26
+ )
27
+ end
28
+ end
29
+
30
+ def audit_proof(array, index, digest = nil)
31
+ audit_proof_path = Array.new
32
+ element = array[index]
33
+
34
+ # This block is inside case with two elements.
35
+ # We have to handle pairs only with our element and add neibour of our element to the path
36
+ # Then we have to update our element
37
+ merkle_tree(array, digest) do |pair_of_elements|
38
+ if pair_of_elements.include?(element)
39
+ audit_proof_path << begin
40
+ if pair_of_elements.first == element
41
+ {
42
+ position: 'right',
43
+ value: pair_of_elements.last
44
+ }
45
+ else
46
+ {
47
+ position: 'left',
48
+ value: pair_of_elements.first
49
+ }
50
+ end
51
+ end
52
+
53
+ element = node_hash(digest, pair_of_elements.first, pair_of_elements.last)
54
+ end
55
+ end
56
+
57
+ audit_proof_path
58
+ end
59
+
60
+ # Culculate a head of tree using audit_proof path
61
+ def consistency_proof(array, index, digest = nil)
62
+ sub_array = array[0..index]
63
+
64
+ audit_proof(sub_array, index, digest).map{ |e| e[:value] }
65
+ end
66
+
67
+ # Culculate a head of tree using audit_proof path
68
+ # By this method we can verify is the element a part of the tree or not depends on returned head
69
+ def audit_proof_build_head(audit_proof_path, element, digest = nil)
70
+ audit_proof_path.inject(element) do |element, node|
71
+ case node[:position]
72
+ when 'right'
73
+ node_hash(digest, element, node[:value])
74
+ when 'left'
75
+ node_hash(digest, node[:value], element)
76
+ end
77
+ end
78
+ end
79
+
80
+ # Culculate a head of sub-tree using consistency_proof path
81
+ # By this method we can verify is the tree includes sub-tree
82
+ def consistency_proof_build_head(consistency_proof_path, digest = nil)
83
+ consistency_proof_path.inject('') do |result, consistency_path_element|
84
+ node_hash(digest, consistency_path_element, result)
85
+ end
86
+ end
87
+
88
+ private def node_hash(digest, hash1, hash2)
89
+ summary_hash = hash1 + hash2
90
+
91
+ digest ? digest.digest("\x01" + summary_hash) : summary_hash
92
+ end
93
+ end
94
+ end
95
+ end
data/lib/hash_tree.rb ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'algorithm'
4
+
5
+ module Merkle
6
+ class HashTree
7
+ ALGORITHM_CLASS = Merkle::Algorithm
8
+
9
+ def initialize(incoming_data, digest = nil)
10
+ @algorithm = ALGORITHM_CLASS
11
+ @digest = digest
12
+ @incoming_data = incoming_data
13
+ @hashed_data = incoming_data_to_hashes
14
+ end
15
+
16
+ attr_reader :hashed_data, :incoming_data, :digest, :algorithm, :head, :audit_proof,
17
+ :consistency_proof, :consistency_proof_head, :audit_proof_head
18
+
19
+ def head_build
20
+ @head =
21
+ algorithm.merkle_tree(hashed_data, digest)
22
+ end
23
+
24
+ def audit_proof_build(index)
25
+ @audit_proof =
26
+ algorithm.audit_proof(hashed_data, index, digest)
27
+ end
28
+
29
+ def audit_proof_build_head(audit_proof_path, element)
30
+ @audit_proof_head =
31
+ algorithm.audit_proof_build_head(audit_proof_path, element, digest)
32
+ end
33
+
34
+ def consistency_proof_build(index)
35
+ @consistency_proof =
36
+ algorithm.consistency_proof(hashed_data, index, digest)
37
+ end
38
+
39
+ def consistency_proof_build_head(consistency_proof_path)
40
+ @consistency_proof_head =
41
+ algorithm.consistency_proof_build_head(consistency_proof_path, digest)
42
+ end
43
+
44
+ private def incoming_data_to_hashes
45
+ digest ? incoming_data.map! { |el| digest.digest("\u0001#{el}") } : incoming_data
46
+ end
47
+ end
48
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Merkle
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash-merkle-tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - tavrelkate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Implementation of Hash Merkle Tree, audit and consistensy proofs
28
+ email:
29
+ - tavrelkate@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files:
33
+ - README.md
34
+ files:
35
+ - README.md
36
+ - lib/algorithm.rb
37
+ - lib/hash_tree.rb
38
+ - lib/version.rb
39
+ homepage: https://github.com/tavrelkate/hash-merkle-tree
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.5.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.2.15
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Implementation of Hash Merkle Tree, audit and consistensy proofs
62
+ test_files: []