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