@wireapp/avs 0.0.3168 → 0.0.4297

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,441 @@
1
+
2
+
3
+ var coders = {};
4
+
5
+ const HDR_VERSION = 0;
6
+
7
+ /*
8
+ Header
9
+
10
+ 1 1 1 1 1 1
11
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
12
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13
+ | V=0 |E| RES |S|LEN |1|KLEN |
14
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15
+ | KID... (length=KLEN) |
16
+ +-------------------------------+
17
+ | CTR... (length=LEN) |
18
+ +-------------------------------+
19
+
20
+ Extensions
21
+
22
+ 0 1 2 3 4 5 6 7
23
+ +-+-+-+-+-+-+-+-+---------------------------+
24
+ |M| ELN | EID | VAL... (length=ELEN) |
25
+ +-+-+-+-+-+-+-+-+---------------------------+
26
+ */
27
+
28
+ function calcLen(val) {
29
+ let l = 0;
30
+
31
+ if (val == 0)
32
+ return 1;
33
+
34
+ while (val != 0) {
35
+ l++;
36
+ val = val >>> 8;
37
+ }
38
+
39
+ return l;
40
+ }
41
+
42
+ function writeBytes(buf, p, len, val)
43
+ {
44
+ switch (len) {
45
+ case 8:
46
+ buf[p] = (val >>> 56) & 0xff;
47
+ p++;
48
+
49
+ case 7:
50
+ buf[p] = (val >>> 48) & 0xff;
51
+ p++;
52
+
53
+ case 6:
54
+ buf[p] = (val >>> 40) & 0xff;
55
+ p++;
56
+
57
+ case 5:
58
+ buf[p] = (val >>> 32) & 0xff;
59
+ p++;
60
+
61
+ case 4:
62
+ buf[p] = (val >>> 24) & 0xff;
63
+ p++;
64
+
65
+ case 3:
66
+ buf[p] = (val >>> 16) & 0xff;
67
+ p++;
68
+
69
+ case 2:
70
+ buf[p] = (val >>> 8) & 0xff;
71
+ p++;
72
+
73
+ case 1:
74
+ buf[p] = val & 0xff;
75
+ break;
76
+
77
+ default:
78
+ return 0;
79
+ }
80
+
81
+ return len;
82
+ }
83
+
84
+ function readBytes(buf, p, len) {
85
+ let l = len;
86
+ let v = 0;
87
+
88
+ while (l > 0) {
89
+ v = v * 256;
90
+ v = v + buf[p];
91
+ p++;
92
+ l--;
93
+ }
94
+
95
+ const lv = {
96
+ len: len,
97
+ val: v
98
+ };
99
+
100
+ return lv;
101
+ }
102
+
103
+ function frameHeaderDecode(buf) {
104
+
105
+ let ext = buf[0] & 0x10;
106
+ const flen = ((buf[1] >>> 4) & 0x7) + 1;
107
+ const x = (buf[1] >>> 3) & 0x1;
108
+ const klen = buf[1] & 0x7;
109
+ let p = 2;
110
+ let kid = -1;
111
+ let fid = -1;
112
+
113
+ if (x) {
114
+ const lv = readBytes(buf, p, klen + 1);
115
+
116
+ p += lv.len;
117
+ kid = lv.val;
118
+ }
119
+ else {
120
+ kid = klen;
121
+ }
122
+
123
+ const lv = readBytes(buf, p, flen);
124
+ p += lv.len;
125
+ fid = lv.val;
126
+
127
+ // Code below handles extensions, we don't use them for now...
128
+ /*
129
+ while(ext) {
130
+ const b = buf[p];
131
+ ext = b & 0x80;
132
+ const elen = ((b >>> 4) & 0x7) + 2;
133
+ p += elen;
134
+ }
135
+ */
136
+
137
+ if (kid < 0 || fid < 0) {
138
+ return null;
139
+ }
140
+ else {
141
+ const frameHdr = {
142
+ len: p,
143
+ frameId: fid,
144
+ keyId: kid
145
+ }
146
+ return frameHdr;
147
+ }
148
+ }
149
+
150
+ function frameHeaderEncode(fid, kid)
151
+ {
152
+ const ab = new ArrayBuffer(32);
153
+ const buf = new Uint8Array(ab);
154
+ let sig = 0;
155
+ let x = 0;
156
+ let klen = 0;
157
+ let flen = 0;
158
+ let p = 2;
159
+ let ext = 0;
160
+
161
+ if (kid > 7) {
162
+ x = 1;
163
+ klen = calcLen(kid) - 1;
164
+ }
165
+ else {
166
+ klen = kid;
167
+ }
168
+
169
+ flen = calcLen(fid);
170
+
171
+ buf[0] = (HDR_VERSION << 5) + (ext << 4);
172
+ buf[1] = (sig << 7) + ((flen - 1) << 4) + (x << 3) + klen;
173
+ if (x) {
174
+ p += writeBytes(buf, p, klen + 1, kid);
175
+ }
176
+ p += writeBytes(buf, p, flen, fid);
177
+
178
+ return buf.subarray(0, p);
179
+ }
180
+
181
+ function xor_iv(iv, fid, kid) {
182
+ var oiv = new Uint8Array(iv);
183
+
184
+ oiv[0] = oiv[0] ^ ((fid >> 24) & 0xFF);
185
+ oiv[1] = oiv[1] ^ ((fid >> 16) & 0xFF);
186
+ oiv[2] = oiv[2] ^ ((fid >> 8) & 0xFF);
187
+ oiv[3] = oiv[3] ^ ((fid ) & 0xFF);
188
+ oiv[4] = oiv[4] ^ ((kid >> 24) & 0xFF);
189
+ oiv[5] = oiv[5] ^ ((kid >> 16) & 0xFF);
190
+ oiv[6] = oiv[6] ^ ((kid >> 8) & 0xFF);
191
+ oiv[7] = oiv[7] ^ ((kid ) & 0xFF);
192
+
193
+ return oiv;
194
+ }
195
+
196
+ function getMediaKey(self, index) {
197
+ postMessage({
198
+ op: "getMediaKey",
199
+ self: self,
200
+ index: index
201
+ });
202
+ }
203
+
204
+ function getCurrentMediaKey(self, index) {
205
+ postMessage({
206
+ op: "getCurrentMediaKey",
207
+ self: self
208
+ });
209
+ }
210
+
211
+ function encryptFrame(coder, rtcFrame, controller) {
212
+ const dataBuf = rtcFrame.data;
213
+ const data = new Uint8Array(dataBuf);
214
+ const isVideo = false;
215
+
216
+ //console.log("encryptFrame=", rtcFrame);
217
+ const t = Object.prototype.toString.call(rtcFrame);
218
+ if (t === '[object RTCEncodedVideoFrame]')
219
+ isVideo = true;
220
+
221
+ if (coder.currentKey == null) {
222
+ getCurrentMediaKey(coder.self);
223
+ return;
224
+ }
225
+
226
+ const baseiv = isVideo ? coder.video.iv : coder.audio.iv;
227
+ const iv = xor_iv(baseiv, coder.frameId, coder.currentKey.id);
228
+
229
+ const hdr = frameHeaderEncode(coder.frameId, coder.currentKey.id);
230
+ crypto.subtle.encrypt(
231
+ {
232
+ name: "AES-GCM",
233
+ iv: iv,
234
+ tagLength: 128,
235
+ additionalData: hdr
236
+ },
237
+ coder.currentKey.key,
238
+ data
239
+ )
240
+ .then(function(encdata){
241
+ const enc8 = new Uint8Array(encdata);
242
+ const enc = new Uint8Array(hdr.length + enc8.length);
243
+
244
+ enc.set(hdr);
245
+ enc.set(enc8, hdr.length);
246
+
247
+ rtcFrame.data = enc.buffer;
248
+ controller.enqueue(rtcFrame);
249
+
250
+ coder.frameId++;
251
+ });
252
+ }
253
+
254
+ function decryptFrame(coder, rtcFrame, controller) {
255
+ const dataBuf = rtcFrame.data;
256
+ const data = new Uint8Array(dataBuf);
257
+ const ssrc = rtcFrame.synchronizationSource.toString();
258
+
259
+ let uinfo = coder.audio.users[ssrc];
260
+ let isVideo = false;
261
+
262
+ if (!uinfo) {
263
+ uinfo = coder.video.users[ssrc];
264
+ isVideo = true;
265
+ }
266
+ if (!uinfo) {
267
+ console.log('decryptFrame: no userinfo for ssrc: ' + ssrc);
268
+ return;
269
+ }
270
+
271
+ const frameHdr = frameHeaderDecode(data);
272
+
273
+ if (frameHdr == null) {
274
+ console.log('decryptFrame: failed to decode frame header');
275
+ return;
276
+ }
277
+
278
+ /*
279
+ console.log("coder[" + coder.self + "]"
280
+ + " len=" + frameHdr.len
281
+ + " kid="+ frameHdr.keyId
282
+ + " fid=" + frameHdr.frameId);
283
+ */
284
+
285
+ var kinfo = null;
286
+ for (i = 0; i < coder.keys.length; i++) {
287
+ if (coder.keys[i].id == frameHdr.keyId) {
288
+ kinfo = coder.keys[i];
289
+ break;
290
+ }
291
+ }
292
+
293
+ if (!kinfo) {
294
+ getMediaKey(coder.self, frameHdr.keyId);
295
+ return;
296
+ }
297
+
298
+ if (isVideo) {
299
+ iv = xor_iv(uinfo.ivv, frameHdr.frameId, frameHdr.keyId);
300
+ }
301
+ else {
302
+ iv = xor_iv(uinfo.iva, frameHdr.frameId, frameHdr.keyId);
303
+ }
304
+
305
+ const hdr = data.subarray(0, frameHdr.len);
306
+ const dec = data.subarray(frameHdr.len);
307
+ crypto.subtle.decrypt(
308
+ {
309
+ name: "AES-GCM",
310
+ iv: iv,
311
+ tagLength: 128,
312
+ additionalData: hdr
313
+ },
314
+ kinfo.key,
315
+ dec
316
+ )
317
+ .then(function(decdata){
318
+ rtcFrame.data = decdata;
319
+ controller.enqueue(rtcFrame);
320
+ })
321
+ .catch(err => {
322
+ console.log(err);
323
+ });
324
+ }
325
+
326
+ onmessage = async (event) => {
327
+ const {op, self} = event.data;
328
+ console.log('AVS worker op=' + op + ' self=' + self);
329
+
330
+ let coder = coders[self];
331
+ if (!coder) {
332
+ console.log('AVS worker: no coder for self=' + self);
333
+ }
334
+ /*
335
+ const {opinfo, readableStream, writableStream} = event.data;
336
+ const mval = opinfo.mtype === 'video' ? 1 : 0; // corresponds to enum frame_media_type
337
+ */
338
+ if (op === 'create') {
339
+ const { iva, ivv } = event.data;
340
+ if (!coder) {
341
+ coder = {
342
+ self: self,
343
+ frameId: Math.random() * 4294967296,
344
+ currentKey: null,
345
+ keys: [],
346
+ audio: {
347
+ iv: iva,
348
+ users: [],
349
+ },
350
+ video: {
351
+ iv: ivv,
352
+ users: [],
353
+ },
354
+ }
355
+ coders[self] = coder;
356
+
357
+ console.log('AVS worker: adding coder for self=' + self);
358
+ }
359
+ }
360
+ else if (op === 'destroy') {
361
+ coders[self] = null;
362
+ }
363
+ else if (op === 'addUser') {
364
+ if (!coder) {
365
+ console.log('addUser: no coder');
366
+ return;
367
+ }
368
+ const {userInfo} = event.data;
369
+
370
+ console.log('addUser: adding audio ssrc: '+userInfo.ssrca);
371
+ coder.audio.users[userInfo.ssrca] = userInfo;
372
+ if (userInfo.ssrcv != 0) {
373
+ coder.video.users[userInfo.ssrcv] = userInfo;
374
+ }
375
+ }
376
+ else if (op === 'setupSender') {
377
+ const {readableStream, writableStream} = event.data;
378
+
379
+ const transformStream = new TransformStream({
380
+ transform: (frame, controller) => {encryptFrame(coder, frame, controller);},
381
+ });
382
+ readableStream
383
+ .pipeThrough(transformStream)
384
+ .pipeTo(writableStream);
385
+
386
+ console.log('AVS worker sender setup successfully');
387
+
388
+ }
389
+ else if (op === 'setupReceiver') {
390
+ const {readableStream, writableStream} = event.data;
391
+
392
+ if (!coder) {
393
+ console.log('setupSender: no coder for self=' + self);
394
+ return;
395
+ }
396
+
397
+ const transformStream = new TransformStream({
398
+ transform: (frame, controller) => {decryptFrame(coder, frame, controller);},
399
+ });
400
+ readableStream
401
+ .pipeThrough(transformStream)
402
+ .pipeTo(writableStream);
403
+
404
+ console.log('AVS worker receiver setup successfully');
405
+ }
406
+ else if (op === 'setMediaKey') {
407
+ const {index, current, key} = event.data;
408
+
409
+ console.log("setMediaKey: got key for: " + self
410
+ + " index=" + index + " current=" + current
411
+ + " keyLen=" + key.length);
412
+
413
+ crypto.subtle.importKey(
414
+ "raw",
415
+ key,
416
+ "AES-GCM",
417
+ false,
418
+ ["encrypt", "decrypt"]
419
+ )
420
+ .then(function(k){
421
+ var kinfo = null;
422
+ for (i = 0; i < coder.keys.length; i++) {
423
+ if (coder.keys[i].id == index) {
424
+ coder.keys[i].key = k;
425
+ kinfo = coder.keys[i];
426
+ break;
427
+ }
428
+ }
429
+ if (!kinfo) {
430
+ kinfo = {id:index, key:k};
431
+ coder.keys.push(kinfo);
432
+ }
433
+ if (index == current)
434
+ coder.currentKey = kinfo;
435
+
436
+ while (coder.keys.length > 4) {
437
+ coder.keys.shift();
438
+ }
439
+ });
440
+ }
441
+ };
@@ -0,0 +1,2 @@
1
+ declare function avs_core(): Promise<any>;
2
+ export = avs_core;