Browse Source

e2ee: add unit tests

Both for just encode function as well as an end-to-end test.
dev1
Philipp Hancke 5 years ago
parent
commit
666f1006de
2 changed files with 177 additions and 2 deletions
  1. 3
    2
      modules/e2ee/Context.js
  2. 174
    0
      modules/e2ee/Context.spec.js

+ 3
- 2
modules/e2ee/Context.js View File

@@ -1,4 +1,5 @@
1 1
 /* eslint-disable no-bitwise */
2
+/* global BigInt */
2 3
 
3 4
 import { deriveKeys, importKey, ratchet } from './crypto-utils';
4 5
 import { isArrayEqual } from './utils';
@@ -57,7 +58,7 @@ export class Context {
57 58
         // A per-sender counter that is used create the AES CTR.
58 59
         // Must be incremented on every frame that is sent, can be reset on
59 60
         // key changes.
60
-        this._sendCount = 0n;
61
+        this._sendCount = BigInt(0); // eslint-disable-line new-cap
61 62
 
62 63
         this._id = id;
63 64
     }
@@ -90,7 +91,7 @@ export class Context {
90 91
      */
91 92
     _setKeys(keys) {
92 93
         this._cryptoKeyRing[this._currentKeyIndex] = keys;
93
-        this._sendCount = 0n; // Reset the send count (bigint).
94
+        this._sendCount = BigInt(0); // eslint-disable-line new-cap
94 95
     }
95 96
 
96 97
     /**

+ 174
- 0
modules/e2ee/Context.spec.js View File

@@ -0,0 +1,174 @@
1
+import { Context } from './Context';
2
+import { ratchet, importKey } from './crypto-utils';
3
+
4
+/*
5
+function hexdump(buffer) {
6
+    const a = new Uint8Array(buffer);
7
+    let s = '';
8
+
9
+    for (let i = 0; i < a.byteLength; i++) {
10
+        s += '0x';
11
+        s += a[i].toString(16);
12
+        s += ' ';
13
+    }
14
+
15
+    return s.trim();
16
+}
17
+*/
18
+
19
+/* TODO: more tests
20
+ * - delta frames
21
+ * - frame header is not encrypted
22
+ * - different sendCounts
23
+ * - different key length
24
+ * - ratcheting in decodeFunction
25
+ * etc
26
+ */
27
+const audioBytes = [ 0xde, 0xad, 0xbe, 0xef ];
28
+const videoBytes = [ 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef ];
29
+
30
+describe('E2EE Context', () => {
31
+    let sender;
32
+    let receiver;
33
+    const key = new Uint8Array([
34
+        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
36
+    ]);
37
+
38
+    beforeEach(() => {
39
+        sender = new Context('sender');
40
+        receiver = new Context('receiver');
41
+    });
42
+
43
+    describe('encode function', () => {
44
+        it('with an audio frame', async done => {
45
+            const sendController = {
46
+                enqueue: encodedFrame => {
47
+                    const data = new Uint8Array(encodedFrame.data);
48
+
49
+                    // An audio frame will have an overhead of 6 bytes with this counter and key size:
50
+                    //   4 bytes truncated signature, counter (1 byte) and 1 byte trailer.
51
+                    expect(data.byteLength).toEqual(audioBytes.length + 6);
52
+
53
+                    // TODO: provide test vector and matcher.
54
+                    done();
55
+                }
56
+            };
57
+            const frame = {
58
+                data: new Uint8Array(audioBytes).buffer,
59
+                type: undefined // type is undefined for audio frames.
60
+            };
61
+
62
+            await sender.setKey(key, 0);
63
+            await receiver.setKey(key, 0);
64
+            await sender.encodeFunction(frame, sendController);
65
+        });
66
+
67
+        it('with a video frame', async done => {
68
+            const sendController = {
69
+                enqueue: encodedFrame => {
70
+                    const data = new Uint8Array(encodedFrame.data);
71
+
72
+                    // A video frame will have an overhead of 12 bytes with this counter and key size:
73
+                    //   10 bytes signature, counter (1 byte) and 1 byte trailer.
74
+
75
+                    expect(data.byteLength).toEqual(videoBytes.length + 12);
76
+
77
+                    // TODO: provide test vector and matcher.
78
+                    done();
79
+                }
80
+            };
81
+            const frame = {
82
+                data: new Uint8Array(videoBytes).buffer,
83
+                type: 'key'
84
+            };
85
+
86
+            await sender.setKey(key, 0);
87
+            await receiver.setKey(key, 0);
88
+            await sender.encodeFunction(frame, sendController);
89
+        });
90
+    });
91
+
92
+    describe('end-to-end test', () => {
93
+        it('with an audio frame', async done => {
94
+            await sender.setKey(key, 0);
95
+            await receiver.setKey(key, 0);
96
+            const receiveController = {
97
+                enqueue: encodedFrame => {
98
+                    const data = new Uint8Array(encodedFrame.data);
99
+
100
+                    expect(data.byteLength).toEqual(audioBytes.length);
101
+                    expect(Array.from(data)).toEqual(audioBytes);
102
+                    done();
103
+                }
104
+            };
105
+            const sendController = {
106
+                enqueue: encodedFrame => {
107
+                    receiver.decodeFunction(encodedFrame, receiveController);
108
+                }
109
+            };
110
+            const frame = {
111
+                data: new Uint8Array(audioBytes).buffer,
112
+                type: undefined // type is undefined for audio frames.
113
+            };
114
+
115
+            await sender.encodeFunction(frame, sendController);
116
+        });
117
+
118
+        it('with a video frame', async done => {
119
+            await sender.setKey(key, 0);
120
+            await receiver.setKey(key, 0);
121
+            const receiveController = {
122
+                enqueue: encodedFrame => {
123
+                    const data = new Uint8Array(encodedFrame.data);
124
+
125
+                    expect(data.byteLength).toEqual(videoBytes.length);
126
+                    expect(Array.from(data)).toEqual(videoBytes);
127
+                    done();
128
+                }
129
+            };
130
+            const sendController = {
131
+                enqueue: encodedFrame => {
132
+                    receiver.decodeFunction(encodedFrame, receiveController);
133
+                }
134
+            };
135
+            const frame = {
136
+                data: new Uint8Array(videoBytes).buffer,
137
+                type: 'key'
138
+            };
139
+
140
+            await sender.encodeFunction(frame, sendController);
141
+        });
142
+
143
+        it('the receiver ratchets forward', async done => {
144
+            await sender.setKey(key, 0);
145
+            await receiver.setKey(key, 0);
146
+
147
+            // Ratchet the key. We reimport from the raw bytes.
148
+            const material = await importKey(key);
149
+
150
+            await sender.setKey(await ratchet(material), 0);
151
+
152
+            const receiveController = {
153
+                enqueue: encodedFrame => {
154
+                    const data = new Uint8Array(encodedFrame.data);
155
+
156
+                    expect(data.byteLength).toEqual(audioBytes.length);
157
+                    expect(Array.from(data)).toEqual(audioBytes);
158
+                    done();
159
+                }
160
+            };
161
+            const sendController = {
162
+                enqueue: encodedFrame => {
163
+                    receiver.decodeFunction(encodedFrame, receiveController);
164
+                }
165
+            };
166
+            const frame = {
167
+                data: new Uint8Array(audioBytes).buffer,
168
+                type: undefined
169
+            };
170
+
171
+            await sender.encodeFunction(frame, sendController);
172
+        });
173
+    });
174
+});

Loading…
Cancel
Save