rb_mumble_protocol 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Cargo.lock +385 -0
- data/Cargo.toml +7 -0
- data/ext/rb_mumble_protocol/Cargo.toml +14 -0
- data/ext/rb_mumble_protocol/src/crypt_state.rs +456 -0
- data/ext/rb_mumble_protocol/src/lib.rs +113 -0
- data/lib/rb_mumble_protocol/crypt_state.rb +6 -0
- data/lib/rb_mumble_protocol/version.rb +5 -0
- data/lib/rb_mumble_protocol.rb +10 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3e1daa7a8599ad332f998313a54453e096eb3cdb0a2e091028bc33e179945168
|
4
|
+
data.tar.gz: 24859982a247cdf5f3d9bfc03d163db347995aa9b7642650043737749ba0d43e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d7f2032dd233a3714bca92ca14f2918b65bd89e1891f68881c720dcd741ea16764e589a72ab4fc293257424a6482420f252460cb0ae4092408f02eccc5def763
|
7
|
+
data.tar.gz: 31c1e2b93dbb4834023c98272090248b4633486e5a8fe7eeb48e6a6c2d09a207d5ad95cb4bf16588370d1d7da1581b31ffe182b03049b3761c4538881fff30d3
|
data/Cargo.lock
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
2
|
+
# It is not intended for manual editing.
|
3
|
+
version = 3
|
4
|
+
|
5
|
+
[[package]]
|
6
|
+
name = "aho-corasick"
|
7
|
+
version = "1.0.2"
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
9
|
+
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
|
10
|
+
dependencies = [
|
11
|
+
"memchr",
|
12
|
+
]
|
13
|
+
|
14
|
+
[[package]]
|
15
|
+
name = "bindgen"
|
16
|
+
version = "0.62.0"
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
18
|
+
checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722"
|
19
|
+
dependencies = [
|
20
|
+
"bitflags",
|
21
|
+
"cexpr",
|
22
|
+
"clang-sys",
|
23
|
+
"lazy_static",
|
24
|
+
"lazycell",
|
25
|
+
"peeking_take_while",
|
26
|
+
"proc-macro2",
|
27
|
+
"quote",
|
28
|
+
"regex",
|
29
|
+
"rustc-hash",
|
30
|
+
"shlex",
|
31
|
+
"syn 1.0.109",
|
32
|
+
]
|
33
|
+
|
34
|
+
[[package]]
|
35
|
+
name = "bitflags"
|
36
|
+
version = "1.3.2"
|
37
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
38
|
+
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
39
|
+
|
40
|
+
[[package]]
|
41
|
+
name = "bytes"
|
42
|
+
version = "1.4.0"
|
43
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
44
|
+
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
45
|
+
|
46
|
+
[[package]]
|
47
|
+
name = "cc"
|
48
|
+
version = "1.0.79"
|
49
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
50
|
+
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
51
|
+
|
52
|
+
[[package]]
|
53
|
+
name = "cexpr"
|
54
|
+
version = "0.6.0"
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
56
|
+
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
57
|
+
dependencies = [
|
58
|
+
"nom",
|
59
|
+
]
|
60
|
+
|
61
|
+
[[package]]
|
62
|
+
name = "cfg-if"
|
63
|
+
version = "1.0.0"
|
64
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
65
|
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
66
|
+
|
67
|
+
[[package]]
|
68
|
+
name = "clang-sys"
|
69
|
+
version = "1.6.1"
|
70
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
71
|
+
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
|
72
|
+
dependencies = [
|
73
|
+
"glob",
|
74
|
+
"libc",
|
75
|
+
"libloading",
|
76
|
+
]
|
77
|
+
|
78
|
+
[[package]]
|
79
|
+
name = "foreign-types"
|
80
|
+
version = "0.3.2"
|
81
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
82
|
+
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
83
|
+
dependencies = [
|
84
|
+
"foreign-types-shared",
|
85
|
+
]
|
86
|
+
|
87
|
+
[[package]]
|
88
|
+
name = "foreign-types-shared"
|
89
|
+
version = "0.1.1"
|
90
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
91
|
+
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
92
|
+
|
93
|
+
[[package]]
|
94
|
+
name = "glob"
|
95
|
+
version = "0.3.1"
|
96
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
97
|
+
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
98
|
+
|
99
|
+
[[package]]
|
100
|
+
name = "lazy_static"
|
101
|
+
version = "1.4.0"
|
102
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
103
|
+
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
104
|
+
|
105
|
+
[[package]]
|
106
|
+
name = "lazycell"
|
107
|
+
version = "1.3.0"
|
108
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
109
|
+
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
110
|
+
|
111
|
+
[[package]]
|
112
|
+
name = "libc"
|
113
|
+
version = "0.2.147"
|
114
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
115
|
+
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
116
|
+
|
117
|
+
[[package]]
|
118
|
+
name = "libloading"
|
119
|
+
version = "0.7.4"
|
120
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
121
|
+
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
122
|
+
dependencies = [
|
123
|
+
"cfg-if",
|
124
|
+
"winapi",
|
125
|
+
]
|
126
|
+
|
127
|
+
[[package]]
|
128
|
+
name = "magnus"
|
129
|
+
version = "0.4.4"
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
131
|
+
checksum = "fc87660cd7daa49fddbfd524c836de54d5c927d520cd163f43700c5087c57d6c"
|
132
|
+
dependencies = [
|
133
|
+
"magnus-macros",
|
134
|
+
"rb-sys",
|
135
|
+
"rb-sys-env",
|
136
|
+
]
|
137
|
+
|
138
|
+
[[package]]
|
139
|
+
name = "magnus-macros"
|
140
|
+
version = "0.3.0"
|
141
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
142
|
+
checksum = "206cb23bfeea05180c97522ef6a3e52a4eb17b0ed2f30ee3ca9c4f994d2378ae"
|
143
|
+
dependencies = [
|
144
|
+
"proc-macro2",
|
145
|
+
"quote",
|
146
|
+
"syn 1.0.109",
|
147
|
+
]
|
148
|
+
|
149
|
+
[[package]]
|
150
|
+
name = "memchr"
|
151
|
+
version = "2.5.0"
|
152
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
153
|
+
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
154
|
+
|
155
|
+
[[package]]
|
156
|
+
name = "minimal-lexical"
|
157
|
+
version = "0.2.1"
|
158
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
159
|
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
160
|
+
|
161
|
+
[[package]]
|
162
|
+
name = "nom"
|
163
|
+
version = "7.1.3"
|
164
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
165
|
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
166
|
+
dependencies = [
|
167
|
+
"memchr",
|
168
|
+
"minimal-lexical",
|
169
|
+
]
|
170
|
+
|
171
|
+
[[package]]
|
172
|
+
name = "once_cell"
|
173
|
+
version = "1.18.0"
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
175
|
+
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
176
|
+
|
177
|
+
[[package]]
|
178
|
+
name = "openssl"
|
179
|
+
version = "0.10.55"
|
180
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
181
|
+
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
|
182
|
+
dependencies = [
|
183
|
+
"bitflags",
|
184
|
+
"cfg-if",
|
185
|
+
"foreign-types",
|
186
|
+
"libc",
|
187
|
+
"once_cell",
|
188
|
+
"openssl-macros",
|
189
|
+
"openssl-sys",
|
190
|
+
]
|
191
|
+
|
192
|
+
[[package]]
|
193
|
+
name = "openssl-macros"
|
194
|
+
version = "0.1.1"
|
195
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
196
|
+
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
197
|
+
dependencies = [
|
198
|
+
"proc-macro2",
|
199
|
+
"quote",
|
200
|
+
"syn 2.0.27",
|
201
|
+
]
|
202
|
+
|
203
|
+
[[package]]
|
204
|
+
name = "openssl-sys"
|
205
|
+
version = "0.9.90"
|
206
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
207
|
+
checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
|
208
|
+
dependencies = [
|
209
|
+
"cc",
|
210
|
+
"libc",
|
211
|
+
"pkg-config",
|
212
|
+
"vcpkg",
|
213
|
+
]
|
214
|
+
|
215
|
+
[[package]]
|
216
|
+
name = "peeking_take_while"
|
217
|
+
version = "0.1.2"
|
218
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
219
|
+
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
220
|
+
|
221
|
+
[[package]]
|
222
|
+
name = "pkg-config"
|
223
|
+
version = "0.3.27"
|
224
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
225
|
+
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
226
|
+
|
227
|
+
[[package]]
|
228
|
+
name = "proc-macro2"
|
229
|
+
version = "1.0.66"
|
230
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
231
|
+
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
232
|
+
dependencies = [
|
233
|
+
"unicode-ident",
|
234
|
+
]
|
235
|
+
|
236
|
+
[[package]]
|
237
|
+
name = "quote"
|
238
|
+
version = "1.0.31"
|
239
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
240
|
+
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
|
241
|
+
dependencies = [
|
242
|
+
"proc-macro2",
|
243
|
+
]
|
244
|
+
|
245
|
+
[[package]]
|
246
|
+
name = "rb-sys"
|
247
|
+
version = "0.9.79"
|
248
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
249
|
+
checksum = "939fb78db3e4f26665c1d4c7b91ca66d3578335a19aba552d4a6445811d07072"
|
250
|
+
dependencies = [
|
251
|
+
"rb-sys-build",
|
252
|
+
]
|
253
|
+
|
254
|
+
[[package]]
|
255
|
+
name = "rb-sys-build"
|
256
|
+
version = "0.9.79"
|
257
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
258
|
+
checksum = "335a95eb0420d52fa94ef12019df3c2c250c6b19cbb3c60bd05cb7e9c362072c"
|
259
|
+
dependencies = [
|
260
|
+
"bindgen",
|
261
|
+
"lazy_static",
|
262
|
+
"proc-macro2",
|
263
|
+
"quote",
|
264
|
+
"regex",
|
265
|
+
"shell-words",
|
266
|
+
"syn 1.0.109",
|
267
|
+
]
|
268
|
+
|
269
|
+
[[package]]
|
270
|
+
name = "rb-sys-env"
|
271
|
+
version = "0.1.2"
|
272
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
273
|
+
checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb"
|
274
|
+
|
275
|
+
[[package]]
|
276
|
+
name = "rb_mumble_protocol"
|
277
|
+
version = "0.1.0"
|
278
|
+
dependencies = [
|
279
|
+
"bytes",
|
280
|
+
"magnus",
|
281
|
+
"openssl",
|
282
|
+
]
|
283
|
+
|
284
|
+
[[package]]
|
285
|
+
name = "regex"
|
286
|
+
version = "1.9.1"
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
288
|
+
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
289
|
+
dependencies = [
|
290
|
+
"aho-corasick",
|
291
|
+
"memchr",
|
292
|
+
"regex-automata",
|
293
|
+
"regex-syntax",
|
294
|
+
]
|
295
|
+
|
296
|
+
[[package]]
|
297
|
+
name = "regex-automata"
|
298
|
+
version = "0.3.3"
|
299
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
300
|
+
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
|
301
|
+
dependencies = [
|
302
|
+
"aho-corasick",
|
303
|
+
"memchr",
|
304
|
+
"regex-syntax",
|
305
|
+
]
|
306
|
+
|
307
|
+
[[package]]
|
308
|
+
name = "regex-syntax"
|
309
|
+
version = "0.7.4"
|
310
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
311
|
+
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
312
|
+
|
313
|
+
[[package]]
|
314
|
+
name = "rustc-hash"
|
315
|
+
version = "1.1.0"
|
316
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
317
|
+
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
318
|
+
|
319
|
+
[[package]]
|
320
|
+
name = "shell-words"
|
321
|
+
version = "1.1.0"
|
322
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
323
|
+
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
324
|
+
|
325
|
+
[[package]]
|
326
|
+
name = "shlex"
|
327
|
+
version = "1.1.0"
|
328
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
329
|
+
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
330
|
+
|
331
|
+
[[package]]
|
332
|
+
name = "syn"
|
333
|
+
version = "1.0.109"
|
334
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
335
|
+
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
336
|
+
dependencies = [
|
337
|
+
"proc-macro2",
|
338
|
+
"quote",
|
339
|
+
"unicode-ident",
|
340
|
+
]
|
341
|
+
|
342
|
+
[[package]]
|
343
|
+
name = "syn"
|
344
|
+
version = "2.0.27"
|
345
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
346
|
+
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
|
347
|
+
dependencies = [
|
348
|
+
"proc-macro2",
|
349
|
+
"quote",
|
350
|
+
"unicode-ident",
|
351
|
+
]
|
352
|
+
|
353
|
+
[[package]]
|
354
|
+
name = "unicode-ident"
|
355
|
+
version = "1.0.11"
|
356
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
357
|
+
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
358
|
+
|
359
|
+
[[package]]
|
360
|
+
name = "vcpkg"
|
361
|
+
version = "0.2.15"
|
362
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
363
|
+
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
364
|
+
|
365
|
+
[[package]]
|
366
|
+
name = "winapi"
|
367
|
+
version = "0.3.9"
|
368
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
369
|
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
370
|
+
dependencies = [
|
371
|
+
"winapi-i686-pc-windows-gnu",
|
372
|
+
"winapi-x86_64-pc-windows-gnu",
|
373
|
+
]
|
374
|
+
|
375
|
+
[[package]]
|
376
|
+
name = "winapi-i686-pc-windows-gnu"
|
377
|
+
version = "0.4.0"
|
378
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
379
|
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
380
|
+
|
381
|
+
[[package]]
|
382
|
+
name = "winapi-x86_64-pc-windows-gnu"
|
383
|
+
version = "0.4.0"
|
384
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
385
|
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
data/Cargo.toml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
[package]
|
2
|
+
name = "rb_mumble_protocol"
|
3
|
+
version = "0.1.0"
|
4
|
+
edition = "2021"
|
5
|
+
authors = ["Mikhail Odebe <derpiranha@gmail.com>"]
|
6
|
+
publish = false
|
7
|
+
|
8
|
+
[lib]
|
9
|
+
crate-type = ["cdylib"]
|
10
|
+
|
11
|
+
[dependencies]
|
12
|
+
magnus = { version = "0.4" }
|
13
|
+
bytes = "1.0"
|
14
|
+
openssl = { version = "0.10" }
|
@@ -0,0 +1,456 @@
|
|
1
|
+
//! Implementation of the cryptography used for Mumble's voice channel
|
2
|
+
|
3
|
+
use bytes::BytesMut;
|
4
|
+
use openssl::memcmp;
|
5
|
+
use openssl::rand::rand_bytes;
|
6
|
+
|
7
|
+
/// Maximum size of an encrypted Mumble packet.
|
8
|
+
/// Note that larger packets can be produced if there is sufficient voice data in one packet but
|
9
|
+
/// there's no guarantee that the remote end will not just drop it.
|
10
|
+
pub const MAX_PACKET_SIZE: usize = 1024;
|
11
|
+
/// Size in bytes of the AES key used in `CryptState`.
|
12
|
+
pub const KEY_SIZE: usize = 16;
|
13
|
+
/// Size in bytes of blocks for the AES primitive.
|
14
|
+
pub const BLOCK_SIZE: usize = std::mem::size_of::<u128>();
|
15
|
+
|
16
|
+
/// Implements OCB2-AES128 for encryption and authentication of the voice packets
|
17
|
+
/// when transmitted over UDP.
|
18
|
+
/// Also provides statistics about good, late and lost packets.
|
19
|
+
///
|
20
|
+
/// Note that OCB is covered by patents, however a license has been granted for use in "most"
|
21
|
+
/// software. See: http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm
|
22
|
+
///
|
23
|
+
/// Based on https://github.com/mumble-voip/mumble/blob/e31d267a11b4ed0597ad41309a7f6b715837141f/src/CryptState.cpp
|
24
|
+
#[derive(Debug)]
|
25
|
+
pub struct CryptState {
|
26
|
+
key: [u8; KEY_SIZE],
|
27
|
+
|
28
|
+
// internally as native endianness, externally as little endian and during ocb_* as big endian
|
29
|
+
encrypt_nonce: u128,
|
30
|
+
decrypt_nonce: u128,
|
31
|
+
decrypt_history: [u8; 0x100],
|
32
|
+
|
33
|
+
good: u32,
|
34
|
+
late: u32,
|
35
|
+
lost: u32,
|
36
|
+
}
|
37
|
+
|
38
|
+
/// The reason a decrypt operation failed.
|
39
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
40
|
+
pub enum DecryptError {
|
41
|
+
/// The packet is too short to be decrypted
|
42
|
+
Eof,
|
43
|
+
/// The packet has already been decrypted previously.
|
44
|
+
Repeat,
|
45
|
+
/// The packet was far too late.
|
46
|
+
Late,
|
47
|
+
/// The MAC of the decrypted packet did not match.
|
48
|
+
///
|
49
|
+
/// This may also indicate a substantial de-sync of the decryption nonce.
|
50
|
+
Mac,
|
51
|
+
}
|
52
|
+
|
53
|
+
impl CryptState {
|
54
|
+
/// Creates a new CryptState with randomly generated key and initial encrypt- and decrypt-nonce.
|
55
|
+
pub fn generate_new() -> Self {
|
56
|
+
let mut key = [0; KEY_SIZE];
|
57
|
+
rand_bytes(&mut key).unwrap();
|
58
|
+
|
59
|
+
CryptState {
|
60
|
+
key,
|
61
|
+
|
62
|
+
encrypt_nonce: 0,
|
63
|
+
decrypt_nonce: 1 << 127,
|
64
|
+
decrypt_history: [0; 0x100],
|
65
|
+
|
66
|
+
good: 0,
|
67
|
+
late: 0,
|
68
|
+
lost: 0,
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
pub fn make_new(&self) -> Self {
|
73
|
+
CryptState {
|
74
|
+
key: self.key.clone(),
|
75
|
+
|
76
|
+
encrypt_nonce: self.encrypt_nonce.clone(),
|
77
|
+
decrypt_nonce: self.decrypt_nonce.clone(),
|
78
|
+
decrypt_history: [0; 0x100],
|
79
|
+
|
80
|
+
good: 0,
|
81
|
+
late: 0,
|
82
|
+
lost: 0,
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
/// Creates a new CryptState from previously generated key, encrypt- and decrypt-nonce.
|
87
|
+
pub fn new_from(
|
88
|
+
key: [u8; KEY_SIZE],
|
89
|
+
encrypt_nonce: [u8; BLOCK_SIZE],
|
90
|
+
decrypt_nonce: [u8; BLOCK_SIZE],
|
91
|
+
) -> Self {
|
92
|
+
CryptState {
|
93
|
+
key,
|
94
|
+
|
95
|
+
encrypt_nonce: u128::from_le_bytes(encrypt_nonce),
|
96
|
+
decrypt_nonce: u128::from_le_bytes(decrypt_nonce),
|
97
|
+
decrypt_history: [0; 0x100],
|
98
|
+
|
99
|
+
good: 0,
|
100
|
+
late: 0,
|
101
|
+
lost: 0,
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
/// Returns the amount of packets transmitted without issues.
|
106
|
+
pub fn get_good(&self) -> u32 {
|
107
|
+
self.good
|
108
|
+
}
|
109
|
+
|
110
|
+
/// Returns the amount of packets which were transmitted successfully but arrived late.
|
111
|
+
pub fn get_late(&self) -> u32 {
|
112
|
+
self.late
|
113
|
+
}
|
114
|
+
|
115
|
+
/// Returns the amount of packets which were lost.
|
116
|
+
pub fn get_lost(&self) -> u32 {
|
117
|
+
self.lost
|
118
|
+
}
|
119
|
+
|
120
|
+
/// Returns the shared, **private** key.
|
121
|
+
pub fn get_key(&self) -> &[u8; KEY_SIZE] {
|
122
|
+
&self.key
|
123
|
+
}
|
124
|
+
|
125
|
+
/// Returns the nonce used for encrypting.
|
126
|
+
pub fn get_encrypt_nonce(&self) -> [u8; BLOCK_SIZE] {
|
127
|
+
self.encrypt_nonce.to_le_bytes()
|
128
|
+
}
|
129
|
+
|
130
|
+
/// Returns the nonce used for decrypting.
|
131
|
+
pub fn get_decrypt_nonce(&self) -> [u8; BLOCK_SIZE] {
|
132
|
+
self.decrypt_nonce.to_le_bytes()
|
133
|
+
}
|
134
|
+
|
135
|
+
/// Updates the nonce used for decrypting.
|
136
|
+
pub fn set_decrypt_nonce(&mut self, nonce: &[u8; BLOCK_SIZE]) {
|
137
|
+
self.decrypt_nonce = u128::from_le_bytes(*nonce);
|
138
|
+
}
|
139
|
+
|
140
|
+
/// Encrypts an encoded voice packet and returns the resulting bytes.
|
141
|
+
pub fn encrypt(&mut self, src: Vec<u8>, dst: &mut BytesMut) {
|
142
|
+
self.encrypt_nonce = self.encrypt_nonce.wrapping_add(1);
|
143
|
+
|
144
|
+
// Leave four bytes for header
|
145
|
+
dst.resize(4, 0);
|
146
|
+
let mut inner = dst.split_off(4);
|
147
|
+
|
148
|
+
// Copy source bytes
|
149
|
+
inner.extend_from_slice(&src);
|
150
|
+
|
151
|
+
// Encryption
|
152
|
+
let tag = self.ocb_encrypt(inner.as_mut());
|
153
|
+
|
154
|
+
// Build result
|
155
|
+
dst.unsplit(inner);
|
156
|
+
dst[0] = self.encrypt_nonce as u8;
|
157
|
+
dst[1..4].copy_from_slice(&tag.to_be_bytes()[0..3]);
|
158
|
+
}
|
159
|
+
|
160
|
+
/// Decrypts a voice packet and (if successful) returns the resulting bytes.
|
161
|
+
pub fn decrypt(&mut self, buf: &mut BytesMut) -> Result<(), DecryptError> {
|
162
|
+
if buf.len() < 4 {
|
163
|
+
return Err(DecryptError::Eof);
|
164
|
+
}
|
165
|
+
let header = buf.split_to(4);
|
166
|
+
let nonce_0 = header[0];
|
167
|
+
|
168
|
+
// If we update our decrypt_nonce and the tag check fails or we've been processing late
|
169
|
+
// packets, we need to revert it
|
170
|
+
let saved_nonce = self.decrypt_nonce;
|
171
|
+
let mut late = false; // will always restore nonce if this is the case
|
172
|
+
let mut lost = 0; // for stats only
|
173
|
+
|
174
|
+
if self.decrypt_nonce.wrapping_add(1) as u8 == nonce_0 {
|
175
|
+
// in order
|
176
|
+
self.decrypt_nonce = self.decrypt_nonce.wrapping_add(1);
|
177
|
+
} else {
|
178
|
+
// packet is late or repeated, or we lost a few packets in between
|
179
|
+
let diff = nonce_0.wrapping_sub(self.decrypt_nonce as u8) as i8;
|
180
|
+
|
181
|
+
self.decrypt_nonce = self.decrypt_nonce.wrapping_add(diff as u128);
|
182
|
+
if diff > 0 {
|
183
|
+
lost = i32::from(diff - 1); // lost a few packets in between this and the last one
|
184
|
+
} else if diff > -30 {
|
185
|
+
if self.decrypt_history[nonce_0 as usize] == (self.decrypt_nonce >> 8) as u8 {
|
186
|
+
self.decrypt_nonce = saved_nonce;
|
187
|
+
return Err(DecryptError::Repeat);
|
188
|
+
}
|
189
|
+
// just late
|
190
|
+
late = true;
|
191
|
+
lost = -1;
|
192
|
+
} else {
|
193
|
+
return Err(DecryptError::Late); // late by more than 30 packets
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
let tag = self.ocb_decrypt(buf.as_mut());
|
198
|
+
|
199
|
+
if !memcmp::eq(&tag.to_be_bytes()[0..3], &header[1..4]) {
|
200
|
+
self.decrypt_nonce = saved_nonce;
|
201
|
+
return Err(DecryptError::Mac);
|
202
|
+
}
|
203
|
+
|
204
|
+
self.decrypt_history[nonce_0 as usize] = (self.decrypt_nonce >> 8) as u8;
|
205
|
+
|
206
|
+
self.good += 1;
|
207
|
+
if late {
|
208
|
+
self.late += 1;
|
209
|
+
self.decrypt_nonce = saved_nonce;
|
210
|
+
}
|
211
|
+
self.lost = (self.lost as i32 + lost as i32) as u32;
|
212
|
+
|
213
|
+
Ok(())
|
214
|
+
}
|
215
|
+
|
216
|
+
/// Encrypt the provided buffer using AES-OCB, returning the tag.
|
217
|
+
fn ocb_encrypt(&self, mut buf: &mut [u8]) -> u128 {
|
218
|
+
let mut offset = self.aes_encrypt(self.encrypt_nonce.to_be());
|
219
|
+
let mut checksum = 0u128;
|
220
|
+
|
221
|
+
while buf.len() > BLOCK_SIZE {
|
222
|
+
let (chunk, remainder) = buf.split_at_mut(BLOCK_SIZE);
|
223
|
+
buf = remainder;
|
224
|
+
let chunk: &mut [u8; BLOCK_SIZE] = chunk.try_into().expect("split_at works");
|
225
|
+
|
226
|
+
offset = s2(offset);
|
227
|
+
|
228
|
+
let plain = u128::from_be_bytes(*chunk);
|
229
|
+
let encrypted = self.aes_encrypt(offset ^ plain) ^ offset;
|
230
|
+
chunk.copy_from_slice(&encrypted.to_be_bytes());
|
231
|
+
|
232
|
+
checksum ^= plain;
|
233
|
+
}
|
234
|
+
|
235
|
+
offset = s2(offset);
|
236
|
+
|
237
|
+
let len = buf.len();
|
238
|
+
assert!(len <= BLOCK_SIZE);
|
239
|
+
let pad = self.aes_encrypt((len * 8) as u128 ^ offset);
|
240
|
+
let mut block = pad.to_be_bytes();
|
241
|
+
block[..len].copy_from_slice(buf);
|
242
|
+
let plain = u128::from_be_bytes(block);
|
243
|
+
let encrypted = pad ^ plain;
|
244
|
+
buf.copy_from_slice(&encrypted.to_be_bytes()[..len]);
|
245
|
+
|
246
|
+
checksum ^= plain;
|
247
|
+
|
248
|
+
self.aes_encrypt(offset ^ s2(offset) ^ checksum)
|
249
|
+
}
|
250
|
+
|
251
|
+
/// Decrypt the provided buffer using AES-OCB, returning the tag.
|
252
|
+
/// **Make sure to verify that the tag matches!**
|
253
|
+
fn ocb_decrypt(&self, mut buf: &mut [u8]) -> u128 {
|
254
|
+
let mut offset = self.aes_encrypt(self.decrypt_nonce.to_be());
|
255
|
+
let mut checksum = 0u128;
|
256
|
+
|
257
|
+
while buf.len() > BLOCK_SIZE {
|
258
|
+
let (chunk, remainder) = buf.split_at_mut(BLOCK_SIZE);
|
259
|
+
buf = remainder;
|
260
|
+
let chunk: &mut [u8; BLOCK_SIZE] = chunk.try_into().expect("split_at works");
|
261
|
+
|
262
|
+
offset = s2(offset);
|
263
|
+
|
264
|
+
let encrypted = u128::from_be_bytes(*chunk);
|
265
|
+
let plain = self.aes_decrypt(offset ^ encrypted) ^ offset;
|
266
|
+
chunk.copy_from_slice(&plain.to_be_bytes());
|
267
|
+
|
268
|
+
checksum ^= plain;
|
269
|
+
}
|
270
|
+
|
271
|
+
offset = s2(offset);
|
272
|
+
|
273
|
+
let len = buf.len();
|
274
|
+
assert!(len <= BLOCK_SIZE);
|
275
|
+
let pad = self.aes_encrypt((len * 8) as u128 ^ offset);
|
276
|
+
let mut block = [0; BLOCK_SIZE];
|
277
|
+
block[..len].copy_from_slice(buf);
|
278
|
+
let plain = u128::from_be_bytes(block) ^ pad;
|
279
|
+
buf.copy_from_slice(&plain.to_be_bytes()[..len]);
|
280
|
+
|
281
|
+
checksum ^= plain;
|
282
|
+
|
283
|
+
self.aes_encrypt(offset ^ s2(offset) ^ checksum)
|
284
|
+
}
|
285
|
+
|
286
|
+
/// AES-128 encryption primitive.
|
287
|
+
fn aes_encrypt(&self, block: u128) -> u128 {
|
288
|
+
// TODO is there no better way to do this (and aes_decrypt)?
|
289
|
+
let mut result = [0u8; BLOCK_SIZE * 2];
|
290
|
+
let mut crypter = openssl::symm::Crypter::new(
|
291
|
+
openssl::symm::Cipher::aes_128_ecb(),
|
292
|
+
openssl::symm::Mode::Encrypt,
|
293
|
+
&self.key,
|
294
|
+
None,
|
295
|
+
)
|
296
|
+
.unwrap();
|
297
|
+
crypter.pad(false);
|
298
|
+
crypter.update(&block.to_be_bytes(), &mut result).unwrap();
|
299
|
+
crypter.finalize(&mut result).unwrap();
|
300
|
+
u128::from_be_bytes((&result[..BLOCK_SIZE]).try_into().unwrap())
|
301
|
+
}
|
302
|
+
|
303
|
+
/// AES-128 decryption primitive.
|
304
|
+
fn aes_decrypt(&self, block: u128) -> u128 {
|
305
|
+
let mut result = [0u8; BLOCK_SIZE * 2];
|
306
|
+
let mut crypter = openssl::symm::Crypter::new(
|
307
|
+
openssl::symm::Cipher::aes_128_ecb(),
|
308
|
+
openssl::symm::Mode::Decrypt,
|
309
|
+
&self.key,
|
310
|
+
None,
|
311
|
+
)
|
312
|
+
.unwrap();
|
313
|
+
crypter.pad(false);
|
314
|
+
crypter.update(&block.to_be_bytes(), &mut result).unwrap();
|
315
|
+
crypter.finalize(&mut result).unwrap();
|
316
|
+
u128::from_be_bytes((&result[..BLOCK_SIZE]).try_into().unwrap())
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
fn s2(block: u128) -> u128 {
|
321
|
+
let rot = block.rotate_left(1);
|
322
|
+
let carry = rot & 1;
|
323
|
+
rot ^ (carry * 0x86)
|
324
|
+
}
|
325
|
+
|
326
|
+
#[cfg(test)]
|
327
|
+
mod test {
|
328
|
+
use bytes::BufMut;
|
329
|
+
|
330
|
+
use super::*;
|
331
|
+
|
332
|
+
fn u128hex(src: &str) -> u128 {
|
333
|
+
u128::from_str_radix(src, 16).unwrap()
|
334
|
+
}
|
335
|
+
|
336
|
+
fn bytes_from_hex(src: &str) -> BytesMut {
|
337
|
+
let mut buf = BytesMut::new();
|
338
|
+
hex_to_bytes(src, &mut buf);
|
339
|
+
buf
|
340
|
+
}
|
341
|
+
|
342
|
+
fn hex_to_bytes(src: &str, dst: &mut BytesMut) {
|
343
|
+
dst.clear();
|
344
|
+
dst.reserve(src.len() / 2);
|
345
|
+
let mut iter = src.chars();
|
346
|
+
while !iter.as_str().is_empty() {
|
347
|
+
dst.put_u8(u8::from_str_radix(&iter.as_str()[..2], 16).unwrap());
|
348
|
+
iter.next();
|
349
|
+
iter.next();
|
350
|
+
}
|
351
|
+
}
|
352
|
+
|
353
|
+
#[test]
|
354
|
+
fn encrypt_and_decrypt_are_inverse() {
|
355
|
+
let mut server_state = CryptState::generate_new();
|
356
|
+
// swap nonce vectors side to side
|
357
|
+
let mut client_state =
|
358
|
+
CryptState::new_from(
|
359
|
+
*server_state.get_key(),
|
360
|
+
server_state.get_decrypt_nonce(),
|
361
|
+
server_state.get_encrypt_nonce(),
|
362
|
+
);
|
363
|
+
|
364
|
+
let mut buffer = BytesMut::new();
|
365
|
+
let src = "test".as_bytes().to_vec();
|
366
|
+
server_state.encrypt(src.clone(), &mut buffer);
|
367
|
+
|
368
|
+
let mut buffer2 = BytesMut::new();
|
369
|
+
buffer2.extend_from_slice(&(buffer.to_vec()));
|
370
|
+
|
371
|
+
client_state.decrypt(&mut buffer2).unwrap();
|
372
|
+
|
373
|
+
assert_eq!(src, buffer2.to_vec());
|
374
|
+
}
|
375
|
+
|
376
|
+
#[test]
|
377
|
+
fn aes_test_vectors() {
|
378
|
+
let key = u128hex("E8E9EAEBEDEEEFF0F2F3F4F5F7F8F9FA");
|
379
|
+
let state =
|
380
|
+
CryptState::new_from(key.to_be_bytes(), Default::default(), Default::default());
|
381
|
+
assert_eq!(
|
382
|
+
u128hex("6743C3D1519AB4F2CD9A78AB09A511BD"),
|
383
|
+
state.aes_encrypt(u128hex("014BAF2278A69D331D5180103643E99A"))
|
384
|
+
);
|
385
|
+
assert_eq!(
|
386
|
+
u128hex("014BAF2278A69D331D5180103643E99A"),
|
387
|
+
state.aes_decrypt(u128hex("6743C3D1519AB4F2CD9A78AB09A511BD"))
|
388
|
+
);
|
389
|
+
}
|
390
|
+
|
391
|
+
// Test vectors from http://web.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt
|
392
|
+
// (excluding ones with headers since those aren't implemented here)
|
393
|
+
#[test]
|
394
|
+
#[allow(clippy::cognitive_complexity)] // all macro-generated
|
395
|
+
fn ocb_test_vectors() {
|
396
|
+
macro_rules! test_cases {
|
397
|
+
($(
|
398
|
+
T : $name:expr,
|
399
|
+
M : $plain:expr,
|
400
|
+
C : $cipher:expr,
|
401
|
+
T : $tag:expr,
|
402
|
+
)*) => {$(
|
403
|
+
let key = u128hex("000102030405060708090a0b0c0d0e0f");
|
404
|
+
let nonce = u128hex("000102030405060708090a0b0c0d0e0f");
|
405
|
+
let state = CryptState::new_from(
|
406
|
+
key.to_be_bytes(),
|
407
|
+
nonce.to_be_bytes(),
|
408
|
+
nonce.to_be_bytes(),
|
409
|
+
);
|
410
|
+
|
411
|
+
let mut result = BytesMut::new();
|
412
|
+
hex_to_bytes($plain.as_ref(), &mut result);
|
413
|
+
let tag = state.ocb_encrypt(&mut result);
|
414
|
+
assert_eq!(bytes_from_hex($cipher), result, concat!("ENCRYPT-RESULT-", $name));
|
415
|
+
assert_eq!(u128hex($tag), tag, concat!("ENCRYPT-TAG-", $name));
|
416
|
+
|
417
|
+
hex_to_bytes($cipher.as_ref(), &mut result);
|
418
|
+
let tag = state.ocb_decrypt(&mut result);
|
419
|
+
assert_eq!(bytes_from_hex($plain), result, concat!("DECRYPT-RESULT-", $name));
|
420
|
+
assert_eq!(u128hex($tag), tag, concat!("DECRYPT-TAG-", $name));
|
421
|
+
)*};
|
422
|
+
}
|
423
|
+
|
424
|
+
test_cases! {
|
425
|
+
T : "OCB-AES-128-0B",
|
426
|
+
M : "",
|
427
|
+
C : "",
|
428
|
+
T : "BF3108130773AD5EC70EC69E7875A7B0",
|
429
|
+
|
430
|
+
T : "OCB-AES-128-8B",
|
431
|
+
M : "0001020304050607",
|
432
|
+
C : "C636B3A868F429BB",
|
433
|
+
T : "A45F5FDEA5C088D1D7C8BE37CABC8C5C",
|
434
|
+
|
435
|
+
T : "OCB-AES-128-16B",
|
436
|
+
M : "000102030405060708090A0B0C0D0E0F",
|
437
|
+
C : "52E48F5D19FE2D9869F0C4A4B3D2BE57",
|
438
|
+
T : "F7EE49AE7AA5B5E6645DB6B3966136F9",
|
439
|
+
|
440
|
+
T : "OCB-AES-128-24B",
|
441
|
+
M : "000102030405060708090A0B0C0D0E0F1011121314151617",
|
442
|
+
C : "F75D6BC8B4DC8D66B836A2B08B32A636CC579E145D323BEB",
|
443
|
+
T : "A1A50F822819D6E0A216784AC24AC84C",
|
444
|
+
|
445
|
+
T : "OCB-AES-128-32B",
|
446
|
+
M : "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
447
|
+
C : "F75D6BC8B4DC8D66B836A2B08B32A636CEC3C555037571709DA25E1BB0421A27",
|
448
|
+
T : "09CA6C73F0B5C6C5FD587122D75F2AA3",
|
449
|
+
|
450
|
+
T : "OCB-AES-128-40B",
|
451
|
+
M : "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
|
452
|
+
C : "F75D6BC8B4DC8D66B836A2B08B32A6369F1CD3C5228D79FD6C267F5F6AA7B231C7DFB9D59951AE9C",
|
453
|
+
T : "9DB0CDF880F73E3E10D4EB3217766688",
|
454
|
+
}
|
455
|
+
}
|
456
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
use std::cell::RefCell;
|
2
|
+
|
3
|
+
use magnus::{class, define_module, method, function, prelude::*, Error, RHash};
|
4
|
+
|
5
|
+
use bytes::BytesMut;
|
6
|
+
|
7
|
+
pub mod crypt_state;
|
8
|
+
|
9
|
+
#[magnus::wrap(class = "RbMumbleProtocol::CryptState", free_immediatly, size)]
|
10
|
+
struct CryptStateRef(RefCell<crypt_state::CryptState>);
|
11
|
+
|
12
|
+
impl CryptStateRef {
|
13
|
+
pub fn new() -> Self {
|
14
|
+
Self(RefCell::new(crypt_state::CryptState::generate_new()))
|
15
|
+
}
|
16
|
+
|
17
|
+
pub fn new_from(
|
18
|
+
key: Vec<u8>,
|
19
|
+
encrypt_nonce: Vec<u8>,
|
20
|
+
decrypt_nonce: Vec<u8>,
|
21
|
+
) -> Self {
|
22
|
+
let new_key =
|
23
|
+
key
|
24
|
+
.try_into()
|
25
|
+
.unwrap_or_else(|v: Vec<u8>| panic!("Expected a Key of length {} but it was {}", 16, v.len()));
|
26
|
+
|
27
|
+
let new_encrypt_nonce =
|
28
|
+
encrypt_nonce
|
29
|
+
.try_into()
|
30
|
+
.unwrap_or_else(|v: Vec<u8>| panic!("Expected a Encrypt nonce of length {} but it was {}", 16, v.len()));
|
31
|
+
|
32
|
+
let new_decrypt_nonce =
|
33
|
+
decrypt_nonce
|
34
|
+
.try_into()
|
35
|
+
.unwrap_or_else(|v: Vec<u8>| panic!("Expected a Decrypt nonce of length {} but it was {}", 16, v.len()));
|
36
|
+
|
37
|
+
let new_state = crypt_state::CryptState::new_from(
|
38
|
+
new_key,
|
39
|
+
new_encrypt_nonce,
|
40
|
+
new_decrypt_nonce
|
41
|
+
);
|
42
|
+
|
43
|
+
Self(RefCell::new(new_state))
|
44
|
+
}
|
45
|
+
|
46
|
+
pub fn key(&self) -> Vec<u8> { self.0.try_borrow_mut().unwrap().get_key().to_vec() }
|
47
|
+
pub fn encrypt_nonce(&self) -> Vec<u8> { self.0.try_borrow_mut().unwrap().get_encrypt_nonce().to_vec() }
|
48
|
+
pub fn decrypt_nonce(&self) -> Vec<u8> { self.0.try_borrow_mut().unwrap().get_decrypt_nonce().to_vec() }
|
49
|
+
|
50
|
+
pub fn stats(&self) -> RHash {
|
51
|
+
let hash = RHash::new();
|
52
|
+
let state = self.0.try_borrow_mut().unwrap();
|
53
|
+
|
54
|
+
let _ = hash.aset("good", state.get_good());
|
55
|
+
let _ = hash.aset("late", state.get_late());
|
56
|
+
let _ = hash.aset("lost", state.get_lost());
|
57
|
+
|
58
|
+
hash
|
59
|
+
}
|
60
|
+
|
61
|
+
pub fn encrypt(&self, src: Vec<u8>) -> Vec<u8> {
|
62
|
+
let mut buffer = BytesMut::new();
|
63
|
+
|
64
|
+
self.0.try_borrow_mut().unwrap().encrypt(src, &mut buffer);
|
65
|
+
|
66
|
+
buffer.to_vec()
|
67
|
+
}
|
68
|
+
|
69
|
+
pub fn decrypt(&self, encrypted: Vec<u8>) -> Vec<u8> {
|
70
|
+
let mut buffer = BytesMut::new();
|
71
|
+
buffer.extend_from_slice(&encrypted);
|
72
|
+
|
73
|
+
self.0.try_borrow_mut().unwrap().decrypt(&mut buffer).unwrap();
|
74
|
+
|
75
|
+
buffer.to_vec()
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
#[magnus::init]
|
80
|
+
fn init() -> Result<(), Error> {
|
81
|
+
let module = define_module("RbMumbleProtocol")?;
|
82
|
+
let class = module.define_class("CryptState", class::object())?;
|
83
|
+
|
84
|
+
class.define_singleton_method("new", function!(CryptStateRef::new, 0))?;
|
85
|
+
class.define_singleton_method("new_from", function!(CryptStateRef::new_from, 3))?;
|
86
|
+
|
87
|
+
class.define_method("key", method!(CryptStateRef::key, 0))?;
|
88
|
+
class.define_method("encrypt_nonce", method!(CryptStateRef::encrypt_nonce, 0))?;
|
89
|
+
class.define_method("decrypt_nonce", method!(CryptStateRef::decrypt_nonce, 0))?;
|
90
|
+
class.define_method("stats", method!(CryptStateRef::stats, 0))?;
|
91
|
+
|
92
|
+
class.define_method("encrypt", method!(CryptStateRef::encrypt, 1))?;
|
93
|
+
class.define_method("decrypt", method!(CryptStateRef::decrypt, 1))?;
|
94
|
+
|
95
|
+
Ok(())
|
96
|
+
}
|
97
|
+
|
98
|
+
#[test]
|
99
|
+
fn encrypt_and_decrypt_are_inverse() {
|
100
|
+
let server_state = CryptStateRef::new();
|
101
|
+
// swap nonce vectors side to side
|
102
|
+
let client_state = CryptStateRef::new_from(
|
103
|
+
server_state.key(),
|
104
|
+
server_state.decrypt_nonce(),
|
105
|
+
server_state.encrypt_nonce()
|
106
|
+
);
|
107
|
+
|
108
|
+
let src= "test".as_bytes().to_vec();
|
109
|
+
let encrypted= server_state.encrypt(src.clone());
|
110
|
+
let result= client_state.decrypt(encrypted.clone());
|
111
|
+
|
112
|
+
assert_eq!(src, result);
|
113
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "rb_mumble_protocol/version"
|
4
|
+
require_relative "rb_mumble_protocol/crypt_state"
|
5
|
+
require_relative "rb_mumble_protocol/rb_mumble_protocol"
|
6
|
+
|
7
|
+
module RbMumbleProtocol
|
8
|
+
class Error < StandardError; end
|
9
|
+
# Your code goes here...
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rb_mumble_protocol
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikhail Odebe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-23 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Gem that providing classes for implementing Mumble-related projects.
|
14
|
+
email:
|
15
|
+
- derpiranha@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions:
|
18
|
+
- ext/rb_mumble_protocol/Cargo.toml
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- Cargo.lock
|
22
|
+
- Cargo.toml
|
23
|
+
- ext/rb_mumble_protocol/Cargo.toml
|
24
|
+
- ext/rb_mumble_protocol/src/crypt_state.rs
|
25
|
+
- ext/rb_mumble_protocol/src/lib.rs
|
26
|
+
- lib/rb_mumble_protocol.rb
|
27
|
+
- lib/rb_mumble_protocol/crypt_state.rb
|
28
|
+
- lib/rb_mumble_protocol/version.rb
|
29
|
+
homepage: https://github.com/Odebe/rb_mumble_protocol
|
30
|
+
licenses: []
|
31
|
+
metadata:
|
32
|
+
homepage_uri: https://github.com/Odebe/rb_mumble_protocol
|
33
|
+
source_code_uri: https://github.com/Odebe/rb_mumble_protocol
|
34
|
+
changelog_uri: https://github.com/Odebe/rb_mumble_protocol
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.6.0
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 3.3.11
|
49
|
+
requirements: []
|
50
|
+
rubygems_version: 3.4.10
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: Gem that providing classes for implementing Mumble-related projects.
|
54
|
+
test_files: []
|