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.
data/lib/melos/tree.rb ADDED
@@ -0,0 +1,265 @@
1
+ class Melos::Tree
2
+ attr_accessor :array, :leaf_count
3
+ # attr_reader @array
4
+
5
+ def initialize
6
+ @array = []
7
+ @leaf_count = 0
8
+ end
9
+
10
+ def self.empty_tree(n_leaves)
11
+ instance = self.allocate
12
+ instance.leaf_count = n_leaves
13
+ instance.array = Array.new(node_width(n_leaves))
14
+ instance
15
+ end
16
+
17
+ def add(val)
18
+ raise ArgumentError.new('Cannot add nil element') if val.nil?
19
+ if @leaf_count == 0
20
+ # initialize tree with one node
21
+ @array = [val]
22
+ @leaf_count = 1
23
+ else
24
+ # find leftmost empty leaf
25
+ extend_tree = true
26
+ for k in 0 .. (@leaf_count - 1) do
27
+ if @array[k * 2].nil?
28
+ @array[k * 2] = val
29
+ extend_tree = false
30
+ break
31
+ end
32
+ end
33
+ if extend_tree
34
+ # if tree is full, extend
35
+ @leaf_count = @leaf_count * 2
36
+ for k in 0 .. @leaf_count - 2
37
+ @array[@leaf_count + k] = nil
38
+ end
39
+ @array[@leaf_count] = val
40
+ end
41
+ end
42
+ @array
43
+ end
44
+
45
+ def remove_leaf(leaf_idx)
46
+ raise ArgumentError.new('Cannot remove from empty tree') if @leaf_count == 0
47
+ remove_node(leaf_idx * 2)
48
+ # then if rigbt half of the tree is empty, truncate tree
49
+ # q: do we recursively shrink tree?
50
+ right_tree_empty = true
51
+ for i in 0 .. (@leaf_count / 2) - 1 do
52
+ if !@array[@leaf_count + 2 * i].nil?
53
+ right_tree_empty = false
54
+ break
55
+ end
56
+ end
57
+ if right_tree_empty
58
+ @array = @array.first(@leaf_count - 1)
59
+ @leaf_count = @leaf_count / 2
60
+ end
61
+ @array
62
+ end
63
+
64
+ def remove_node(node_idx)
65
+ @array[node_idx] = nil
66
+ end
67
+
68
+ def root
69
+ self.class.root(@leaf_count)
70
+ end
71
+
72
+ def leaf_at(leaf_index)
73
+ @array[leaf_index * 2]
74
+ end
75
+
76
+ class << self
77
+ def n_leaves(tree)
78
+ (tree.size + 1) / 2
79
+ end
80
+
81
+ def log2(x)
82
+ if x == 0
83
+ return 0
84
+ end
85
+ k = 0
86
+ while (x >> k) > 0
87
+ k += 1
88
+ end
89
+ return (k - 1)
90
+ end
91
+
92
+ def level(x)
93
+ if x & 0x01 == 0
94
+ return 0
95
+ end
96
+ k = 0
97
+ while ((x >> k) & 0x01) == 1
98
+ k += 1
99
+ end
100
+ return k
101
+ end
102
+
103
+ def node_width(n)
104
+ if n == 0
105
+ 0
106
+ else
107
+ 2 * (n - 1) + 1
108
+ end
109
+ end
110
+
111
+ def root(n_leaves)
112
+ w = node_width(n_leaves)
113
+
114
+ (1 << log2(w)) - 1
115
+ end
116
+
117
+ def left(x)
118
+ k = level(x)
119
+ raise ArgumentError.new('leaf node has no children') if k == 0
120
+ x ^ (0x01 << (k - 1))
121
+ end
122
+
123
+ def right(x)
124
+ k = level(x)
125
+ raise ArgumentError.new('leaf node has no children') if k == 0
126
+ x ^ (0x03 << (k - 1))
127
+ end
128
+
129
+ def parent(x, n_leaves)
130
+ raise ArgumentError.new('root node has no parent') if x == root(n_leaves)
131
+ k = level(x)
132
+ b = (x >> (k + 1)) & 0x01
133
+ (x | (1 << k)) ^ (b << (k + 1))
134
+ end
135
+
136
+ def sibling(x, n_leaves)
137
+ p = parent(x, n_leaves)
138
+ if x < p
139
+ right(p)
140
+ else
141
+ left(p)
142
+ end
143
+ end
144
+
145
+ # used for determining sibling of a node from an UpdatePath
146
+ # i.e. the node (sibling) on the copath side
147
+ # takes two node indexes and the # of leaves
148
+ def sibling_from_leaf(x_of_leaf, x_of_ancestor, n_leaves)
149
+ dp = direct_path(x_of_leaf, n_leaves)
150
+ dp_including_self = [x_of_leaf] + dp
151
+ raise ArgumentError.new('specified node is not an ancestor of leaf') unless dp.include?(x_of_ancestor)
152
+ l = left(x_of_ancestor)
153
+ r = right(x_of_ancestor)
154
+ # if direct path (including self) includes left side, return right, else return left
155
+ dp_including_self.include?(l) ? r : l
156
+ end
157
+
158
+ def direct_path(x, n_leaves)
159
+ r = root(n_leaves)
160
+ return [] if x == r
161
+ d = []
162
+ while x != r
163
+ x = parent(x, n_leaves)
164
+ d << x
165
+ end
166
+ return d
167
+ end
168
+
169
+ def copath(x, n_leaves)
170
+ return [] if x == root(n_leaves)
171
+
172
+ d = direct_path(x, n_leaves)
173
+ d.insert(0, x)
174
+ d.pop
175
+
176
+ d.map { sibling(_1, n_leaves) }
177
+ end
178
+
179
+ def common_ancestor_semantic(x, y, n)
180
+ dx = Set.new([x]) | Set.new(direct_path(x, n))
181
+ dy = Set.new([y]) | Set.new(direct_path(y, n))
182
+ dxy = dx & dy
183
+ if dxy.size == 0
184
+ raise ArgumentError.new('Failed to find common ancestor')
185
+ end
186
+
187
+ dxy.min { level(_1) }
188
+ end
189
+
190
+ def overlap_with_filtered_direct_path(x, filtered_direct_path, n)
191
+ dx = Set.new([x]) | Set.new(direct_path(x, n))
192
+ df = Set.new(filtered_direct_path)
193
+ dxf = dx & df
194
+ if dxf.size == 0
195
+ raise ArgumentError.new('Failed to find overlap')
196
+ end
197
+
198
+ dxf.min { level(_1) }
199
+ end
200
+
201
+ def leaf?(node_index)
202
+ node_index % 2 == 0
203
+ end
204
+
205
+ def truncate!(tree)
206
+ root = root(n_leaves(tree))
207
+ if tree[(root + 1)..]&.all?(nil)
208
+ tree.slice!(root..) # keep left half of tree
209
+ truncate!(tree) # then attempt to truncate again
210
+ end
211
+ # right half of tree has an element, so finish
212
+ end
213
+
214
+ def filtered_direct_path(tree, node_index)
215
+ n_l = n_leaves(tree)
216
+ direct_path(node_index, n_l).reject { resolution(tree, sibling_from_leaf(node_index, _1, n_l)) == [] }
217
+ end
218
+
219
+ def copath_nodes_of_filtered_direct_path(tree, node_index)
220
+ filtered_direct_path(tree, node_index).map do |a|
221
+ sibling_from_leaf(node_index, a, n_leaves(tree))
222
+ end
223
+ end
224
+
225
+ def resolution(tree, node_index)
226
+ node = tree[node_index]
227
+ if node.nil?
228
+ if Melos::Tree.leaf?(node_index)
229
+ # The resolution of a blank leaf node is the empty list.
230
+ []
231
+ else
232
+ # The resolution of a blank intermediate node is the result of concatenating the resolution of its left child with the resolution of its right child, in that order.
233
+ resolution(tree, Melos::Tree.left(node_index)) + resolution(tree, Melos::Tree.right(node_index))
234
+ end
235
+ else
236
+ # The resolution of a non-blank node comprises the node itself, followed by its list of unmerged leaves, if any.
237
+ if node.parent_node
238
+ [node_index] + node.parent_node.unmerged_leaves.map { _1 * 2} # convert leaf index to node index
239
+ else
240
+ [node_index]
241
+ end
242
+ end
243
+ end
244
+
245
+ # def common_ancestor_direct(x, y)
246
+ # lx = level(x) + 1
247
+ # ly = level(y) + 1
248
+ # if (lx <= ly) && (x >> ly == y >> ly)
249
+ # return y
250
+ # elsif (ly <= lx) && (x >> lx == y >> lx)
251
+ # return x
252
+ # end
253
+
254
+ # xn = x
255
+ # yn = y
256
+ # k = 0
257
+ # while xn != yn
258
+ # xn = xn >> 1
259
+ # yn = yn >> 1
260
+ # k + 1
261
+ # end
262
+ # (xn << k) + (1 << (k - 1)) - 1
263
+ # end
264
+ end
265
+ end
data/lib/melos/util.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Melos::Util
2
+ extend self
3
+
4
+ def from_hex(hex)
5
+ [hex].pack('H*')
6
+ end
7
+
8
+ def to_hex(bin)
9
+ bin.unpack1('H*')
10
+ end
11
+ end
data/lib/melos/vec.rb ADDED
@@ -0,0 +1,89 @@
1
+ module Melos::Vec
2
+ extend self
3
+
4
+ def read_varint(data)
5
+ byte = 0
6
+ v = data[byte].ord
7
+ prefix = v >> 6
8
+ length = 1 << prefix
9
+
10
+ v = v & 0x3f
11
+ (length - 1).times do
12
+ byte += 1
13
+ v = (v << 8) + data[byte].ord
14
+ end
15
+
16
+ return v
17
+ end
18
+
19
+ def write_varint(len)
20
+ header = []
21
+ case len
22
+ when 0..63
23
+ header[0] = len
24
+ when 64..16383
25
+ header[0] = (1 << 6) | ((len & 0x3f00) >> 8)
26
+ header[1] = len & 0x00ff
27
+ when 16384..1073741823
28
+ header[0] = (2 << 6) | ((len & 0x3f000000) >> 24)
29
+ header[1] = ((len & 0x00ff0000) >> 16)
30
+ header[2] = ((len & 0x0000ff00) >> 8)
31
+ header[3] = len & 0x000000ff
32
+ else
33
+ raise ArgumentError.new('too long to be encoded in variable length vector')
34
+ end
35
+
36
+ header.pack('C*')
37
+ end
38
+
39
+ def from_string(str) # = to_vec
40
+ write_varint(str.bytesize) + str
41
+ end
42
+
43
+ def get_prefix_and_length(str)
44
+ prefix = str[0].ord >> 6
45
+ length = read_varint(str)
46
+
47
+ [prefix, length]
48
+ end
49
+
50
+ def parse_vec(vec_as_string)
51
+ prefix = vec_as_string[0].ord >> 6
52
+ length = read_varint(vec_as_string)
53
+ case prefix
54
+ when 0
55
+ str = vec_as_string.byteslice(1, length)
56
+ rest = vec_as_string.byteslice((1 + length)..)
57
+ when 1
58
+ str = vec_as_string.byteslice(2, length)
59
+ rest = vec_as_string.byteslice((2 + length)..)
60
+ when 2
61
+ str = vec_as_string[4, length]
62
+ rest = vec_as_string[(4 + length)..]
63
+ else
64
+ raise ArgumentError.new('invalid header')
65
+ end
66
+
67
+ [str, rest]
68
+ end
69
+
70
+ def get_first_vec(vec_as_string)
71
+ prefix = vec[0].ord >> 6
72
+ length = read_varint(vec_as_string)
73
+ case prefix
74
+ when 0
75
+ first_vec = vec_as_string[0, 1 + length]
76
+ rest = vec_as_string[(1 + length)..]
77
+ when 1
78
+ first_vec = vec_as_string[0, 2 + length]
79
+ rest = vec_as_string[(2 + length)..]
80
+ when 2
81
+ first_vec = vec_as_string[0, 4 + length]
82
+ rest = vec_as_string[(4 + length)..]
83
+ else
84
+ raise ArgumentError.new('invalid header')
85
+ end
86
+
87
+ [first_vec, rest]
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Melos
4
+ VERSION = "0.0.1"
5
+ end
data/lib/melos.rb ADDED
@@ -0,0 +1,15 @@
1
+ module Melos
2
+ MLS_VERSION = 0x01 # mls10
3
+ end
4
+
5
+ require_relative 'melos/version'
6
+ require_relative 'melos/vec'
7
+ require_relative 'melos/tree'
8
+ require_relative 'melos/util'
9
+ require_relative 'melos/constants'
10
+ require_relative 'melos/key_schedule'
11
+ require_relative 'melos/psk'
12
+ require_relative 'melos/secret_tree'
13
+ require_relative 'melos/struct/base'
14
+ require_relative 'melos/struct/structs'
15
+ require_relative 'melos/struct/ratchet_tree'
data/sig/melos.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Melos
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end