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.
@@ -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