melos 0.0.1
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 +7 -0
- data/.rspec +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +5 -0
- data/Rakefile +10 -0
- data/lib/melos/constants.rb +84 -0
- data/lib/melos/crypto.rb +307 -0
- data/lib/melos/key_schedule.rb +62 -0
- data/lib/melos/psk.rb +35 -0
- data/lib/melos/secret_tree.rb +109 -0
- data/lib/melos/struct/base.rb +172 -0
- data/lib/melos/struct/ratchet_tree.rb +324 -0
- data/lib/melos/struct/structs.rb +1019 -0
- data/lib/melos/tree.rb +265 -0
- data/lib/melos/util.rb +11 -0
- data/lib/melos/vec.rb +89 -0
- data/lib/melos/version.rb +5 -0
- data/lib/melos.rb +15 -0
- data/sig/melos.rbs +4 -0
- data/test_vectors/crypto-basics.json +303 -0
- data/test_vectors/deserialization.json +58 -0
- data/test_vectors/key-schedule.json +926 -0
- data/test_vectors/message-protection.json +142 -0
- data/test_vectors/messages.json +5702 -0
- data/test_vectors/passive-client-handling-commit.json +2683 -0
- data/test_vectors/passive-client-random.json +2657 -0
- data/test_vectors/passive-client-welcome.json +814 -0
- data/test_vectors/psk_secret.json +2382 -0
- data/test_vectors/secret-tree.json +4846 -0
- data/test_vectors/transcript-hashes.json +58 -0
- data/test_vectors/tree-math.json +8276 -0
- data/test_vectors/tree-operations.json +47 -0
- data/test_vectors/tree-validation.json +11839 -0
- data/test_vectors/treekem.json +14877 -0
- data/test_vectors/welcome.json +51 -0
- metadata +110 -0
@@ -0,0 +1,172 @@
|
|
1
|
+
module Melos::Struct; end
|
2
|
+
|
3
|
+
class Melos::Struct::Base
|
4
|
+
def initialize(buf)
|
5
|
+
context, _ = deserialize(buf)
|
6
|
+
set_instance_vars(context)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.new_and_rest(buf)
|
11
|
+
instance = self.allocate
|
12
|
+
context, buf = instance.send(:deserialize, buf)
|
13
|
+
instance.send(:set_instance_vars, context)
|
14
|
+
[instance, buf]
|
15
|
+
end
|
16
|
+
|
17
|
+
def raw
|
18
|
+
buf = ''
|
19
|
+
self.class::STRUCT.each do |elem|
|
20
|
+
case elem[1]
|
21
|
+
when :select
|
22
|
+
value = self.instance_variable_get("@#{elem[0]}")
|
23
|
+
buf += serialize_select_elem(value, elem[3])
|
24
|
+
else
|
25
|
+
value = self.instance_variable_get("@#{elem[0]}")
|
26
|
+
buf += serialize_elem(value, elem[1], elem[2])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
buf
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.vecs(buf)
|
33
|
+
value, buf = Melos::Vec.parse_vec(buf)
|
34
|
+
array = []
|
35
|
+
while (value.bytesize > 0)
|
36
|
+
current_instance, value = Melos::Vec.parse_vec(value)
|
37
|
+
array << current_instance
|
38
|
+
end
|
39
|
+
[array, buf]
|
40
|
+
end
|
41
|
+
|
42
|
+
# context here takes a hash
|
43
|
+
# returns [value, rest_of_buffer]
|
44
|
+
# value could return nil, which means predicate was not applicable
|
45
|
+
# predicate takes the context and returns true or false
|
46
|
+
def deserialize_select_elem_with_context(buf, context, predicate, type, type_param)
|
47
|
+
if predicate.(context)
|
48
|
+
deserialize_elem(buf, type, type_param)
|
49
|
+
else
|
50
|
+
[nil, buf]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def deserialize(buf)
|
56
|
+
context = []
|
57
|
+
self.class::STRUCT.each do |elem|
|
58
|
+
case elem[1]
|
59
|
+
when :select
|
60
|
+
value, buf = deserialize_select_elem_with_context(buf, context.to_h, elem[2], elem[3], elem[4])
|
61
|
+
context << [elem[0], value]
|
62
|
+
when :framed_content_auth_data
|
63
|
+
value, buf = Melos::Struct::FramedContentAuthData.new_and_rest_with_content_type(buf, context.to_h[:content].content_type)
|
64
|
+
context << [elem[0], value]
|
65
|
+
else
|
66
|
+
value, buf = deserialize_elem(buf, elem[1], elem[2])
|
67
|
+
context << [elem[0], value]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
[context, buf]
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_instance_vars(context)
|
74
|
+
context.each do |elem|
|
75
|
+
self.instance_variable_set("@#{elem[0]}", elem[1])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def deserialize_elem(buf, type, type_param)
|
80
|
+
case type
|
81
|
+
when :uint8
|
82
|
+
value = buf.byteslice(0, 1).unpack1('C')
|
83
|
+
buf = buf.byteslice(1..)
|
84
|
+
when :uint16
|
85
|
+
value = buf.byteslice(0, 2).unpack1('S>')
|
86
|
+
buf = buf.byteslice(2..)
|
87
|
+
when :uint32
|
88
|
+
value = buf.byteslice(0, 4).unpack1('L>')
|
89
|
+
buf = buf.byteslice(4..)
|
90
|
+
when :uint64
|
91
|
+
value = buf.byteslice(0, 8).unpack1('Q>')
|
92
|
+
buf = buf.byteslice(8..)
|
93
|
+
when :vec
|
94
|
+
value, buf = Melos::Vec.parse_vec(buf)
|
95
|
+
when :vec_of_type
|
96
|
+
vec, buf = Melos::Vec.parse_vec(buf)
|
97
|
+
value = []
|
98
|
+
while (vec.bytesize > 0)
|
99
|
+
current_instance, vec = deserialize_elem(vec, type_param, nil)
|
100
|
+
value << current_instance
|
101
|
+
end
|
102
|
+
when :class
|
103
|
+
value, buf = type_param.send(:new_and_rest, buf)
|
104
|
+
when :classes
|
105
|
+
# prefix, length = buf.get_prefix_and_length
|
106
|
+
# puts "#{prefix}, #{length}"
|
107
|
+
vec, buf = Melos::Vec.parse_vec(buf)
|
108
|
+
value = []
|
109
|
+
while (vec.bytesize > 0)
|
110
|
+
current_instance, vec = type_param.send(:new_and_rest, vec)
|
111
|
+
value << current_instance
|
112
|
+
end
|
113
|
+
when :optional
|
114
|
+
presence = buf.byteslice(0, 1).unpack1('C')
|
115
|
+
buf = buf.byteslice(1..)
|
116
|
+
case presence
|
117
|
+
when 0
|
118
|
+
value = nil
|
119
|
+
when 1
|
120
|
+
# as of RFC 9420, optional always takes a class
|
121
|
+
value, buf = type_param.send(:new_and_rest, buf)
|
122
|
+
end
|
123
|
+
when :opaque
|
124
|
+
value = buf.byteslice(0, type_param.to_i)
|
125
|
+
buf = buf.byteslice((type_param.to_i)..)
|
126
|
+
when :padding
|
127
|
+
value = buf
|
128
|
+
end
|
129
|
+
[value, buf]
|
130
|
+
end
|
131
|
+
|
132
|
+
# take a name and type
|
133
|
+
def serialize_elem(value, type, type_param)
|
134
|
+
case type
|
135
|
+
when :uint8
|
136
|
+
[value].pack('C')
|
137
|
+
when :uint16
|
138
|
+
[value].pack('S>')
|
139
|
+
when :uint32
|
140
|
+
[value].pack('L>')
|
141
|
+
when :uint64
|
142
|
+
[value].pack('Q>')
|
143
|
+
when :vec
|
144
|
+
Melos::Vec.from_string(value)
|
145
|
+
when :vec_of_type
|
146
|
+
Melos::Vec.from_string(value.map { serialize_elem(_1, type_param, nil) }.join)
|
147
|
+
when :class, :framed_content_auth_data
|
148
|
+
value.raw
|
149
|
+
when :classes
|
150
|
+
Melos::Vec.from_string(value.map(&:raw).join)
|
151
|
+
when :optional
|
152
|
+
if value.nil?
|
153
|
+
[0].pack('C')
|
154
|
+
else
|
155
|
+
# as of RFC 9420, optional always takes a class
|
156
|
+
[1].pack('C') + value.raw
|
157
|
+
end
|
158
|
+
when :opaque
|
159
|
+
value
|
160
|
+
when :padding
|
161
|
+
value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def serialize_select_elem(value, type)
|
166
|
+
if value.nil?
|
167
|
+
''
|
168
|
+
else
|
169
|
+
serialize_elem(value, type, nil)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
## Ratchet Tree Extension (12.4.3.3)
|
2
|
+
require_relative 'base'
|
3
|
+
require_relative 'structs'
|
4
|
+
require_relative '../vec'
|
5
|
+
require_relative '../tree'
|
6
|
+
require_relative '../crypto'
|
7
|
+
|
8
|
+
module Melos::Struct::RatchetTree
|
9
|
+
def self.parse(vec)
|
10
|
+
array, _ = new_and_rest(vec)
|
11
|
+
array
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new_and_rest(vec)
|
15
|
+
array = []
|
16
|
+
buf, rest = Melos::Vec.parse_vec(vec)
|
17
|
+
while buf.bytesize > 0
|
18
|
+
presence = buf.byteslice(0, 1).unpack1('C')
|
19
|
+
buf = buf.byteslice(1..)
|
20
|
+
case presence
|
21
|
+
when 0
|
22
|
+
array << nil
|
23
|
+
when 1
|
24
|
+
node, buf = Melos::Struct::Node.new_and_rest(buf)
|
25
|
+
array << node
|
26
|
+
end
|
27
|
+
end
|
28
|
+
[array, rest]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.raw(array)
|
32
|
+
buf = ''
|
33
|
+
array.each do |optional_node|
|
34
|
+
if optional_node.nil?
|
35
|
+
buf += [0].pack('C')
|
36
|
+
else
|
37
|
+
buf += [1].pack('C')
|
38
|
+
buf += optional_node.raw
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Melos::Vec.from_string(buf)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.tree_hash(tree, node_index, suite)
|
46
|
+
node = tree[node_index]
|
47
|
+
if Melos::Tree.leaf?(node_index)
|
48
|
+
# is a leaf node
|
49
|
+
leaf_index = node_index / 2
|
50
|
+
leaf_node_hash_input = [leaf_index].pack('L>')
|
51
|
+
if node.nil?
|
52
|
+
leaf_node_hash_input += [0].pack('C')
|
53
|
+
else
|
54
|
+
leaf_node_hash_input += [1].pack('C') + node.leaf_node.raw
|
55
|
+
end
|
56
|
+
|
57
|
+
tree_hash_input = [1].pack('C') + leaf_node_hash_input
|
58
|
+
else
|
59
|
+
# is a parent node, so calculate using ParentNodeHashInput
|
60
|
+
parent_node_hash_input = ''
|
61
|
+
if node.nil?
|
62
|
+
parent_node_hash_input += [0].pack('C')
|
63
|
+
else
|
64
|
+
parent_node_hash_input += [1].pack('C') + node.parent_node.raw
|
65
|
+
end
|
66
|
+
parent_node_hash_input += Melos::Vec.from_string(tree_hash(tree, Melos::Tree.left(node_index), suite))
|
67
|
+
parent_node_hash_input += Melos::Vec.from_string(tree_hash(tree, Melos::Tree.right(node_index), suite))
|
68
|
+
|
69
|
+
tree_hash_input = [2].pack('C') + parent_node_hash_input
|
70
|
+
end
|
71
|
+
|
72
|
+
# The RFC omits the actual definition of calculating a tree hash...
|
73
|
+
# it could totally be a ExpandWithLabel-ish thing...
|
74
|
+
Melos::Crypto.hash(suite, tree_hash_input)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.tree_hash_except(tree, node_index, unmerged_leaves, suite)
|
78
|
+
new_tree = tree.dup
|
79
|
+
unmerged_leaves.each do |leaf_index|
|
80
|
+
node_index_to_remove = leaf_index * 2
|
81
|
+
new_tree[node_index_to_remove] = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
tree_hash(new_tree, node_index, suite)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.calculate_parent_hash(tree, node_index, sibling, suite)
|
88
|
+
parent_node = tree[node_index].parent_node
|
89
|
+
sibling_hash = tree_hash_except(tree, sibling, parent_node.unmerged_leaves, suite)
|
90
|
+
Melos::Crypto.parent_hash(suite, parent_node.encryption_key, parent_node.parent_hash, sibling_hash)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.verify_parent_hash_at(tree, node_index, suite)
|
94
|
+
node = tree[node_index]
|
95
|
+
if Melos::Tree.leaf?(node_index)
|
96
|
+
false # maybe an ArgumentError, because there is no verifying a ParentHash on a leaf node
|
97
|
+
else
|
98
|
+
if node.nil?
|
99
|
+
true
|
100
|
+
else
|
101
|
+
left_index = Melos::Tree.left(node_index)
|
102
|
+
right_index = Melos::Tree.right(node_index)
|
103
|
+
|
104
|
+
# either the node at node_index is Parent-Hash Valid wrt someone in left tree or someone in right tree
|
105
|
+
has_parent_hash(tree, left_index, calculate_parent_hash(tree, node_index, right_index, suite)) || has_parent_hash(tree, right_index, calculate_parent_hash(tree, node_index, left_index, suite))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.has_parent_hash(tree, child_index, parent_hash_value)
|
111
|
+
resolutions = Melos::Tree.resolution(tree, child_index)
|
112
|
+
resolutions.each do |node_index|
|
113
|
+
if tree[node_index]&.parent_hash_in_node == parent_hash_value
|
114
|
+
# if any of the resolution of specified child has matching parent_hash_value then parent is Parent-Hash Valid wrt that child
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
return false
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.verify_parent_hash_of_tree(tree, suite)
|
122
|
+
parent_indexes = (1..((tree.count - 1) / 2)).map { _1 * 2 - 1} # this makes node_indexes of odd numbers
|
123
|
+
parent_indexes_from_bottom_to_top = parent_indexes.sort_by { Melos::Tree.level(_1) } # this sorts node_indexes based on level
|
124
|
+
parent_indexes_from_bottom_to_top.all? { verify_parent_hash_at(tree, _1, suite) } # this makes it so that nodes are evaluated from lower level to higher level
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.add_leaf_node(tree, node_to_insert)
|
128
|
+
inserted = false
|
129
|
+
inserted_node_index = 0
|
130
|
+
# if there is a blank in tree, insert there
|
131
|
+
tree.each_with_index do |node, node_index|
|
132
|
+
if Melos::Tree.leaf?(node_index)
|
133
|
+
if tree[node_index].nil?
|
134
|
+
tree[node_index] = node_to_insert
|
135
|
+
inserted = true
|
136
|
+
inserted_node_index = node_index
|
137
|
+
break
|
138
|
+
end
|
139
|
+
else
|
140
|
+
# do nothing to a parent
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# if not, extend tree
|
144
|
+
if !inserted
|
145
|
+
tree << nil
|
146
|
+
tree << node_to_insert
|
147
|
+
inserted_node_index = tree.count - 1
|
148
|
+
end
|
149
|
+
# then update unmerged list up till root
|
150
|
+
inserted_leaf_index = inserted_node_index / 2
|
151
|
+
current_node_index = inserted_node_index
|
152
|
+
while(current_node_index != Melos::Tree.root(tree.count))
|
153
|
+
if tree[current_node_index] && tree[current_node_index].node_type == 0x02
|
154
|
+
tree[current_node_index].parent_node.unmerged_leaves << inserted_leaf_index
|
155
|
+
end
|
156
|
+
current_node_index = Melos::Tree.parent(current_node_index, tree.count)
|
157
|
+
end
|
158
|
+
|
159
|
+
return inserted_leaf_index
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.update_leaf_node(tree, node_to_update, leaf_index_of_sender)
|
163
|
+
node_index = leaf_index_of_sender * 2
|
164
|
+
tree[node_index] = node_to_update
|
165
|
+
# blank the intermediate nodes along the path from sender's leaf to root
|
166
|
+
current_node_index = node_index
|
167
|
+
while(current_node_index != Melos::Tree.root(tree.count))
|
168
|
+
if tree[current_node_index] && tree[current_node_index].node_type == 0x02
|
169
|
+
tree[current_node_index] = nil
|
170
|
+
end
|
171
|
+
current_node_index = Melos::Tree.parent(current_node_index, tree.count)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.remove_leaf_node(tree, leaf_index_to_remove)
|
176
|
+
node_index = leaf_index_to_remove * 2
|
177
|
+
tree[node_index] = nil
|
178
|
+
# blank the intermediate nodes along the path from sender's leaf to root
|
179
|
+
current_node_index = node_index
|
180
|
+
while(current_node_index != Melos::Tree.root(tree.count))
|
181
|
+
if tree[current_node_index] && tree[current_node_index].node_type == 0x02
|
182
|
+
tree[current_node_index] = nil
|
183
|
+
end
|
184
|
+
current_node_index = Melos::Tree.parent(current_node_index, tree.count)
|
185
|
+
end
|
186
|
+
# then truncate tree
|
187
|
+
Melos::Tree.truncate!(tree)
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.root_tree_hash(suite, tree)
|
191
|
+
root_index = Melos::Tree.root(Melos::Tree.n_leaves(tree))
|
192
|
+
tree_hash(tree, root_index, suite)
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.merge_update_path(suite, ratchet_tree, leaf_index, update_path)
|
196
|
+
node_index_of_leaf = leaf_index * 2
|
197
|
+
filtered_direct_path = Melos::Tree.filtered_direct_path(ratchet_tree, node_index_of_leaf)
|
198
|
+
nodes_from_update_path = update_path.nodes
|
199
|
+
|
200
|
+
parent_hashes = calculate_parent_hashes(suite, ratchet_tree, leaf_index, update_path.nodes)
|
201
|
+
# update parent nodes on path
|
202
|
+
filtered_direct_path.each_with_index do |node_index, path_index|
|
203
|
+
parent_node = Melos::Struct::ParentNode.create(
|
204
|
+
encryption_key: nodes_from_update_path[path_index].encryption_key,
|
205
|
+
parent_hash: parent_hashes[path_index + 1],
|
206
|
+
unmerged_leaves: []
|
207
|
+
)
|
208
|
+
node = Melos::Struct::Node.new_parent_node(parent_node)
|
209
|
+
ratchet_tree[node_index] = node
|
210
|
+
end
|
211
|
+
# update leaf
|
212
|
+
node = Melos::Struct::Node.new_leaf_node(update_path.leaf_node)
|
213
|
+
ratchet_tree[node_index_of_leaf] = node
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.calculate_parent_hashes(suite, ratchet_tree, leaf_index_from, update_path_nodes)
|
217
|
+
hashes = []
|
218
|
+
filtered_direct_path = Melos::Tree.filtered_direct_path(ratchet_tree, leaf_index_from * 2)
|
219
|
+
# count down from root, calculate parent hash
|
220
|
+
calculated_parent_hash = ""
|
221
|
+
# node_index = Melos::Tree.root(Melos::Tree.n_leaves(ratchet_tree))
|
222
|
+
# puts "fdp count: #{filtered_direct_path.count}"
|
223
|
+
# puts "update path count: #{nodes_from_update_path.count}"
|
224
|
+
hashes[filtered_direct_path.count] = ''
|
225
|
+
(filtered_direct_path.count - 1).downto(0) do |path_index|
|
226
|
+
node_index = filtered_direct_path[path_index]
|
227
|
+
leaf_node_index = leaf_index_from * 2
|
228
|
+
sibling_node_index = Melos::Tree.sibling_from_leaf(leaf_node_index, node_index, Melos::Tree.n_leaves(ratchet_tree))
|
229
|
+
encryption_key = update_path_nodes[path_index].encryption_key
|
230
|
+
sibling_hash = Melos::Struct::RatchetTree.tree_hash(ratchet_tree, sibling_node_index, suite)
|
231
|
+
calculated_parent_hash = Melos::Crypto.parent_hash(suite, encryption_key, calculated_parent_hash, sibling_hash)
|
232
|
+
hashes[path_index] = calculated_parent_hash
|
233
|
+
end
|
234
|
+
|
235
|
+
hashes
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.decrypt_path_secret(suite, ratchet_tree, encryption_priv_tree, update_path, sender_leaf_index, receiver_leaf_index, group_context, leaves_to_remove = [])
|
239
|
+
receiver_node_index = receiver_leaf_index * 2
|
240
|
+
|
241
|
+
filtered_direct_path = Melos::Tree.filtered_direct_path(ratchet_tree, sender_leaf_index * 2)
|
242
|
+
# puts "filtered direct path: #{filtered_direct_path}"
|
243
|
+
raise ArgumentError.new('malformed update path') unless filtered_direct_path.count == update_path.nodes.count
|
244
|
+
overlap_node = Melos::Tree.overlap_with_filtered_direct_path(receiver_node_index, filtered_direct_path, Melos::Tree.n_leaves(ratchet_tree))
|
245
|
+
# puts "overlap node: #{overlap_node}"
|
246
|
+
overlap_index = filtered_direct_path.find_index { _1 == overlap_node }
|
247
|
+
overlap_node_index = filtered_direct_path[overlap_index]
|
248
|
+
# puts "overlap index: #{overlap_index}"
|
249
|
+
copath_node_index = Melos::Tree.copath_nodes_of_filtered_direct_path(ratchet_tree, sender_leaf_index * 2)[overlap_index]
|
250
|
+
# puts "copath node: #{copath_node_index}"
|
251
|
+
resolution_of_copath_node = Melos::Tree.resolution(ratchet_tree, copath_node_index)
|
252
|
+
resolution_of_copath_node = resolution_of_copath_node - leaves_to_remove.map{ _1 * 2 } # leaf index -> node index
|
253
|
+
# puts "resolution: #{resolution_of_copath_node}"
|
254
|
+
|
255
|
+
priv_key = nil
|
256
|
+
priv_index = nil
|
257
|
+
resolution_of_copath_node.each_with_index do |res, idx|
|
258
|
+
if encryption_priv_tree[res]
|
259
|
+
priv_key = encryption_priv_tree[res]
|
260
|
+
priv_index = idx
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
if priv_key.nil?
|
265
|
+
raise ArgumentError.new("priv key not found in tree (sender leaf index: #{sender_leaf_index})")
|
266
|
+
end
|
267
|
+
target_update_path_node = update_path.nodes[overlap_index]
|
268
|
+
target_encrypted_path_secret = target_update_path_node.encrypted_path_secret[priv_index]
|
269
|
+
raise ArgumentError.new('# of resolution of copath node does not match with # of encrypted path secrets') unless target_update_path_node.encrypted_path_secret.count == resolution_of_copath_node.count
|
270
|
+
|
271
|
+
# puts "priv_key: #{to_hex priv_key}"
|
272
|
+
path_secret = Melos::Crypto.decrypt_with_label(suite, priv_key, "UpdatePathNode", group_context.raw, target_encrypted_path_secret.kem_output, target_encrypted_path_secret.ciphertext)
|
273
|
+
|
274
|
+
# place private keys based on path secret
|
275
|
+
update_encryption_priv_tree(suite, ratchet_tree, encryption_priv_tree, overlap_node_index, path_secret)
|
276
|
+
|
277
|
+
path_secret
|
278
|
+
end
|
279
|
+
|
280
|
+
def self.update_encryption_priv_tree(suite, ratchet_tree, encryption_priv_tree, start, path_secret)
|
281
|
+
filtered_direct_path = Melos::Tree.filtered_direct_path(ratchet_tree, start)
|
282
|
+
secret = path_secret
|
283
|
+
filtered_direct_path = [start] + filtered_direct_path
|
284
|
+
|
285
|
+
filtered_direct_path.each do |n_i|
|
286
|
+
node_secret = Melos::Crypto.derive_secret(suite, secret, "node")
|
287
|
+
encryption_priv, encryption_pub = Melos::Crypto.derive_key_pair(suite, node_secret)
|
288
|
+
encryption_priv_tree[n_i] = encryption_priv
|
289
|
+
# puts "n_i #{n_i}: ps: #{to_hex secret} -> ns: #{to_hex node_secret} -> pub: #{to_hex encryption_pub}"
|
290
|
+
# p Melos::Crypto.encapsulation_key_pair_corresponds?(suite, encryption_priv, ratchet_tree[n_i].public_encryption_key)
|
291
|
+
secret = Melos::Crypto.derive_secret(suite, secret, "path")
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def self.calculate_commit_secret(suite, ratchet_tree, update_path, sender_leaf_index, receiver_leaf_index, path_secret)
|
296
|
+
receiver_node_index = receiver_leaf_index * 2
|
297
|
+
|
298
|
+
filtered_direct_path = Melos::Tree.filtered_direct_path(ratchet_tree, sender_leaf_index * 2)
|
299
|
+
raise ArgumentError.new('malformed update path') unless filtered_direct_path.count == update_path.nodes.count
|
300
|
+
overlap_node = Melos::Tree.overlap_with_filtered_direct_path(receiver_node_index, filtered_direct_path, Melos::Tree.n_leaves(ratchet_tree))
|
301
|
+
overlap_index = filtered_direct_path.find_index { _1 == overlap_node}
|
302
|
+
|
303
|
+
path_secret_n = path_secret
|
304
|
+
index = overlap_index
|
305
|
+
while filtered_direct_path[index] != Melos::Tree.root(Melos::Tree.n_leaves(ratchet_tree))
|
306
|
+
path_secret_n = Melos::Crypto.derive_secret(suite, path_secret_n, "path")
|
307
|
+
index += 1
|
308
|
+
end
|
309
|
+
Melos::Crypto.derive_secret(suite, path_secret_n, "path") # commit secret is node's path_secret +1
|
310
|
+
end
|
311
|
+
|
312
|
+
# just a test function
|
313
|
+
def self.dump_tree(tree)
|
314
|
+
tree.each_with_index do |node, index|
|
315
|
+
if node.nil?
|
316
|
+
puts "#{index}, nil"
|
317
|
+
elsif node.parent_node
|
318
|
+
puts "#{index}, PN (#{Melos::Util.to_hex(node.public_encryption_key)[0, 8]}) - #{node.parent_node.unmerged_leaves}"
|
319
|
+
else
|
320
|
+
puts "#{index}, LN (#{Melos::Util.to_hex(node.public_encryption_key)[0, 8]})"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|