From 91307951d349ffac5295db00feb9386be8af2593 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 15 May 2023 12:55:04 +0200 Subject: [PATCH 01/26] Fix cached JPEG test This test didn't really check anything useful as the end result would be the same if the second JPEG failed to render. Fix this by clearing the canvas between the images, so we can tell if the second image actually rendered or not. --- tests/test.jpeg.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test.jpeg.js b/tests/test.jpeg.js index 6834f03d..7580c617 100644 --- a/tests/test.jpeg.js +++ b/tests/test.jpeg.js @@ -246,6 +246,8 @@ describe('JPEG Decoder', function () { testDecodeRect(decoder, 0, 0, 4, 4, data1, display, 24); + display.fillRect(0, 0, 4, 4, [128, 128, 128, 255]); + let data2 = [ // JPEG data 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, From 0dd9678e64842121ccbe89b5c91ff25df008abca Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 13 May 2023 13:29:09 +0200 Subject: [PATCH 02/26] Harmonise extended clipboard tests Let them all follow the same pattern to make things more clear. --- tests/test.rfb.js | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 4262ee63..de2c13df 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2528,17 +2528,14 @@ describe('Remote Frame Buffer Protocol Client', function () { let data = [3, 0, 0, 0]; const flags = [0x10, 0x00, 0x00, 0x01]; - /* The size 10 (utf8 encoded string size) and the - string "Aå漢字!" utf8 encoded and deflated. */ - let deflatedData = [120, 94, 99, 96, 96, 224, 114, 60, - 188, 244, 217, 158, 69, 79, 215, - 78, 87, 4, 0, 35, 207, 6, 66]; + let text = encodeUTF8("Aå漢字!"); + let deflatedText = deflateWithSize(text); // How much data we are sending. - push32(data, toUnsigned32bit(-(4 + deflatedData.length))); + push32(data, toUnsigned32bit(-(4 + deflatedText.length))); data = data.concat(flags); - data = data.concat(deflatedData); + data = data.concat(Array.from(deflatedText)); const spy = sinon.spy(); client.addEventListener("clipboard", spy); @@ -2562,15 +2559,12 @@ describe('Remote Frame Buffer Protocol Client', function () { push32(data, toUnsigned32bit(-(4 + deflatedText.length))); data = data.concat(flags); - - let sendData = new Uint8Array(data.length + deflatedText.length); - sendData.set(data); - sendData.set(deflatedText, data.length); + data = data.concat(Array.from(deflatedText)); const spy = sinon.spy(); client.addEventListener("clipboard", spy); - client._sock._websocket._receiveData(sendData); + client._sock._websocket._receiveData(new Uint8Array(data)); expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.text).to.equal(expectedData); client.removeEventListener("clipboard", spy); @@ -2589,15 +2583,12 @@ describe('Remote Frame Buffer Protocol Client', function () { push32(data, toUnsigned32bit(-(4 + deflatedText.length))); data = data.concat(flags); - - let sendData = new Uint8Array(data.length + deflatedText.length); - sendData.set(data); - sendData.set(deflatedText, data.length); + data = data.concat(Array.from(deflatedText)); const spy = sinon.spy(); client.addEventListener("clipboard", spy); - client._sock._websocket._receiveData(sendData); + client._sock._websocket._receiveData(new Uint8Array(data)); expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.text).to.equal(expectedData); client.removeEventListener("clipboard", spy); From 775ccaa74cca90d1270fcd172e1507897aee270d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 14 May 2023 17:58:06 +0200 Subject: [PATCH 03/26] Handle immediate responses in RSA-AES authentication The event handlers might provide the needed response right away, before even existing the event handler. We should be prepared to handle this case. --- core/ra2.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/ra2.js b/core/ra2.js index 647aea2f..9557054f 100644 --- a/core/ra2.js +++ b/core/ra2.js @@ -158,10 +158,11 @@ export default class RSAAESAuthenticationState extends EventTargetMixin { serverPublickey.set(serverE, 4 + serverKeyBytes); // verify server public key + let approveKey = this._waitApproveKeyAsync(); this.dispatchEvent(new CustomEvent("serververification", { detail: { type: "RSA", publickey: serverPublickey } })); - await this._waitApproveKeyAsync(); + await approveKey; // 2: Send client public key const clientKeyLength = 2048; @@ -260,6 +261,7 @@ export default class RSAAESAuthenticationState extends EventTargetMixin { throw new Error("RA2: failed to authenticate the message"); } subtype = subtype[0]; + let waitCredentials = this._waitCredentialsAsync(subtype); if (subtype === 1) { if (this._getCredentials().username === undefined || this._getCredentials().password === undefined) { @@ -276,7 +278,7 @@ export default class RSAAESAuthenticationState extends EventTargetMixin { } else { throw new Error("RA2: wrong subtype"); } - await this._waitCredentialsAsync(subtype); + await waitCredentials; let username; if (subtype === 1) { username = encodeUTF8(this._getCredentials().username).slice(0, 255); From 9b115a4485eb6bebc3cdec799ad304749e0bc0fb Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 15 May 2023 20:01:10 +0200 Subject: [PATCH 04/26] Send ArrayBuffer, not Uint8Array in tests This matches the true contents of a WebSocket 'message' event, so should be a more realistic test. --- tests/fake.websocket.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fake.websocket.js b/tests/fake.websocket.js index 8fb02c57..e5a2b2d6 100644 --- a/tests/fake.websocket.js +++ b/tests/fake.websocket.js @@ -58,8 +58,8 @@ export default class FakeWebSocket { // Break apart the data to expose bugs where we assume data is // neatly packaged for (let i = 0;i < data.length;i++) { - let buf = data.subarray(i, i+1); - this.onmessage(new MessageEvent("message", { 'data': buf })); + let buf = data.slice(i, i+1); + this.onmessage(new MessageEvent("message", { 'data': buf.buffer })); } } } From da75689f4c3fcff11056592771495fa3806d1374 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 15 May 2023 20:50:40 +0200 Subject: [PATCH 05/26] Fix data for empty RRE rect test The given data was not a correct RRE rect. --- tests/test.rre.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test.rre.js b/tests/test.rre.js index 8e006f87..ac3aabbb 100644 --- a/tests/test.rre.js +++ b/tests/test.rre.js @@ -93,7 +93,10 @@ describe('RRE Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [ 0x00, 0xff, 0xff, 0xff, 0xff ], display, 24); + testDecodeRect(decoder, 1, 2, 0, 0, + [ 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff ], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, From 3ef57d16005e89ed0ed93a27efff0a66d8662bbc Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 10:37:41 +0200 Subject: [PATCH 06/26] Fix security to authentication state test The "None" authentication will directly progress past authentication, so it's not a good type for this test. --- tests/test.rfb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index de2c13df..eeaa1caa 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1177,7 +1177,7 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should transition to the Authentication state and continue on successful negotiation', function () { - const authSchemes = [1, 1]; + const authSchemes = [1, 2]; client._negotiateAuthentication = sinon.spy(); client._sock._websocket._receiveData(new Uint8Array(authSchemes)); expect(client._rfbInitState).to.equal('Authentication'); From cd231e53ed5228380b1b1faac0494039f282d533 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 15:03:18 +0200 Subject: [PATCH 07/26] Don't overwrite methods with spies Spies should just attach without modifying the real method, or we might get unwanted side effects. --- tests/test.rfb.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index eeaa1caa..27f6392f 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -301,7 +301,7 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should call initMsg "soon"', function () { - client._initMsg = sinon.spy(); + sinon.spy(client, "_initMsg"); client.sendCredentials({ password: 'pass' }); this.clock.tick(5); expect(client._initMsg).to.have.been.calledOnce; @@ -393,13 +393,13 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('#focus', function () { it('should move focus to canvas object', function () { - client._canvas.focus = sinon.spy(); + sinon.spy(client._canvas, "focus"); client.focus(); expect(client._canvas.focus).to.have.been.calledOnce; }); it('should include focus options', function () { - client._canvas.focus = sinon.spy(); + sinon.spy(client._canvas, "focus"); client.focus({ foobar: 12, gazonk: true }); expect(client._canvas.focus).to.have.been.calledOnce; expect(client._canvas.focus).to.have.been.calledWith({ foobar: 12, gazonk: true}); @@ -408,7 +408,7 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('#blur', function () { it('should remove focus from canvas object', function () { - client._canvas.blur = sinon.spy(); + sinon.spy(client._canvas, "blur"); client.blur(); expect(client._canvas.blur).to.have.been.calledOnce; }); @@ -1178,7 +1178,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should transition to the Authentication state and continue on successful negotiation', function () { const authSchemes = [1, 2]; - client._negotiateAuthentication = sinon.spy(); + sinon.spy(client, "_negotiateAuthentication"); client._sock._websocket._receiveData(new Uint8Array(authSchemes)); expect(client._rfbInitState).to.equal('Authentication'); expect(client._negotiateAuthentication).to.have.been.calledOnce; @@ -1430,7 +1430,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfbCredentials = { username: 'user', target: 'target', password: 'password' }; - client._negotiateStdVNCAuth = sinon.spy(); + sinon.spy(client, "_negotiateStdVNCAuth"); sendSecurity(22, client); expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; }); @@ -1461,8 +1461,6 @@ describe('Remote Frame Buffer Protocol Client', function () { client._rfbCredentials = { username: 'user', target: 'target', password: 'password' }; - client._negotiateStdVNCAuth = sinon.spy(); - sendSecurity(22, client); const expected = [22, 4, 6]; // auth selection, len user, len target @@ -1526,7 +1524,7 @@ describe('Remote Frame Buffer Protocol Client', function () { /*it('should attempt to use VNC auth over no auth when possible', function () { client._rfbTightVNC = true; - client._negotiateStdVNCAuth = sinon.spy(); + sinon.spy(client, "_negotiateStdVNCAuth"); sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client); expect(client._sock).to.have.sent([0, 0, 0, 1]); expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; @@ -1542,7 +1540,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should accept VNC authentication and transition to that', function () { client._rfbTightVNC = true; - client._negotiateStdVNCAuth = sinon.spy(); + sinon.spy(client, "_negotiateStdVNCAuth"); sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client); expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2])); expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; @@ -1651,7 +1649,7 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, client._rfbCredentials.password); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - client._initMsg = sinon.spy(); + sinon.spy(client, "_initMsg"); client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); expect(client._initMsg).to.have.been.called; }); @@ -1674,7 +1672,7 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, client._rfbCredentials.password); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - client._initMsg = sinon.spy(); + sinon.spy(client, "_initMsg"); client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); expect(client._initMsg).to.have.been.called; }); @@ -1697,7 +1695,7 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, client._rfbCredentials.password); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - client._initMsg = sinon.spy(); + sinon.spy(client, "_initMsg"); client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); expect(client._initMsg).to.have.been.called; }); @@ -3882,13 +3880,13 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('WebSocket Events', function () { // message events it('should do nothing if we receive an empty message and have nothing in the queue', function () { - client._normalMsg = sinon.spy(); + sinon.spy(client, "_normalMsg"); client._sock._websocket._receiveData(new Uint8Array([])); expect(client._normalMsg).to.not.have.been.called; }); it('should handle a message in the connected state as a normal message', function () { - client._normalMsg = sinon.spy(); + sinon.spy(client, "_normalMsg"); client._sock._websocket._receiveData(new Uint8Array([1, 2, 3])); expect(client._normalMsg).to.have.been.called; }); @@ -3896,7 +3894,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should handle a message in any non-disconnected/failed state like an init message', function () { client._rfbConnectionState = 'connecting'; client._rfbInitState = 'ProtocolVersion'; - client._initMsg = sinon.spy(); + sinon.spy(client, "_initMsg"); client._sock._websocket._receiveData(new Uint8Array([1, 2, 3])); expect(client._initMsg).to.have.been.called; }); From c7c293279b0a0b7e3545d27f29aae8692979748a Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 12:29:48 +0200 Subject: [PATCH 08/26] Remove commented out Tight test case This is not something we intend to implement, so remove this never used test case. --- tests/test.rfb.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 27f6392f..6310d8e4 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1522,15 +1522,6 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(client._rfbInitState).to.equal('SecurityResult'); }); - /*it('should attempt to use VNC auth over no auth when possible', function () { - client._rfbTightVNC = true; - sinon.spy(client, "_negotiateStdVNCAuth"); - sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client); - expect(client._sock).to.have.sent([0, 0, 0, 1]); - expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; - expect(client._rfbAuthScheme).to.equal(2); - });*/ // while this would make sense, the original code doesn't actually do this - it('should accept the "no auth" auth type and transition to SecurityResult', function () { client._rfbTightVNC = true; sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client); From 29a50620ffacc9f40eb2e4f0f5f437e3db1858e5 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 12:30:36 +0200 Subject: [PATCH 09/26] Avoid touching internals in Tight auth tests We should test using only external manipulation so we don't assume a specific implementation. --- tests/test.rfb.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 6310d8e4..548c82ab 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1523,14 +1523,16 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should accept the "no auth" auth type and transition to SecurityResult', function () { - client._rfbTightVNC = true; + sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); + client._sock._websocket._getSentData(); // skip the tunnel choice here sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client); expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1])); expect(client._rfbInitState).to.equal('SecurityResult'); }); it('should accept VNC authentication and transition to that', function () { - client._rfbTightVNC = true; + sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); + client._sock._websocket._getSentData(); // skip the tunnel choice here sinon.spy(client, "_negotiateStdVNCAuth"); sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client); expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2])); @@ -1540,7 +1542,8 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should fail if there are no supported auth types', function () { sinon.spy(client, "_fail"); - client._rfbTightVNC = true; + sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); + client._sock._websocket._getSentData(); // skip the tunnel choice here sendNumStrPairs([[23, 'stdv', 'badval__']], client); expect(client._fail).to.have.been.calledOnce; }); From 0679c8a80176b023912be1f338d59e84801ee122 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 16:45:36 +0200 Subject: [PATCH 10/26] Test credentials using normal API Avoid poking around in the internals and instead test things using the official methods and events. This should give us more realistic and robust tests. --- tests/test.rfb.js | 90 +++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 548c82ab..0a626343 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -287,26 +287,6 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(client._sock.off).to.have.been.calledWith('open'); }); }); - - describe('#sendCredentials', function () { - let client; - beforeEach(function () { - client = makeRFB(); - client._rfbConnectionState = 'connecting'; - }); - - it('should set the rfb credentials properly"', function () { - client.sendCredentials({ password: 'pass' }); - expect(client._rfbCredentials).to.deep.equal({ password: 'pass' }); - }); - - it('should call initMsg "soon"', function () { - sinon.spy(client, "_initMsg"); - client.sendCredentials({ password: 'pass' }); - this.clock.tick(5); - expect(client._initMsg).to.have.been.calledOnce; - }); - }); }); describe('Public API Basic Behavior', function () { @@ -1240,31 +1220,36 @@ describe('Remote Frame Buffer Protocol Client', function () { for (let i = 0; i < 16; i++) { challenge[i] = i; } client._sock._websocket._receiveData(new Uint8Array(challenge)); - expect(client._rfbCredentials).to.be.empty; expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.types).to.have.members(["password"]); }); it('should encrypt the password with DES and then send it back', function () { - client._rfbCredentials = { password: 'passwd' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ password: 'passwd' }); + }); sendSecurity(2, client); client._sock._websocket._getSentData(); // skip the choice of auth reply const challenge = []; for (let i = 0; i < 16; i++) { challenge[i] = i; } client._sock._websocket._receiveData(new Uint8Array(challenge)); + clock.tick(); const desPass = RFB.genDES('passwd', challenge); expect(client._sock).to.have.sent(new Uint8Array(desPass)); }); it('should transition to SecurityResult immediately after sending the password', function () { - client._rfbCredentials = { password: 'passwd' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ password: 'passwd' }); + }); sendSecurity(2, client); const challenge = []; for (let i = 0; i < 16; i++) { challenge[i] = i; } client._sock._websocket._receiveData(new Uint8Array(challenge)); + clock.tick(); expect(client._rfbInitState).to.equal('SecurityResult'); }); @@ -1287,10 +1272,8 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should fire the credentialsrequired event if all credentials are missing', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); - client._rfbCredentials = {}; sendSecurity(30, client); - expect(client._rfbCredentials).to.be.empty; expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.types).to.have.members(["username", "password"]); }); @@ -1298,7 +1281,7 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should fire the credentialsrequired event if some credentials are missing', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); - client._rfbCredentials = { password: 'password'}; + client.sendCredentials({ password: 'password'}); sendSecurity(30, client); expect(spy).to.have.been.calledOnce; @@ -1306,8 +1289,10 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should return properly encrypted credentials and public key', async function () { - client._rfbCredentials = { username: 'user', - password: 'password' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'user', + password: 'password' }); + }); sendSecurity(30, client); expect(client._sock).to.have.sent([30]); @@ -1406,8 +1391,10 @@ describe('Remote Frame Buffer Protocol Client', function () { window.crypto.getRandomValues.restore(); }); it('should send public value and encrypted credentials', function () { - client._rfbCredentials = { username: 'username', - password: 'password123456' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'username', + password: 'password123456' }); + }); sendSecurity(113, client); expect(client._sock).to.have.sent([113]); @@ -1419,6 +1406,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._sock._websocket._receiveData(g); client._sock._websocket._receiveData(p); client._sock._websocket._receiveData(A); + clock.tick(); expect(client._sock).to.have.sent(expected); expect(client._rfbInitState).to.equal('SecurityResult'); @@ -1427,21 +1415,22 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('XVP Authentication (type 22) Handler', function () { it('should fall through to standard VNC authentication upon completion', function () { - client._rfbCredentials = { username: 'user', - target: 'target', - password: 'password' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'user', + target: 'target', + password: 'password' }); + }); sinon.spy(client, "_negotiateStdVNCAuth"); sendSecurity(22, client); + clock.tick(); expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; }); it('should fire the credentialsrequired event if all credentials are missing', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); - client._rfbCredentials = {}; sendSecurity(22, client); - expect(client._rfbCredentials).to.be.empty; expect(spy).to.have.been.calledOnce; expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]); }); @@ -1449,8 +1438,8 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should fire the credentialsrequired event if some credentials are missing', function () { const spy = sinon.spy(); client.addEventListener("credentialsrequired", spy); - client._rfbCredentials = { username: 'user', - target: 'target' }; + client.sendCredentials({ username: 'user', + target: 'target' }); sendSecurity(22, client); expect(spy).to.have.been.calledOnce; @@ -1458,10 +1447,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should send user and target separately', function () { - client._rfbCredentials = { username: 'user', - target: 'target', - password: 'password' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'user', + target: 'target', + password: 'password' }); + }); sendSecurity(22, client); + clock.tick(); const expected = [22, 4, 6]; // auth selection, len user, len target for (let i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); } @@ -1626,7 +1618,9 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should support Plain authentication', function () { - client._rfbCredentials = { username: 'username', password: 'password' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'username', password: 'password' }); + }); // VeNCrypt version client._sock._websocket._receiveData(new Uint8Array([0, 2])); expect(client._sock).to.have.sent(new Uint8Array([0, 2])); @@ -1635,6 +1629,8 @@ describe('Remote Frame Buffer Protocol Client', function () { // Subtype list. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + clock.tick(); + const expectedResponse = []; push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); @@ -1649,7 +1645,9 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should support Plain authentication with an empty password', function () { - client._rfbCredentials = { username: 'username', password: '' }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'username', password: '' }); + }); // VeNCrypt version client._sock._websocket._receiveData(new Uint8Array([0, 2])); expect(client._sock).to.have.sent(new Uint8Array([0, 2])); @@ -1658,6 +1656,8 @@ describe('Remote Frame Buffer Protocol Client', function () { // Subtype list. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + clock.tick(); + const expectedResponse = []; push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); @@ -1672,7 +1672,9 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should support Plain authentication with a very long username and password', function () { - client._rfbCredentials = { username: 'a'.repeat(300), password: 'a'.repeat(300) }; + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'a'.repeat(300), password: 'b'.repeat(300) }); + }); // VeNCrypt version client._sock._websocket._receiveData(new Uint8Array([0, 2])); expect(client._sock).to.have.sent(new Uint8Array([0, 2])); @@ -1681,6 +1683,8 @@ describe('Remote Frame Buffer Protocol Client', function () { // Subtype list. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + clock.tick(); + const expectedResponse = []; push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); From 79f099108fb03bccf5f9541ebf5884753e19cecc Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 12:34:26 +0200 Subject: [PATCH 11/26] Split Plain authentication tests to own section VeNCrypt is a multiplexer for many authentication methods, not just Plain. So let's split it to its own section, just like other types. --- tests/test.rfb.js | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 0a626343..5bb4001d 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1616,23 +1616,29 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); }); + }); - it('should support Plain authentication', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'username', password: 'password' }); - }); + describe('Plain Authentication (type 256) Handler', function () { + beforeEach(function () { + sendSecurity(19, client); + expect(client._sock).to.have.sent(new Uint8Array([19])); // VeNCrypt version client._sock._websocket._receiveData(new Uint8Array([0, 2])); expect(client._sock).to.have.sent(new Uint8Array([0, 2])); // Server ACK. client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list. + }); + + it('should support Plain authentication', function () { + client.addEventListener("credentialsrequired", () => { + client.sendCredentials({ username: 'username', password: 'password' }); + }); client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); clock.tick(); const expectedResponse = []; - push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); push32(expectedResponse, client._rfbCredentials.password.length); pushString(expectedResponse, client._rfbCredentials.username); @@ -1648,18 +1654,12 @@ describe('Remote Frame Buffer Protocol Client', function () { client.addEventListener("credentialsrequired", () => { client.sendCredentials({ username: 'username', password: '' }); }); - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); clock.tick(); const expectedResponse = []; - push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); push32(expectedResponse, client._rfbCredentials.password.length); pushString(expectedResponse, client._rfbCredentials.username); @@ -1675,18 +1675,12 @@ describe('Remote Frame Buffer Protocol Client', function () { client.addEventListener("credentialsrequired", () => { client.sendCredentials({ username: 'a'.repeat(300), password: 'b'.repeat(300) }); }); - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); + expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); clock.tick(); const expectedResponse = []; - push32(expectedResponse, 256); // Chosen subtype. push32(expectedResponse, client._rfbCredentials.username.length); push32(expectedResponse, client._rfbCredentials.password.length); pushString(expectedResponse, client._rfbCredentials.username); From 71bb6f02cdf993072172eefe138127e66943ca03 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 15:04:13 +0200 Subject: [PATCH 12/26] Fix Plain authentication test checks We should have constants local for the test function when doing comparisons or we might have false positives because we compare with buggy values in the code under test. --- tests/test.rfb.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 5bb4001d..f5410eb0 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1639,10 +1639,10 @@ describe('Remote Frame Buffer Protocol Client', function () { clock.tick(); const expectedResponse = []; - push32(expectedResponse, client._rfbCredentials.username.length); - push32(expectedResponse, client._rfbCredentials.password.length); - pushString(expectedResponse, client._rfbCredentials.username); - pushString(expectedResponse, client._rfbCredentials.password); + push32(expectedResponse, 8); + push32(expectedResponse, 8); + pushString(expectedResponse, 'username'); + pushString(expectedResponse, 'password'); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); sinon.spy(client, "_initMsg"); @@ -1660,10 +1660,10 @@ describe('Remote Frame Buffer Protocol Client', function () { clock.tick(); const expectedResponse = []; - push32(expectedResponse, client._rfbCredentials.username.length); - push32(expectedResponse, client._rfbCredentials.password.length); - pushString(expectedResponse, client._rfbCredentials.username); - pushString(expectedResponse, client._rfbCredentials.password); + push32(expectedResponse, 8); + push32(expectedResponse, 0); + pushString(expectedResponse, 'username'); + pushString(expectedResponse, ''); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); sinon.spy(client, "_initMsg"); @@ -1681,10 +1681,10 @@ describe('Remote Frame Buffer Protocol Client', function () { clock.tick(); const expectedResponse = []; - push32(expectedResponse, client._rfbCredentials.username.length); - push32(expectedResponse, client._rfbCredentials.password.length); - pushString(expectedResponse, client._rfbCredentials.username); - pushString(expectedResponse, client._rfbCredentials.password); + push32(expectedResponse, 300); + push32(expectedResponse, 300); + pushString(expectedResponse, 'a'.repeat(300)); + pushString(expectedResponse, 'b'.repeat(300)); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); sinon.spy(client, "_initMsg"); From 0ee0e96f34a3fa383e855b1f0b0e0b4b630e9311 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 19:23:25 +0200 Subject: [PATCH 13/26] Fix ARD authentication test to send real data Stop bypassing the data handling steps in the test as that means those parts don't get tested. --- tests/test.rfb.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index f5410eb0..c76ebf19 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1306,9 +1306,27 @@ describe('Remote Frame Buffer Protocol Client', function () { const serverPublicKey = legacyCrypto.exportKey("raw", serverKey.publicKey); const clientPublicKey = legacyCrypto.exportKey("raw", clientKey.publicKey); - await client._negotiateARDAuthAsync(128, serverPublicKey, clientKey); + let data = []; - client._negotiateARDAuth(); + data = data.concat(Array.from(generator)); + push16(data, prime.length); + data = data.concat(Array.from(prime)); + data = data.concat(Array.from(serverPublicKey)); + + client._sock._websocket._receiveData(new Uint8Array(data)); + + // FIXME: We don't have a good way to know when the + // async stuff is done, so we hook in to this + // internal function that is called at the + // end + await new Promise((resolve, reject) => { + sinon.stub(client, "_resumeAuthentication") + .callsFake(() => { + RFB.prototype._resumeAuthentication.call(client); + resolve(); + }); + }); + clock.tick(); expect(client._rfbInitState).to.equal('SecurityResult'); From 458405e05df5c9c04e6ec36a7e38725b04986c14 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 14 May 2023 17:59:01 +0200 Subject: [PATCH 14/26] Merge RSA-AES tests in to RFB tests These test the RFB class, so they should be with all other tests for that class. --- tests/test.ra2.js | 357 ------------------------------------------ tests/test.rfb.js | 385 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+), 357 deletions(-) delete mode 100644 tests/test.ra2.js diff --git a/tests/test.ra2.js b/tests/test.ra2.js deleted file mode 100644 index cc505b1f..00000000 --- a/tests/test.ra2.js +++ /dev/null @@ -1,357 +0,0 @@ -const expect = chai.expect; - -import RFB from '../core/rfb.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function fakeGetRandomValues(arr) { - if (arr.length === 16) { - arr.set(new Uint8Array([ - 0x1c, 0x08, 0xfe, 0x21, 0x78, 0xef, 0x4e, 0xf9, - 0x3f, 0x05, 0xec, 0xea, 0xd4, 0x6b, 0xa5, 0xd5, - ])); - } else { - arr.set(new Uint8Array([ - 0xee, 0xe2, 0xf1, 0x5a, 0x3c, 0xa7, 0xbe, 0x95, - 0x6f, 0x2a, 0x75, 0xfd, 0x62, 0x01, 0xcb, 0xbf, - 0x43, 0x74, 0xca, 0x47, 0x4d, 0xfb, 0x0f, 0xcf, - 0x3a, 0x6d, 0x55, 0x6b, 0x59, 0x3a, 0xf6, 0x87, - 0xcb, 0x03, 0xb7, 0x28, 0x35, 0x7b, 0x15, 0x8e, - 0xb6, 0xc8, 0x8f, 0x2d, 0x5e, 0x7b, 0x1c, 0x9a, - 0x32, 0x55, 0xe7, 0x64, 0x36, 0x25, 0x7b, 0xa3, - 0xe9, 0x4f, 0x6f, 0x97, 0xdc, 0xa4, 0xd4, 0x62, - 0x6d, 0x7f, 0xab, 0x02, 0x6b, 0x13, 0x56, 0x69, - 0xfb, 0xd0, 0xd4, 0x13, 0x76, 0xcd, 0x0d, 0xd0, - 0x1f, 0xd1, 0x0c, 0x63, 0x3a, 0x34, 0x20, 0x6c, - 0xbb, 0x60, 0x45, 0x82, 0x23, 0xfd, 0x7c, 0x77, - 0x6d, 0xcc, 0x5e, 0xaa, 0xc3, 0x0c, 0x43, 0xb7, - 0x8d, 0xc0, 0x27, 0x6e, 0xeb, 0x1d, 0x6c, 0x5f, - 0xd8, 0x1c, 0x3c, 0x1c, 0x60, 0x2e, 0x82, 0x15, - 0xfd, 0x2e, 0x5f, 0x3a, 0x15, 0x53, 0x14, 0x70, - 0x4f, 0xe1, 0x65, 0x68, 0x35, 0x6d, 0xc7, 0x64, - 0xdb, 0xdd, 0x09, 0x31, 0x4f, 0x7b, 0x6d, 0x6c, - 0x77, 0x59, 0x5e, 0x1e, 0xfa, 0x4b, 0x06, 0x14, - 0xbe, 0xdc, 0x9c, 0x3d, 0x7b, 0xed, 0xf3, 0x2b, - 0x19, 0x26, 0x11, 0x8e, 0x3f, 0xab, 0x73, 0x9a, - 0x0a, 0x3a, 0xaa, 0x85, 0x06, 0xd5, 0xca, 0x3f, - 0xc3, 0xe2, 0x33, 0x7f, 0x97, 0x74, 0x98, 0x8f, - 0x2f, 0xa5, 0xfc, 0x7e, 0xb1, 0x77, 0x71, 0x58, - 0xf0, 0xbc, 0x04, 0x59, 0xbb, 0xb4, 0xc6, 0xcc, - 0x0f, 0x06, 0xcd, 0xa2, 0xd5, 0x01, 0x2f, 0xb2, - 0x22, 0x0b, 0xfc, 0x1e, 0x59, 0x9f, 0xd3, 0x4f, - 0x30, 0x95, 0xc6, 0x80, 0x0f, 0x69, 0xf3, 0x4a, - 0xd4, 0x36, 0xb6, 0x5a, 0x0b, 0x16, 0x0d, 0x81, - 0x31, 0xb0, 0x69, 0xd4, 0x4e, - ])); - } -} - -async function fakeGeneratekey() { - let key = JSON.parse('{"alg":"RSA-OAEP-256","d":"B7QR2yI8sXjo8vQhJpX9odqqR\ -6wIuPrTM1B1JJEKVeSrr7OYcc1FRJ52Vap9LIAU-ezigs9QDvWMxknB8motLnG69Wck37nt9_z4s8l\ -FQp0nROA-oaR92HW34KNL1b2fEVWGI0N86h730MvTJC5O2cmKeMezIG-oNqbbfFyP8AW-WLdDlgZm1\ -1-FjzhbVpb0Bc7nRSgBPSV-EY6Sl-LuglxDx4LaTdQW7QE_WXoRUt-GYGfTseuFQQK5WeoyX3yBtQy\ -dpauW6rrgyWdtP4hDFIoZsX6w1i-UMWMMwlIB5FdnUSi26igVGADGpV_vGMP36bv-EHp0bY-Qp0gpI\ -fLfgQ","dp":"Z1v5UceFfV2bhmbG19eGYb30jFxqoRBq36PKNY7IunMs1keYy0FpLbyGhtgMZ1Ymm\ -c8wEzGYsCPEP-ykcun_rlyu7YxmcnyC9YQqTqLyqvO-7rUqDvk9TMfdqWFP6heADRhKZmEbmcau6_m\ -2MwwK9kOkMKWvpqp8_TpJMnAH7zE","dq":"OBacRE15aY3NtCR4cvP5os3sT70JbDdDLHT3IHZM6r\ -E35CYNpLDia2chm_wnMcYvKFW9zC2ajRZ15i9c_VXQzS7ZlTaQYBFyMt7kVhxMEMFsPv1crD6t3uEI\ -j0LNuNYyy0jkon_LPZKQFK654CiL-L2YaNXOH4HbHP02dWeVQIE","e":"AQAB","ext":true,"ke\ -y_ops":["decrypt"],"kty":"RSA","n":"m1c92ZFk9ZI6l_O4YFiNxbv0Ng94SB3yThy1P_mcqr\ -GDQkRiGVdcTxAk38T9PgLztmspF-6U5TAHO-gSmmW88AC9m6f1Mspps6r7zl-M_OG-TwvGzf3BDz8z\ -Eg1FPbZV7whO1M4TCAZ0PqwG7qCc6nK1WiAhaKrSpzuPdL1igfNBsX7qu5wgw4ZTTGSLbVC_LfULQ5\ -FADgFTRXUSaxm1F8C_Lwy6a2e4nTcXilmtN2IHUjHegzm-Tq2HizmR3ARdWJpESYIW5-AXoiqj29tD\ -rqCmu2WPkB2psVp83IzZfaQNQzjNfvA8GpimkcDCkP5VMRrtKCcG4ZAFnO-A3NBX_Q","p":"2Q_lN\ -L7vCOBzAppYzCZo3WSh0hX-MOZyPUznks5U2TjmfdNZoL6_FJRiGyyLvwSiZFdEAAvpAyESFfFigng\ -AqMLSf448nPg15VUGj533CotsEM0WpoEr1JCgqdUbgDAfJQIBcwOmegBqd7lWm7uzEnRCvouB70ybk\ -JfpdprhkVE","q":"tzTt-F3g2u_3Ctj26Ho9iN_wC_W0lXGzslLt5nLmss8JqdLoDDrijjU-gjeRh\ -7lgiuHdUc3dorfFKbaMNOjoW3QKqt9oZ1JM0HKeRw0X2PnWW_0WK6DK5ASWDTXbMq2sUZqJvYEyL74\ -H2Zrt0RPAux7XQLEVgND6ROdXnMJ70O0","qi":"qfl4cXQkz4BNqa2De0-PfdU-8d1w3onnaGqx1D\ -s2fHzD_SJ4cNghn2TksoT9Qo64b3pUjH9igi2pyEjomk6D12N6FG0e10u7vFKv3W5YqUOgTpYdbcWH\ -dZ2qZWJU0XQZIrF8jLGTOO4GYP6_9sJ5R7Wk_0MdqQy8qvixWD4zLcY"}'); - key = await window.crypto.subtle.importKey("jwk", key, { - name: "RSA-OAEP", - hash: {name: "SHA-256"} - }, true, ["decrypt"]); - return {privateKey: key}; -} - -const receiveData = new Uint8Array([ - // server public key - 0x00, 0x00, 0x08, 0x00, 0xac, 0x1a, 0xbc, 0x42, - 0x8a, 0x2a, 0x69, 0x65, 0x54, 0xf8, 0x9a, 0xe6, - 0x43, 0xaa, 0xf7, 0x27, 0xf6, 0x2a, 0xf8, 0x8f, - 0x36, 0xd4, 0xae, 0x54, 0x0f, 0x16, 0x28, 0x08, - 0xc2, 0x5b, 0xca, 0x23, 0xdc, 0x27, 0x88, 0x1a, - 0x12, 0x82, 0xa8, 0x54, 0xea, 0x00, 0x99, 0x8d, - 0x02, 0x1d, 0x77, 0x4a, 0xeb, 0xd0, 0x93, 0x40, - 0x79, 0x86, 0xcb, 0x37, 0xd4, 0xb2, 0xc7, 0xcd, - 0x93, 0xe1, 0x00, 0x4d, 0x86, 0xff, 0x97, 0x33, - 0x0c, 0xad, 0x51, 0x47, 0x45, 0x85, 0x56, 0x07, - 0x65, 0x21, 0x7c, 0x57, 0x6d, 0x68, 0x7d, 0xd7, - 0x00, 0x43, 0x0c, 0x9d, 0x3b, 0xa1, 0x5a, 0x11, - 0xed, 0x51, 0x77, 0xf9, 0xd1, 0x5b, 0x33, 0xd7, - 0x1a, 0xeb, 0x65, 0x57, 0xc0, 0x01, 0x51, 0xff, - 0x9b, 0x82, 0xb3, 0xeb, 0x82, 0xc2, 0x1f, 0xca, - 0x47, 0xc0, 0x6a, 0x09, 0xe0, 0xf7, 0xda, 0x39, - 0x85, 0x12, 0xe7, 0x45, 0x8d, 0xb4, 0x1a, 0xda, - 0xcb, 0x86, 0x58, 0x52, 0x37, 0x66, 0x9d, 0x8a, - 0xce, 0xf2, 0x18, 0x78, 0x7d, 0x7f, 0xf0, 0x07, - 0x94, 0x8e, 0x6b, 0x17, 0xd9, 0x00, 0x2a, 0x3a, - 0xb9, 0xd4, 0x77, 0xde, 0x70, 0x85, 0xc4, 0x3a, - 0x62, 0x10, 0x02, 0xee, 0xba, 0xd8, 0xc0, 0x62, - 0xd0, 0x8e, 0xc1, 0x98, 0x19, 0x8e, 0x39, 0x0f, - 0x3e, 0x1d, 0x61, 0xb1, 0x93, 0x13, 0x59, 0x39, - 0xcb, 0x96, 0xf2, 0x17, 0xc9, 0xe1, 0x41, 0xd3, - 0x20, 0xdd, 0x62, 0x5e, 0x7d, 0x53, 0xd6, 0xb7, - 0x1d, 0xfe, 0x02, 0x18, 0x1f, 0xe0, 0xef, 0x3d, - 0x94, 0xe3, 0x0a, 0x9c, 0x59, 0x54, 0xd8, 0x98, - 0x16, 0x9c, 0x31, 0xda, 0x41, 0x0f, 0x2e, 0x71, - 0x68, 0xe0, 0xa2, 0x62, 0x3e, 0xe5, 0x25, 0x31, - 0xcf, 0xfc, 0x67, 0x63, 0xc3, 0xb0, 0xda, 0x3f, - 0x7b, 0x59, 0xbe, 0x7e, 0x9e, 0xa8, 0xd0, 0x01, - 0x4f, 0x43, 0x7f, 0x8d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, - // server random - 0x01, 0x00, 0x5b, 0x58, 0x2a, 0x96, 0x2d, 0xbb, - 0x88, 0xec, 0xc3, 0x54, 0x00, 0xf3, 0xbb, 0xbe, - 0x17, 0xa3, 0x84, 0xd3, 0xef, 0xd8, 0x4a, 0x31, - 0x09, 0x20, 0xdd, 0xbc, 0x16, 0x9d, 0xc9, 0x5b, - 0x99, 0x62, 0x86, 0xfe, 0x0b, 0x28, 0x4b, 0xfe, - 0x5b, 0x56, 0x2d, 0xcb, 0x6e, 0x6f, 0xec, 0xf0, - 0x53, 0x0c, 0x33, 0x84, 0x93, 0xc9, 0xbf, 0x79, - 0xde, 0xb3, 0xb9, 0x29, 0x60, 0x78, 0xde, 0xe6, - 0x1d, 0xa7, 0x89, 0x48, 0x3f, 0xd1, 0x58, 0x66, - 0x27, 0x9c, 0xd4, 0x6e, 0x72, 0x9c, 0x6e, 0x4a, - 0xc0, 0x69, 0x79, 0x6f, 0x79, 0x0f, 0x13, 0xc4, - 0x20, 0xcf, 0xa6, 0xbb, 0xce, 0x18, 0x6d, 0xd5, - 0x9e, 0xd9, 0x67, 0xbe, 0x61, 0x43, 0x67, 0x11, - 0x76, 0x2f, 0xfd, 0x78, 0x75, 0x2b, 0x89, 0x35, - 0xdd, 0x0f, 0x13, 0x7f, 0xee, 0x78, 0xad, 0x32, - 0x56, 0x21, 0x81, 0x08, 0x1f, 0xcf, 0x4c, 0x29, - 0xa3, 0xeb, 0x89, 0x2d, 0xbe, 0xba, 0x8d, 0xe4, - 0x69, 0x28, 0xba, 0x53, 0x82, 0xce, 0x5c, 0xf6, - 0x5e, 0x5e, 0xa5, 0xb3, 0x88, 0xd8, 0x3d, 0xab, - 0xf4, 0x24, 0x9e, 0x3f, 0x04, 0xaf, 0xdc, 0x48, - 0x90, 0x53, 0x37, 0xe6, 0x82, 0x1d, 0xe0, 0x15, - 0x91, 0xa1, 0xc6, 0xa9, 0x54, 0xe5, 0x2a, 0xb5, - 0x64, 0x2d, 0x93, 0xc0, 0xc0, 0xe1, 0x0f, 0x6a, - 0x4b, 0xdb, 0x77, 0xf8, 0x4a, 0x0f, 0x83, 0x36, - 0xdd, 0x5e, 0x1e, 0xdd, 0x39, 0x65, 0xa2, 0x11, - 0xc2, 0xcf, 0x56, 0x1e, 0xa1, 0x29, 0xae, 0x11, - 0x9f, 0x3a, 0x82, 0xc7, 0xbd, 0x89, 0x6e, 0x59, - 0xb8, 0x59, 0x17, 0xcb, 0x65, 0xa0, 0x4b, 0x4d, - 0xbe, 0x33, 0x32, 0x85, 0x9c, 0xca, 0x5e, 0x95, - 0xc2, 0x5a, 0xd0, 0xc9, 0x8b, 0xf1, 0xf5, 0x14, - 0xcf, 0x76, 0x80, 0xc2, 0x24, 0x0a, 0x39, 0x7e, - 0x60, 0x64, 0xce, 0xd9, 0xb8, 0xad, 0x24, 0xa8, - 0xdf, 0xcb, - // server hash - 0x00, 0x14, 0x39, 0x30, 0x66, 0xb5, 0x66, 0x8a, - 0xcd, 0xb9, 0xda, 0xe0, 0xde, 0xcb, 0xf6, 0x47, - 0x5f, 0x54, 0x66, 0xe0, 0xbc, 0x49, 0x37, 0x01, - 0xf2, 0x9e, 0xef, 0xcc, 0xcd, 0x4d, 0x6c, 0x0e, - 0xc6, 0xab, 0x28, 0xd4, 0x7b, 0x13, - // subtype - 0x00, 0x01, 0x30, 0x2a, 0xc3, 0x0b, 0xc2, 0x1c, - 0xeb, 0x02, 0x44, 0x92, 0x5d, 0xfd, 0xf9, 0xa7, - 0x94, 0xd0, 0x19, -]); - -const sendData = new Uint8Array([ - // client public key - 0x00, 0x00, 0x08, 0x00, 0x9b, 0x57, 0x3d, 0xd9, - 0x91, 0x64, 0xf5, 0x92, 0x3a, 0x97, 0xf3, 0xb8, - 0x60, 0x58, 0x8d, 0xc5, 0xbb, 0xf4, 0x36, 0x0f, - 0x78, 0x48, 0x1d, 0xf2, 0x4e, 0x1c, 0xb5, 0x3f, - 0xf9, 0x9c, 0xaa, 0xb1, 0x83, 0x42, 0x44, 0x62, - 0x19, 0x57, 0x5c, 0x4f, 0x10, 0x24, 0xdf, 0xc4, - 0xfd, 0x3e, 0x02, 0xf3, 0xb6, 0x6b, 0x29, 0x17, - 0xee, 0x94, 0xe5, 0x30, 0x07, 0x3b, 0xe8, 0x12, - 0x9a, 0x65, 0xbc, 0xf0, 0x00, 0xbd, 0x9b, 0xa7, - 0xf5, 0x32, 0xca, 0x69, 0xb3, 0xaa, 0xfb, 0xce, - 0x5f, 0x8c, 0xfc, 0xe1, 0xbe, 0x4f, 0x0b, 0xc6, - 0xcd, 0xfd, 0xc1, 0x0f, 0x3f, 0x33, 0x12, 0x0d, - 0x45, 0x3d, 0xb6, 0x55, 0xef, 0x08, 0x4e, 0xd4, - 0xce, 0x13, 0x08, 0x06, 0x74, 0x3e, 0xac, 0x06, - 0xee, 0xa0, 0x9c, 0xea, 0x72, 0xb5, 0x5a, 0x20, - 0x21, 0x68, 0xaa, 0xd2, 0xa7, 0x3b, 0x8f, 0x74, - 0xbd, 0x62, 0x81, 0xf3, 0x41, 0xb1, 0x7e, 0xea, - 0xbb, 0x9c, 0x20, 0xc3, 0x86, 0x53, 0x4c, 0x64, - 0x8b, 0x6d, 0x50, 0xbf, 0x2d, 0xf5, 0x0b, 0x43, - 0x91, 0x40, 0x0e, 0x01, 0x53, 0x45, 0x75, 0x12, - 0x6b, 0x19, 0xb5, 0x17, 0xc0, 0xbf, 0x2f, 0x0c, - 0xba, 0x6b, 0x67, 0xb8, 0x9d, 0x37, 0x17, 0x8a, - 0x59, 0xad, 0x37, 0x62, 0x07, 0x52, 0x31, 0xde, - 0x83, 0x39, 0xbe, 0x4e, 0xad, 0x87, 0x8b, 0x39, - 0x91, 0xdc, 0x04, 0x5d, 0x58, 0x9a, 0x44, 0x49, - 0x82, 0x16, 0xe7, 0xe0, 0x17, 0xa2, 0x2a, 0xa3, - 0xdb, 0xdb, 0x43, 0xae, 0xa0, 0xa6, 0xbb, 0x65, - 0x8f, 0x90, 0x1d, 0xa9, 0xb1, 0x5a, 0x7c, 0xdc, - 0x8c, 0xd9, 0x7d, 0xa4, 0x0d, 0x43, 0x38, 0xcd, - 0x7e, 0xf0, 0x3c, 0x1a, 0x98, 0xa6, 0x91, 0xc0, - 0xc2, 0x90, 0xfe, 0x55, 0x31, 0x1a, 0xed, 0x28, - 0x27, 0x06, 0xe1, 0x90, 0x05, 0x9c, 0xef, 0x80, - 0xdc, 0xd0, 0x57, 0xfd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, - // client random - 0x01, 0x00, 0x84, 0x7f, 0x26, 0x54, 0x74, 0xf6, - 0x47, 0xaf, 0x33, 0x64, 0x0d, 0xa6, 0xe5, 0x30, - 0xba, 0xe6, 0xe4, 0x8e, 0x50, 0x40, 0x71, 0x1c, - 0x0e, 0x06, 0x63, 0xf5, 0x07, 0x2a, 0x26, 0x68, - 0xd6, 0xcf, 0xa6, 0x80, 0x84, 0x5e, 0x64, 0xd4, - 0x5e, 0x62, 0x31, 0xfe, 0x44, 0x51, 0x0b, 0x7c, - 0x4d, 0x55, 0xc5, 0x4a, 0x7e, 0x0d, 0x4d, 0x9b, - 0x84, 0xb4, 0x32, 0x2b, 0x4d, 0x8a, 0x34, 0x8d, - 0xc8, 0xcf, 0x19, 0x3b, 0x64, 0x82, 0x27, 0x9e, - 0xa7, 0x70, 0x2a, 0xc1, 0xb8, 0xf3, 0x6a, 0x3a, - 0xf2, 0x75, 0x6e, 0x1d, 0xeb, 0xb6, 0x70, 0x7a, - 0x15, 0x18, 0x38, 0x00, 0xb4, 0x4f, 0x55, 0xb5, - 0xd8, 0x03, 0x4e, 0xb8, 0x53, 0xff, 0x80, 0x62, - 0xf1, 0x9d, 0x27, 0xe8, 0x2a, 0x3d, 0x98, 0x19, - 0x32, 0x09, 0x7e, 0x9a, 0xb0, 0xc7, 0x46, 0x23, - 0x10, 0x85, 0x35, 0x00, 0x96, 0xce, 0xb3, 0x2c, - 0x84, 0x8d, 0xf4, 0x9e, 0xa8, 0x42, 0x67, 0xed, - 0x09, 0xa6, 0x09, 0x97, 0xb3, 0x64, 0x26, 0xfb, - 0x71, 0x11, 0x9b, 0x3f, 0xbb, 0x57, 0xb8, 0x5b, - 0x2e, 0xc5, 0x2d, 0x8c, 0x5c, 0xf7, 0xef, 0x27, - 0x25, 0x88, 0x42, 0x45, 0x43, 0xa4, 0xe7, 0xde, - 0xea, 0xf9, 0x15, 0x7b, 0x5d, 0x66, 0x24, 0xce, - 0xf7, 0xc8, 0x2f, 0xc5, 0xc0, 0x3d, 0xcd, 0xf2, - 0x62, 0xfc, 0x1a, 0x5e, 0xec, 0xff, 0xf1, 0x1b, - 0xc8, 0xdb, 0xc1, 0x0f, 0x54, 0x66, 0x9e, 0xfd, - 0x99, 0x9b, 0x23, 0x70, 0x62, 0x37, 0x80, 0xad, - 0x91, 0x6b, 0x84, 0x85, 0x6a, 0x4c, 0x80, 0x9e, - 0x60, 0x8a, 0x93, 0xa3, 0xc8, 0x8e, 0xc4, 0x4b, - 0x4d, 0xb4, 0x8e, 0x3e, 0xaf, 0xce, 0xcd, 0x83, - 0xe5, 0x21, 0x90, 0x95, 0x20, 0x3c, 0x82, 0xb4, - 0x7c, 0xab, 0x63, 0x9c, 0xae, 0xc3, 0xc9, 0x71, - 0x1a, 0xec, 0x34, 0x18, 0x47, 0xec, 0x5c, 0x4d, - 0xed, 0x84, - // client hash - 0x00, 0x14, 0x9c, 0x91, 0x9e, 0x76, 0xcf, 0x1e, - 0x66, 0x87, 0x5e, 0x29, 0xf1, 0x13, 0x80, 0xea, - 0x7d, 0xec, 0xae, 0xf9, 0x60, 0x01, 0xd3, 0x6f, - 0xb7, 0x9e, 0xb2, 0xcd, 0x2d, 0xc8, 0xf8, 0x84, - 0xb2, 0x9f, 0xc3, 0x7e, 0xb4, 0xbe, - // credentials - 0x00, 0x08, 0x9d, 0xc8, 0x3a, 0xb8, 0x80, 0x4f, - 0xe3, 0x52, 0xdb, 0x62, 0x9e, 0x97, 0x64, 0x82, - 0xa8, 0xa1, 0x6b, 0x7e, 0x4d, 0x68, 0x8c, 0x29, - 0x91, 0x38, -]); - -describe('RA2 handshake', function () { - let sock; - let rfb; - let sentData; - - before(() => { - FakeWebSocket.replace(); - sinon.stub(window.crypto, "getRandomValues").callsFake(fakeGetRandomValues); - sinon.stub(window.crypto.subtle, "generateKey").callsFake(fakeGeneratekey); - }); - after(() => { - FakeWebSocket.restore(); - window.crypto.getRandomValues.restore(); - window.crypto.subtle.generateKey.restore(); - }); - - it('should fire the serververification event', function (done) { - sentData = new Uint8Array(); - rfb = new RFB(document.createElement('div'), "ws://example.com"); - sock = rfb._sock; - sock.send = (data) => { - let res = new Uint8Array(sentData.length + data.length); - res.set(sentData); - res.set(data, sentData.length); - sentData = res; - }; - rfb._rfbInitState = "Security"; - rfb._rfbVersion = 3.8; - sock._websocket._receiveData(new Uint8Array([1, 6])); - rfb.addEventListener("serververification", (e) => { - expect(e.detail.publickey).to.eql(receiveData.slice(0, 516)); - done(); - }); - sock._websocket._receiveData(receiveData); - }); - - it('should handle approveServer and fire the credentialsrequired event', function (done) { - rfb.addEventListener("credentialsrequired", (e) => { - expect(e.detail.types).to.eql(["password"]); - done(); - }); - rfb.approveServer(); - }); - - it('should match sendData after sending credentials', function (done) { - rfb.addEventListener("securityresult", (event) => { - expect(sentData.slice(1)).to.eql(sendData); - done(); - }); - rfb.sendCredentials({ "password": "123456" }); - }); -}); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index c76ebf19..ef2c6491 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1255,6 +1255,391 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); + describe('RSA-AES Authentication (type 6) Handler', function () { + function fakeGetRandomValues(arr) { + if (arr.length === 16) { + arr.set(new Uint8Array([ + 0x1c, 0x08, 0xfe, 0x21, 0x78, 0xef, 0x4e, 0xf9, + 0x3f, 0x05, 0xec, 0xea, 0xd4, 0x6b, 0xa5, 0xd5, + ])); + } else { + arr.set(new Uint8Array([ + 0xee, 0xe2, 0xf1, 0x5a, 0x3c, 0xa7, 0xbe, 0x95, + 0x6f, 0x2a, 0x75, 0xfd, 0x62, 0x01, 0xcb, 0xbf, + 0x43, 0x74, 0xca, 0x47, 0x4d, 0xfb, 0x0f, 0xcf, + 0x3a, 0x6d, 0x55, 0x6b, 0x59, 0x3a, 0xf6, 0x87, + 0xcb, 0x03, 0xb7, 0x28, 0x35, 0x7b, 0x15, 0x8e, + 0xb6, 0xc8, 0x8f, 0x2d, 0x5e, 0x7b, 0x1c, 0x9a, + 0x32, 0x55, 0xe7, 0x64, 0x36, 0x25, 0x7b, 0xa3, + 0xe9, 0x4f, 0x6f, 0x97, 0xdc, 0xa4, 0xd4, 0x62, + 0x6d, 0x7f, 0xab, 0x02, 0x6b, 0x13, 0x56, 0x69, + 0xfb, 0xd0, 0xd4, 0x13, 0x76, 0xcd, 0x0d, 0xd0, + 0x1f, 0xd1, 0x0c, 0x63, 0x3a, 0x34, 0x20, 0x6c, + 0xbb, 0x60, 0x45, 0x82, 0x23, 0xfd, 0x7c, 0x77, + 0x6d, 0xcc, 0x5e, 0xaa, 0xc3, 0x0c, 0x43, 0xb7, + 0x8d, 0xc0, 0x27, 0x6e, 0xeb, 0x1d, 0x6c, 0x5f, + 0xd8, 0x1c, 0x3c, 0x1c, 0x60, 0x2e, 0x82, 0x15, + 0xfd, 0x2e, 0x5f, 0x3a, 0x15, 0x53, 0x14, 0x70, + 0x4f, 0xe1, 0x65, 0x68, 0x35, 0x6d, 0xc7, 0x64, + 0xdb, 0xdd, 0x09, 0x31, 0x4f, 0x7b, 0x6d, 0x6c, + 0x77, 0x59, 0x5e, 0x1e, 0xfa, 0x4b, 0x06, 0x14, + 0xbe, 0xdc, 0x9c, 0x3d, 0x7b, 0xed, 0xf3, 0x2b, + 0x19, 0x26, 0x11, 0x8e, 0x3f, 0xab, 0x73, 0x9a, + 0x0a, 0x3a, 0xaa, 0x85, 0x06, 0xd5, 0xca, 0x3f, + 0xc3, 0xe2, 0x33, 0x7f, 0x97, 0x74, 0x98, 0x8f, + 0x2f, 0xa5, 0xfc, 0x7e, 0xb1, 0x77, 0x71, 0x58, + 0xf0, 0xbc, 0x04, 0x59, 0xbb, 0xb4, 0xc6, 0xcc, + 0x0f, 0x06, 0xcd, 0xa2, 0xd5, 0x01, 0x2f, 0xb2, + 0x22, 0x0b, 0xfc, 0x1e, 0x59, 0x9f, 0xd3, 0x4f, + 0x30, 0x95, 0xc6, 0x80, 0x0f, 0x69, 0xf3, 0x4a, + 0xd4, 0x36, 0xb6, 0x5a, 0x0b, 0x16, 0x0d, 0x81, + 0x31, 0xb0, 0x69, 0xd4, 0x4e, + ])); + } + } + + async function fakeGeneratekey() { + let key = { "alg": "RSA-OAEP-256", + "d": "B7QR2yI8sXjo8vQhJpX9odqqR6wIuPr" + + "TM1B1JJEKVeSrr7OYcc1FRJ52Vap9LI" + + "AU-ezigs9QDvWMxknB8motLnG69Wck3" + + "7nt9_z4s8lFQp0nROA-oaR92HW34KNL" + + "1b2fEVWGI0N86h730MvTJC5O2cmKeMe" + + "zIG-oNqbbfFyP8AW-WLdDlgZm11-Fjz" + + "hbVpb0Bc7nRSgBPSV-EY6Sl-LuglxDx" + + "4LaTdQW7QE_WXoRUt-GYGfTseuFQQK5" + + "WeoyX3yBtQydpauW6rrgyWdtP4hDFIo" + + "ZsX6w1i-UMWMMwlIB5FdnUSi26igVGA" + + "DGpV_vGMP36bv-EHp0bY-Qp0gpIfLfgQ", + "dp": "Z1v5UceFfV2bhmbG19eGYb30jFxqoR" + + "Bq36PKNY7IunMs1keYy0FpLbyGhtgM" + + "Z1Ymmc8wEzGYsCPEP-ykcun_rlyu7Y" + + "xmcnyC9YQqTqLyqvO-7rUqDvk9TMfd" + + "qWFP6heADRhKZmEbmcau6_m2MwwK9k" + + "OkMKWvpqp8_TpJMnAH7zE", + "dq": "OBacRE15aY3NtCR4cvP5os3sT70JbD" + + "dDLHT3IHZM6rE35CYNpLDia2chm_wn" + + "McYvKFW9zC2ajRZ15i9c_VXQzS7ZlT" + + "aQYBFyMt7kVhxMEMFsPv1crD6t3uEI" + + "j0LNuNYyy0jkon_LPZKQFK654CiL-L" + + "2YaNXOH4HbHP02dWeVQIE", + "e": "AQAB", + "ext": true, + "key_ops": ["decrypt"], + "kty": "RSA", + "n": "m1c92ZFk9ZI6l_O4YFiNxbv0Ng94SB3" + + "yThy1P_mcqrGDQkRiGVdcTxAk38T9Pg" + + "LztmspF-6U5TAHO-gSmmW88AC9m6f1M" + + "spps6r7zl-M_OG-TwvGzf3BDz8zEg1F" + + "PbZV7whO1M4TCAZ0PqwG7qCc6nK1WiA" + + "haKrSpzuPdL1igfNBsX7qu5wgw4ZTTG" + + "SLbVC_LfULQ5FADgFTRXUSaxm1F8C_L" + + "wy6a2e4nTcXilmtN2IHUjHegzm-Tq2H" + + "izmR3ARdWJpESYIW5-AXoiqj29tDrqC" + + "mu2WPkB2psVp83IzZfaQNQzjNfvA8Gp" + + "imkcDCkP5VMRrtKCcG4ZAFnO-A3NBX_Q", + "p": "2Q_lNL7vCOBzAppYzCZo3WSh0hX-MOZ" + + "yPUznks5U2TjmfdNZoL6_FJRiGyyLvw" + + "SiZFdEAAvpAyESFfFigngAqMLSf448n" + + "Pg15VUGj533CotsEM0WpoEr1JCgqdUb" + + "gDAfJQIBcwOmegBqd7lWm7uzEnRCvou" + + "B70ybkJfpdprhkVE", + "q": "tzTt-F3g2u_3Ctj26Ho9iN_wC_W0lXG" + + "zslLt5nLmss8JqdLoDDrijjU-gjeRh7" + + "lgiuHdUc3dorfFKbaMNOjoW3QKqt9oZ" + + "1JM0HKeRw0X2PnWW_0WK6DK5ASWDTXb" + + "Mq2sUZqJvYEyL74H2Zrt0RPAux7XQLE" + + "VgND6ROdXnMJ70O0", + "qi": "qfl4cXQkz4BNqa2De0-PfdU-8d1w3o" + + "nnaGqx1Ds2fHzD_SJ4cNghn2TksoT9" + + "Qo64b3pUjH9igi2pyEjomk6D12N6FG" + + "0e10u7vFKv3W5YqUOgTpYdbcWHdZ2q" + + "ZWJU0XQZIrF8jLGTOO4GYP6_9sJ5R7" + + "Wk_0MdqQy8qvixWD4zLcY", + }; + key = await window.crypto.subtle.importKey("jwk", key, { + name: "RSA-OAEP", + hash: {name: "SHA-256"} + }, true, ["decrypt"]); + return {privateKey: key}; + } + + before(() => { + sinon.stub(window.crypto, "getRandomValues").callsFake(fakeGetRandomValues); + sinon.stub(window.crypto.subtle, "generateKey").callsFake(fakeGeneratekey); + }); + after(() => { + window.crypto.getRandomValues.restore(); + window.crypto.subtle.generateKey.restore(); + }); + + beforeEach(function () { + sendSecurity(6, client); + expect(client._sock).to.have.sent(new Uint8Array([6])); + }); + + const receiveData = new Uint8Array([ + // server public key + 0x00, 0x00, 0x08, 0x00, 0xac, 0x1a, 0xbc, 0x42, + 0x8a, 0x2a, 0x69, 0x65, 0x54, 0xf8, 0x9a, 0xe6, + 0x43, 0xaa, 0xf7, 0x27, 0xf6, 0x2a, 0xf8, 0x8f, + 0x36, 0xd4, 0xae, 0x54, 0x0f, 0x16, 0x28, 0x08, + 0xc2, 0x5b, 0xca, 0x23, 0xdc, 0x27, 0x88, 0x1a, + 0x12, 0x82, 0xa8, 0x54, 0xea, 0x00, 0x99, 0x8d, + 0x02, 0x1d, 0x77, 0x4a, 0xeb, 0xd0, 0x93, 0x40, + 0x79, 0x86, 0xcb, 0x37, 0xd4, 0xb2, 0xc7, 0xcd, + 0x93, 0xe1, 0x00, 0x4d, 0x86, 0xff, 0x97, 0x33, + 0x0c, 0xad, 0x51, 0x47, 0x45, 0x85, 0x56, 0x07, + 0x65, 0x21, 0x7c, 0x57, 0x6d, 0x68, 0x7d, 0xd7, + 0x00, 0x43, 0x0c, 0x9d, 0x3b, 0xa1, 0x5a, 0x11, + 0xed, 0x51, 0x77, 0xf9, 0xd1, 0x5b, 0x33, 0xd7, + 0x1a, 0xeb, 0x65, 0x57, 0xc0, 0x01, 0x51, 0xff, + 0x9b, 0x82, 0xb3, 0xeb, 0x82, 0xc2, 0x1f, 0xca, + 0x47, 0xc0, 0x6a, 0x09, 0xe0, 0xf7, 0xda, 0x39, + 0x85, 0x12, 0xe7, 0x45, 0x8d, 0xb4, 0x1a, 0xda, + 0xcb, 0x86, 0x58, 0x52, 0x37, 0x66, 0x9d, 0x8a, + 0xce, 0xf2, 0x18, 0x78, 0x7d, 0x7f, 0xf0, 0x07, + 0x94, 0x8e, 0x6b, 0x17, 0xd9, 0x00, 0x2a, 0x3a, + 0xb9, 0xd4, 0x77, 0xde, 0x70, 0x85, 0xc4, 0x3a, + 0x62, 0x10, 0x02, 0xee, 0xba, 0xd8, 0xc0, 0x62, + 0xd0, 0x8e, 0xc1, 0x98, 0x19, 0x8e, 0x39, 0x0f, + 0x3e, 0x1d, 0x61, 0xb1, 0x93, 0x13, 0x59, 0x39, + 0xcb, 0x96, 0xf2, 0x17, 0xc9, 0xe1, 0x41, 0xd3, + 0x20, 0xdd, 0x62, 0x5e, 0x7d, 0x53, 0xd6, 0xb7, + 0x1d, 0xfe, 0x02, 0x18, 0x1f, 0xe0, 0xef, 0x3d, + 0x94, 0xe3, 0x0a, 0x9c, 0x59, 0x54, 0xd8, 0x98, + 0x16, 0x9c, 0x31, 0xda, 0x41, 0x0f, 0x2e, 0x71, + 0x68, 0xe0, 0xa2, 0x62, 0x3e, 0xe5, 0x25, 0x31, + 0xcf, 0xfc, 0x67, 0x63, 0xc3, 0xb0, 0xda, 0x3f, + 0x7b, 0x59, 0xbe, 0x7e, 0x9e, 0xa8, 0xd0, 0x01, + 0x4f, 0x43, 0x7f, 0x8d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, + // server random + 0x01, 0x00, 0x5b, 0x58, 0x2a, 0x96, 0x2d, 0xbb, + 0x88, 0xec, 0xc3, 0x54, 0x00, 0xf3, 0xbb, 0xbe, + 0x17, 0xa3, 0x84, 0xd3, 0xef, 0xd8, 0x4a, 0x31, + 0x09, 0x20, 0xdd, 0xbc, 0x16, 0x9d, 0xc9, 0x5b, + 0x99, 0x62, 0x86, 0xfe, 0x0b, 0x28, 0x4b, 0xfe, + 0x5b, 0x56, 0x2d, 0xcb, 0x6e, 0x6f, 0xec, 0xf0, + 0x53, 0x0c, 0x33, 0x84, 0x93, 0xc9, 0xbf, 0x79, + 0xde, 0xb3, 0xb9, 0x29, 0x60, 0x78, 0xde, 0xe6, + 0x1d, 0xa7, 0x89, 0x48, 0x3f, 0xd1, 0x58, 0x66, + 0x27, 0x9c, 0xd4, 0x6e, 0x72, 0x9c, 0x6e, 0x4a, + 0xc0, 0x69, 0x79, 0x6f, 0x79, 0x0f, 0x13, 0xc4, + 0x20, 0xcf, 0xa6, 0xbb, 0xce, 0x18, 0x6d, 0xd5, + 0x9e, 0xd9, 0x67, 0xbe, 0x61, 0x43, 0x67, 0x11, + 0x76, 0x2f, 0xfd, 0x78, 0x75, 0x2b, 0x89, 0x35, + 0xdd, 0x0f, 0x13, 0x7f, 0xee, 0x78, 0xad, 0x32, + 0x56, 0x21, 0x81, 0x08, 0x1f, 0xcf, 0x4c, 0x29, + 0xa3, 0xeb, 0x89, 0x2d, 0xbe, 0xba, 0x8d, 0xe4, + 0x69, 0x28, 0xba, 0x53, 0x82, 0xce, 0x5c, 0xf6, + 0x5e, 0x5e, 0xa5, 0xb3, 0x88, 0xd8, 0x3d, 0xab, + 0xf4, 0x24, 0x9e, 0x3f, 0x04, 0xaf, 0xdc, 0x48, + 0x90, 0x53, 0x37, 0xe6, 0x82, 0x1d, 0xe0, 0x15, + 0x91, 0xa1, 0xc6, 0xa9, 0x54, 0xe5, 0x2a, 0xb5, + 0x64, 0x2d, 0x93, 0xc0, 0xc0, 0xe1, 0x0f, 0x6a, + 0x4b, 0xdb, 0x77, 0xf8, 0x4a, 0x0f, 0x83, 0x36, + 0xdd, 0x5e, 0x1e, 0xdd, 0x39, 0x65, 0xa2, 0x11, + 0xc2, 0xcf, 0x56, 0x1e, 0xa1, 0x29, 0xae, 0x11, + 0x9f, 0x3a, 0x82, 0xc7, 0xbd, 0x89, 0x6e, 0x59, + 0xb8, 0x59, 0x17, 0xcb, 0x65, 0xa0, 0x4b, 0x4d, + 0xbe, 0x33, 0x32, 0x85, 0x9c, 0xca, 0x5e, 0x95, + 0xc2, 0x5a, 0xd0, 0xc9, 0x8b, 0xf1, 0xf5, 0x14, + 0xcf, 0x76, 0x80, 0xc2, 0x24, 0x0a, 0x39, 0x7e, + 0x60, 0x64, 0xce, 0xd9, 0xb8, 0xad, 0x24, 0xa8, + 0xdf, 0xcb, + // server hash + 0x00, 0x14, 0x39, 0x30, 0x66, 0xb5, 0x66, 0x8a, + 0xcd, 0xb9, 0xda, 0xe0, 0xde, 0xcb, 0xf6, 0x47, + 0x5f, 0x54, 0x66, 0xe0, 0xbc, 0x49, 0x37, 0x01, + 0xf2, 0x9e, 0xef, 0xcc, 0xcd, 0x4d, 0x6c, 0x0e, + 0xc6, 0xab, 0x28, 0xd4, 0x7b, 0x13, + // subtype + 0x00, 0x01, 0x30, 0x2a, 0xc3, 0x0b, 0xc2, 0x1c, + 0xeb, 0x02, 0x44, 0x92, 0x5d, 0xfd, 0xf9, 0xa7, + 0x94, 0xd0, 0x19, + ]); + + const sendData = new Uint8Array([ + // client public key + 0x00, 0x00, 0x08, 0x00, 0x9b, 0x57, 0x3d, 0xd9, + 0x91, 0x64, 0xf5, 0x92, 0x3a, 0x97, 0xf3, 0xb8, + 0x60, 0x58, 0x8d, 0xc5, 0xbb, 0xf4, 0x36, 0x0f, + 0x78, 0x48, 0x1d, 0xf2, 0x4e, 0x1c, 0xb5, 0x3f, + 0xf9, 0x9c, 0xaa, 0xb1, 0x83, 0x42, 0x44, 0x62, + 0x19, 0x57, 0x5c, 0x4f, 0x10, 0x24, 0xdf, 0xc4, + 0xfd, 0x3e, 0x02, 0xf3, 0xb6, 0x6b, 0x29, 0x17, + 0xee, 0x94, 0xe5, 0x30, 0x07, 0x3b, 0xe8, 0x12, + 0x9a, 0x65, 0xbc, 0xf0, 0x00, 0xbd, 0x9b, 0xa7, + 0xf5, 0x32, 0xca, 0x69, 0xb3, 0xaa, 0xfb, 0xce, + 0x5f, 0x8c, 0xfc, 0xe1, 0xbe, 0x4f, 0x0b, 0xc6, + 0xcd, 0xfd, 0xc1, 0x0f, 0x3f, 0x33, 0x12, 0x0d, + 0x45, 0x3d, 0xb6, 0x55, 0xef, 0x08, 0x4e, 0xd4, + 0xce, 0x13, 0x08, 0x06, 0x74, 0x3e, 0xac, 0x06, + 0xee, 0xa0, 0x9c, 0xea, 0x72, 0xb5, 0x5a, 0x20, + 0x21, 0x68, 0xaa, 0xd2, 0xa7, 0x3b, 0x8f, 0x74, + 0xbd, 0x62, 0x81, 0xf3, 0x41, 0xb1, 0x7e, 0xea, + 0xbb, 0x9c, 0x20, 0xc3, 0x86, 0x53, 0x4c, 0x64, + 0x8b, 0x6d, 0x50, 0xbf, 0x2d, 0xf5, 0x0b, 0x43, + 0x91, 0x40, 0x0e, 0x01, 0x53, 0x45, 0x75, 0x12, + 0x6b, 0x19, 0xb5, 0x17, 0xc0, 0xbf, 0x2f, 0x0c, + 0xba, 0x6b, 0x67, 0xb8, 0x9d, 0x37, 0x17, 0x8a, + 0x59, 0xad, 0x37, 0x62, 0x07, 0x52, 0x31, 0xde, + 0x83, 0x39, 0xbe, 0x4e, 0xad, 0x87, 0x8b, 0x39, + 0x91, 0xdc, 0x04, 0x5d, 0x58, 0x9a, 0x44, 0x49, + 0x82, 0x16, 0xe7, 0xe0, 0x17, 0xa2, 0x2a, 0xa3, + 0xdb, 0xdb, 0x43, 0xae, 0xa0, 0xa6, 0xbb, 0x65, + 0x8f, 0x90, 0x1d, 0xa9, 0xb1, 0x5a, 0x7c, 0xdc, + 0x8c, 0xd9, 0x7d, 0xa4, 0x0d, 0x43, 0x38, 0xcd, + 0x7e, 0xf0, 0x3c, 0x1a, 0x98, 0xa6, 0x91, 0xc0, + 0xc2, 0x90, 0xfe, 0x55, 0x31, 0x1a, 0xed, 0x28, + 0x27, 0x06, 0xe1, 0x90, 0x05, 0x9c, 0xef, 0x80, + 0xdc, 0xd0, 0x57, 0xfd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, + // client random + 0x01, 0x00, 0x84, 0x7f, 0x26, 0x54, 0x74, 0xf6, + 0x47, 0xaf, 0x33, 0x64, 0x0d, 0xa6, 0xe5, 0x30, + 0xba, 0xe6, 0xe4, 0x8e, 0x50, 0x40, 0x71, 0x1c, + 0x0e, 0x06, 0x63, 0xf5, 0x07, 0x2a, 0x26, 0x68, + 0xd6, 0xcf, 0xa6, 0x80, 0x84, 0x5e, 0x64, 0xd4, + 0x5e, 0x62, 0x31, 0xfe, 0x44, 0x51, 0x0b, 0x7c, + 0x4d, 0x55, 0xc5, 0x4a, 0x7e, 0x0d, 0x4d, 0x9b, + 0x84, 0xb4, 0x32, 0x2b, 0x4d, 0x8a, 0x34, 0x8d, + 0xc8, 0xcf, 0x19, 0x3b, 0x64, 0x82, 0x27, 0x9e, + 0xa7, 0x70, 0x2a, 0xc1, 0xb8, 0xf3, 0x6a, 0x3a, + 0xf2, 0x75, 0x6e, 0x1d, 0xeb, 0xb6, 0x70, 0x7a, + 0x15, 0x18, 0x38, 0x00, 0xb4, 0x4f, 0x55, 0xb5, + 0xd8, 0x03, 0x4e, 0xb8, 0x53, 0xff, 0x80, 0x62, + 0xf1, 0x9d, 0x27, 0xe8, 0x2a, 0x3d, 0x98, 0x19, + 0x32, 0x09, 0x7e, 0x9a, 0xb0, 0xc7, 0x46, 0x23, + 0x10, 0x85, 0x35, 0x00, 0x96, 0xce, 0xb3, 0x2c, + 0x84, 0x8d, 0xf4, 0x9e, 0xa8, 0x42, 0x67, 0xed, + 0x09, 0xa6, 0x09, 0x97, 0xb3, 0x64, 0x26, 0xfb, + 0x71, 0x11, 0x9b, 0x3f, 0xbb, 0x57, 0xb8, 0x5b, + 0x2e, 0xc5, 0x2d, 0x8c, 0x5c, 0xf7, 0xef, 0x27, + 0x25, 0x88, 0x42, 0x45, 0x43, 0xa4, 0xe7, 0xde, + 0xea, 0xf9, 0x15, 0x7b, 0x5d, 0x66, 0x24, 0xce, + 0xf7, 0xc8, 0x2f, 0xc5, 0xc0, 0x3d, 0xcd, 0xf2, + 0x62, 0xfc, 0x1a, 0x5e, 0xec, 0xff, 0xf1, 0x1b, + 0xc8, 0xdb, 0xc1, 0x0f, 0x54, 0x66, 0x9e, 0xfd, + 0x99, 0x9b, 0x23, 0x70, 0x62, 0x37, 0x80, 0xad, + 0x91, 0x6b, 0x84, 0x85, 0x6a, 0x4c, 0x80, 0x9e, + 0x60, 0x8a, 0x93, 0xa3, 0xc8, 0x8e, 0xc4, 0x4b, + 0x4d, 0xb4, 0x8e, 0x3e, 0xaf, 0xce, 0xcd, 0x83, + 0xe5, 0x21, 0x90, 0x95, 0x20, 0x3c, 0x82, 0xb4, + 0x7c, 0xab, 0x63, 0x9c, 0xae, 0xc3, 0xc9, 0x71, + 0x1a, 0xec, 0x34, 0x18, 0x47, 0xec, 0x5c, 0x4d, + 0xed, 0x84, + // client hash + 0x00, 0x14, 0x9c, 0x91, 0x9e, 0x76, 0xcf, 0x1e, + 0x66, 0x87, 0x5e, 0x29, 0xf1, 0x13, 0x80, 0xea, + 0x7d, 0xec, 0xae, 0xf9, 0x60, 0x01, 0xd3, 0x6f, + 0xb7, 0x9e, 0xb2, 0xcd, 0x2d, 0xc8, 0xf8, 0x84, + 0xb2, 0x9f, 0xc3, 0x7e, 0xb4, 0xbe, + // credentials + 0x00, 0x08, 0x9d, 0xc8, 0x3a, 0xb8, 0x80, 0x4f, + 0xe3, 0x52, 0xdb, 0x62, 0x9e, 0x97, 0x64, 0x82, + 0xa8, 0xa1, 0x6b, 0x7e, 0x4d, 0x68, 0x8c, 0x29, + 0x91, 0x38, + ]); + + it('should fire the serververification event', function (done) { + client.addEventListener("serververification", (e) => { + expect(e.detail.publickey).to.eql(receiveData.slice(0, 516)); + done(); + }); + client._sock._websocket._receiveData(receiveData); + }); + + it('should handle approveServer and fire the credentialsrequired event', function (done) { + client.addEventListener("serververification", (e) => { + client.approveServer(); + }); + client.addEventListener("credentialsrequired", (e) => { + expect(e.detail.types).to.eql(["password"]); + done(); + }); + client._sock._websocket._receiveData(receiveData); + }); + + it('should match sendData after sending credentials', function (done) { + client.addEventListener("serververification", (e) => { + client.approveServer(); + }); + client.addEventListener("credentialsrequired", (e) => { + client.sendCredentials({ "password": "123456" }); + clock.tick(); + }); + client.addEventListener("securityresult", (event) => { + expect(client._sock).to.have.sent(sendData); + done(); + }); + client._sock._websocket._receiveData(receiveData); + }); + }); + describe('ARD Authentication (type 30) Handler', function () { let byteArray = new Uint8Array(Array.from(new Uint8Array(128).keys())); function fakeGetRandomValues(arr) { From afbb1da4d57f91fd0220c5282505ca752d25bdb3 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 16 May 2023 19:38:33 +0200 Subject: [PATCH 15/26] Remove custom RSA-AES event We shouldn't add extra, undocumented, API just for the tests. They need to figure out less invasive way to probe things. --- core/rfb.js | 1 - tests/test.rfb.js | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index d8e2de74..ea8dc0ff 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -1864,7 +1864,6 @@ export default class RFB extends EventTargetMixin { } }) .then(() => { - this.dispatchEvent(new CustomEvent('securityresult')); this._rfbInitState = "SecurityResult"; return true; }).finally(() => { diff --git a/tests/test.rfb.js b/tests/test.rfb.js index ef2c6491..f3939151 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1631,10 +1631,20 @@ describe('Remote Frame Buffer Protocol Client', function () { client.addEventListener("credentialsrequired", (e) => { client.sendCredentials({ "password": "123456" }); clock.tick(); - }); - client.addEventListener("securityresult", (event) => { - expect(client._sock).to.have.sent(sendData); - done(); + // FIXME: We don't have a good way to know when + // the async stuff is done, so we hook in + // to this internal function that is + // called at the end + new Promise((resolve, reject) => { + sinon.stub(client._sock._websocket, "send") + .callsFake((data) => { + FakeWebSocket.prototype.send.call(client._sock._websocket, data); + resolve(); + }); + }).then(() => { + expect(client._sock).to.have.sent(sendData); + done(); + }); }); client._sock._websocket._receiveData(receiveData); }); From 42bc251eb4ce8bff00db58a7972061590f79e94e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 19 May 2023 16:00:23 +0200 Subject: [PATCH 16/26] Make RSA-AES tests more asynchronous The code tested here makes heavy use of promises, so it is easier to test things also using promise centric code. --- tests/test.rfb.js | 102 ++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index f3939151..3825cebd 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1605,48 +1605,72 @@ describe('Remote Frame Buffer Protocol Client', function () { 0x91, 0x38, ]); - it('should fire the serververification event', function (done) { - client.addEventListener("serververification", (e) => { - expect(e.detail.publickey).to.eql(receiveData.slice(0, 516)); - done(); - }); - client._sock._websocket._receiveData(receiveData); - }); - - it('should handle approveServer and fire the credentialsrequired event', function (done) { - client.addEventListener("serververification", (e) => { - client.approveServer(); - }); - client.addEventListener("credentialsrequired", (e) => { - expect(e.detail.types).to.eql(["password"]); - done(); - }); - client._sock._websocket._receiveData(receiveData); - }); - - it('should match sendData after sending credentials', function (done) { - client.addEventListener("serververification", (e) => { - client.approveServer(); - }); - client.addEventListener("credentialsrequired", (e) => { - client.sendCredentials({ "password": "123456" }); - clock.tick(); - // FIXME: We don't have a good way to know when - // the async stuff is done, so we hook in - // to this internal function that is - // called at the end - new Promise((resolve, reject) => { - sinon.stub(client._sock._websocket, "send") - .callsFake((data) => { - FakeWebSocket.prototype.send.call(client._sock._websocket, data); - resolve(); - }); - }).then(() => { - expect(client._sock).to.have.sent(sendData); - done(); + it('should fire the serververification event', async function () { + let verification = new Promise((resolve, reject) => { + client.addEventListener("serververification", (e) => { + resolve(e.detail.publickey); }); }); + client._sock._websocket._receiveData(receiveData); + + expect(await verification).to.deep.equal(receiveData.slice(0, 516)); + }); + + it('should handle approveServer and fire the credentialsrequired event', async function () { + let verification = new Promise((resolve, reject) => { + client.addEventListener("serververification", (e) => { + resolve(e.detail.publickey); + }); + }); + let credentials = new Promise((resolve, reject) => { + client.addEventListener("credentialsrequired", (e) => { + resolve(e.detail.types); + }); + }); + + client._sock._websocket._receiveData(receiveData); + + await verification; + client.approveServer(); + + expect(await credentials).to.have.members(["password"]); + }); + + it('should send credentials to server', async function () { + let verification = new Promise((resolve, reject) => { + client.addEventListener("serververification", (e) => { + resolve(e.detail.publickey); + }); + }); + let credentials = new Promise((resolve, reject) => { + client.addEventListener("credentialsrequired", (e) => { + resolve(e.detail.types); + }); + }); + + client._sock._websocket._receiveData(receiveData); + + await verification; + client.approveServer(); + + await credentials; + client.sendCredentials({ "password": "123456" }); + clock.tick(); + + // FIXME: We don't have a good way to know when + // the async stuff is done, so we hook in + // to this internal function that is + // called at the end + await new Promise((resolve, reject) => { + sinon.stub(client._sock._websocket, "send") + .callsFake((data) => { + FakeWebSocket.prototype.send.call(client._sock._websocket, data); + resolve(); + }); + }); + + expect(client._sock).to.have.sent(sendData); }); }); From 549ccc7121a0ea4550c1854dc96b589a01be2ffd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 17 May 2023 13:15:11 +0200 Subject: [PATCH 17/26] Split RSA-AES test data Make the tests more clear what data is expected in the different stages of the handshake. --- tests/test.rfb.js | 60 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 3825cebd..bd91e3c9 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1378,8 +1378,7 @@ describe('Remote Frame Buffer Protocol Client', function () { expect(client._sock).to.have.sent(new Uint8Array([6])); }); - const receiveData = new Uint8Array([ - // server public key + const serverPublicKey = [ 0x00, 0x00, 0x08, 0x00, 0xac, 0x1a, 0xbc, 0x42, 0x8a, 0x2a, 0x69, 0x65, 0x54, 0xf8, 0x9a, 0xe6, 0x43, 0xaa, 0xf7, 0x27, 0xf6, 0x2a, 0xf8, 0x8f, @@ -1445,7 +1444,9 @@ describe('Remote Frame Buffer Protocol Client', function () { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - // server random + ]; + + const serverRandom = [ 0x01, 0x00, 0x5b, 0x58, 0x2a, 0x96, 0x2d, 0xbb, 0x88, 0xec, 0xc3, 0x54, 0x00, 0xf3, 0xbb, 0xbe, 0x17, 0xa3, 0x84, 0xd3, 0xef, 0xd8, 0x4a, 0x31, @@ -1479,20 +1480,23 @@ describe('Remote Frame Buffer Protocol Client', function () { 0xcf, 0x76, 0x80, 0xc2, 0x24, 0x0a, 0x39, 0x7e, 0x60, 0x64, 0xce, 0xd9, 0xb8, 0xad, 0x24, 0xa8, 0xdf, 0xcb, - // server hash + ]; + + const serverHash = [ 0x00, 0x14, 0x39, 0x30, 0x66, 0xb5, 0x66, 0x8a, 0xcd, 0xb9, 0xda, 0xe0, 0xde, 0xcb, 0xf6, 0x47, 0x5f, 0x54, 0x66, 0xe0, 0xbc, 0x49, 0x37, 0x01, 0xf2, 0x9e, 0xef, 0xcc, 0xcd, 0x4d, 0x6c, 0x0e, 0xc6, 0xab, 0x28, 0xd4, 0x7b, 0x13, - // subtype + ]; + + const subType = [ 0x00, 0x01, 0x30, 0x2a, 0xc3, 0x0b, 0xc2, 0x1c, 0xeb, 0x02, 0x44, 0x92, 0x5d, 0xfd, 0xf9, 0xa7, 0x94, 0xd0, 0x19, - ]); + ]; - const sendData = new Uint8Array([ - // client public key + const clientPublicKey = [ 0x00, 0x00, 0x08, 0x00, 0x9b, 0x57, 0x3d, 0xd9, 0x91, 0x64, 0xf5, 0x92, 0x3a, 0x97, 0xf3, 0xb8, 0x60, 0x58, 0x8d, 0xc5, 0xbb, 0xf4, 0x36, 0x0f, @@ -1558,7 +1562,9 @@ describe('Remote Frame Buffer Protocol Client', function () { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, - // client random + ]; + + const clientRandom = [ 0x01, 0x00, 0x84, 0x7f, 0x26, 0x54, 0x74, 0xf6, 0x47, 0xaf, 0x33, 0x64, 0x0d, 0xa6, 0xe5, 0x30, 0xba, 0xe6, 0xe4, 0x8e, 0x50, 0x40, 0x71, 0x1c, @@ -1592,18 +1598,22 @@ describe('Remote Frame Buffer Protocol Client', function () { 0x7c, 0xab, 0x63, 0x9c, 0xae, 0xc3, 0xc9, 0x71, 0x1a, 0xec, 0x34, 0x18, 0x47, 0xec, 0x5c, 0x4d, 0xed, 0x84, - // client hash + ]; + + const clientHash = [ 0x00, 0x14, 0x9c, 0x91, 0x9e, 0x76, 0xcf, 0x1e, 0x66, 0x87, 0x5e, 0x29, 0xf1, 0x13, 0x80, 0xea, 0x7d, 0xec, 0xae, 0xf9, 0x60, 0x01, 0xd3, 0x6f, 0xb7, 0x9e, 0xb2, 0xcd, 0x2d, 0xc8, 0xf8, 0x84, 0xb2, 0x9f, 0xc3, 0x7e, 0xb4, 0xbe, - // credentials + ]; + + const credentialsData = [ 0x00, 0x08, 0x9d, 0xc8, 0x3a, 0xb8, 0x80, 0x4f, 0xe3, 0x52, 0xdb, 0x62, 0x9e, 0x97, 0x64, 0x82, 0xa8, 0xa1, 0x6b, 0x7e, 0x4d, 0x68, 0x8c, 0x29, 0x91, 0x38, - ]); + ]; it('should fire the serververification event', async function () { let verification = new Promise((resolve, reject) => { @@ -1612,9 +1622,10 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - client._sock._websocket._receiveData(receiveData); + client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); + client._sock._websocket._receiveData(new Uint8Array(serverRandom)); - expect(await verification).to.deep.equal(receiveData.slice(0, 516)); + expect(await verification).to.deep.equal(new Uint8Array(serverPublicKey)); }); it('should handle approveServer and fire the credentialsrequired event', async function () { @@ -1629,11 +1640,15 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - client._sock._websocket._receiveData(receiveData); + client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); + client._sock._websocket._receiveData(new Uint8Array(serverRandom)); await verification; client.approveServer(); + client._sock._websocket._receiveData(new Uint8Array(serverHash)); + client._sock._websocket._receiveData(new Uint8Array(subType)); + expect(await credentials).to.have.members(["password"]); }); @@ -1649,12 +1664,23 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - client._sock._websocket._receiveData(receiveData); + client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); + client._sock._websocket._receiveData(new Uint8Array(serverRandom)); await verification; client.approveServer(); + client._sock._websocket._receiveData(new Uint8Array(serverHash)); + client._sock._websocket._receiveData(new Uint8Array(subType)); + await credentials; + + let expected = []; + expected = expected.concat(clientPublicKey); + expected = expected.concat(clientRandom); + expected = expected.concat(clientHash); + expect(client._sock).to.have.sent(new Uint8Array(expected)); + client.sendCredentials({ "password": "123456" }); clock.tick(); @@ -1670,7 +1696,7 @@ describe('Remote Frame Buffer Protocol Client', function () { }); }); - expect(client._sock).to.have.sent(sendData); + expect(client._sock).to.have.sent(new Uint8Array(credentialsData)); }); }); From 13fa6b5908c84cf53ec27252e83709df3e0b48c9 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 22 May 2023 13:05:10 +0200 Subject: [PATCH 18/26] Fix last rect test Avoid poking in to internals and instead test that the RFB object responds correctly to new messages. --- tests/test.rfb.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index bd91e3c9..8cb29bed 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2873,7 +2873,11 @@ describe('Remote Frame Buffer Protocol Client', function () { it('should handle the last_rect pseudo-encoding', function () { sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100); - expect(client._FBU.rects).to.equal(0); + // Send a bell message and make sure it is parsed + let spy = sinon.spy(); + client.addEventListener("bell", spy); + client._sock._websocket._receiveData(new Uint8Array([0x02])); + expect(spy).to.have.been.calledOnce; }); it('should handle the DesktopName pseudo-encoding', function () { From 0c80c68e92587ed739817027581726d3962c40cd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 22 May 2023 21:21:43 +0200 Subject: [PATCH 19/26] Avoid hooking in to RFB._fail for tests This is an internal function so we should not be examining it in the tests. Instead use the well defined public APIs to check for correct behaviour. --- tests/test.rfb.js | 97 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 8cb29bed..9936716e 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1081,9 +1081,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail on an invalid version', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + sendVer('002.000', client); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); }); @@ -1140,20 +1144,26 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail if there are no supported schemes', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + const authSchemes = [1, 32]; client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); it('should fail with the appropriate message if no types are sent', function () { const failureData = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115]; - sinon.spy(client, '_fail'); + let callback = sinon.spy(); + client.addEventListener("securityfailure", callback); + client._sock._websocket._receiveData(new Uint8Array(failureData)); - expect(client._fail).to.have.been.calledOnce; - expect(client._fail).to.have.been.calledWith( - 'Security negotiation failed on no security types (reason: whoops)'); + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.status).to.equal(1); + expect(callback.args[0][0].detail.reason).to.equal("whoops"); }); it('should transition to the Authentication state and continue on successful negotiation', function () { @@ -1177,11 +1187,14 @@ describe('Remote Frame Buffer Protocol Client', function () { sendVer('003.006\n', client); client._sock._websocket._getSentData(); + let callback = sinon.spy(); + client.addEventListener("securityfailure", callback); - sinon.spy(client, '_fail'); client._sock._websocket._receiveData(new Uint8Array(data)); - expect(client._fail).to.have.been.calledWith( - 'Security negotiation failed on authentication scheme (reason: Whoopsies)'); + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.status).to.equal(1); + expect(callback.args[0][0].detail.reason).to.equal("Whoopsies"); }); it('should transition straight to ServerInitialisation on "no auth" for versions < 3.7', function () { @@ -1205,9 +1218,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail on an unknown auth scheme', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + sendSecurity(57, client); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); describe('VNC Authentication (type 2) Handler', function () { @@ -1954,9 +1971,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail if no supported tunnels are listed', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); it('should choose the notunnel tunnel type', function () { @@ -1996,11 +2017,15 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail if there are no supported auth types', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); client._sock._websocket._getSentData(); // skip the tunnel choice here sendNumStrPairs([[23, 'stdv', 'badval__']], client); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); }); @@ -2011,9 +2036,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail with non-0.2 versions', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + client._sock._websocket._receiveData(new Uint8Array([0, 1])); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); it('should fail if there are no supported subtypes', function () { @@ -2023,9 +2052,11 @@ describe('Remote Frame Buffer Protocol Client', function () { // Server ACK. client._sock._websocket._receiveData(new Uint8Array([0])); // Subtype list - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 9, 0, 0, 1, 4])); - expect(client._fail).to.have.been.calledOnce; + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); it('should support standard types', function () { @@ -2460,10 +2491,14 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail on an unsupported encoding', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + const rectInfo = { x: 8, y: 11, width: 27, height: 32, encoding: 234 }; sendFbuMsg([rectInfo], [[]], client); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); describe('Message Encoding Handlers', function () { @@ -2909,9 +2944,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail on unknown XVP message types', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + client._sock._websocket._receiveData(new Uint8Array([250, 0, 10, 237])); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); }); @@ -3216,9 +3255,13 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should fail on an unknown message type', function () { - sinon.spy(client, "_fail"); + let callback = sinon.spy(); + client.addEventListener("disconnect", callback); + client._sock._websocket._receiveData(new Uint8Array([87])); - expect(client._fail).to.have.been.calledOnce; + + expect(callback).to.have.been.calledOnce; + expect(callback.args[0][0].detail.clean).to.be.false; }); }); From 336ec86997bdf639d5799c40a262a979019c9471 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 23 May 2023 07:22:10 +0200 Subject: [PATCH 20/26] Remove internal monitoring from Plain tests Tests should avoid poking in to the internals and should only look at external behaviour. --- tests/test.rfb.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 9936716e..5422b995 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -2138,10 +2138,6 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, 'username'); pushString(expectedResponse, 'password'); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - - sinon.spy(client, "_initMsg"); - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._initMsg).to.have.been.called; }); it('should support Plain authentication with an empty password', function () { @@ -2159,10 +2155,6 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, 'username'); pushString(expectedResponse, ''); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - - sinon.spy(client, "_initMsg"); - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._initMsg).to.have.been.called; }); it('should support Plain authentication with a very long username and password', function () { @@ -2180,10 +2172,6 @@ describe('Remote Frame Buffer Protocol Client', function () { pushString(expectedResponse, 'a'.repeat(300)); pushString(expectedResponse, 'b'.repeat(300)); expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - - sinon.spy(client, "_initMsg"); - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._initMsg).to.have.been.called; }); }); }); From e07ca6a8e2428939de72348a1edfa7b749dec00b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 28 May 2023 16:30:41 +0200 Subject: [PATCH 21/26] Fix Websock send tests Avoid poking around in the internals and instead test what is actually sent out on the WebSocket. --- tests/test.websock.js | 49 ++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/tests/test.websock.js b/tests/test.websock.js index 857fdca8..1a0ba233 100644 --- a/tests/test.websock.js +++ b/tests/test.websock.js @@ -6,7 +6,7 @@ import FakeWebSocket from './fake.websocket.js'; describe('Websock', function () { "use strict"; - describe('Queue methods', function () { + describe('Receive queue methods', function () { let sock; const RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); @@ -185,61 +185,48 @@ describe('Websock', function () { expect(sock.rQi).to.equal(5); }); }); + }); + + describe('Send queue methods', function () { + let sock; + + beforeEach(function () { + let websock = new FakeWebSocket(); + websock._open(); + sock = new Websock(); + sock.attach(websock); + }); describe('flush', function () { - beforeEach(function () { - sock._websocket = { - send: sinon.spy() - }; - }); - it('should actually send on the websocket', function () { - sock._websocket.bufferedAmount = 8; - sock._websocket.readyState = WebSocket.OPEN; sock._sQ = new Uint8Array([1, 2, 3]); sock._sQlen = 3; const encoded = sock._encodeMessage(); sock.flush(); - expect(sock._websocket.send).to.have.been.calledOnce; - expect(sock._websocket.send).to.have.been.calledWith(encoded); + expect(sock).to.have.sent(encoded); }); it('should not call send if we do not have anything queued up', function () { sock._sQlen = 0; - sock._websocket.bufferedAmount = 8; sock.flush(); - expect(sock._websocket.send).not.to.have.been.called; + expect(sock).to.have.sent(new Uint8Array([])); }); }); describe('send', function () { - beforeEach(function () { - sock.flush = sinon.spy(); - }); - - it('should add to the send queue', function () { + it('should send the given data immediately', function () { sock.send([1, 2, 3]); - const sq = sock.sQ; - expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3])); - }); - - it('should call flush', function () { - sock.send([1, 2, 3]); - expect(sock.flush).to.have.been.calledOnce; + expect(sock).to.have.sent(new Uint8Array([1, 2, 3])); }); }); describe('sendString', function () { - beforeEach(function () { - sock.send = sinon.spy(); - }); - - it('should call send after converting the string to an array', function () { + it('should send after converting the string to an array', function () { sock.sendString("\x01\x02\x03"); - expect(sock.send).to.have.been.calledWith([1, 2, 3]); + expect(sock).to.have.sent(new Uint8Array([1, 2, 3])); }); }); }); From 9c7576a5876750c1413a712ae71825e250fdeb10 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 29 May 2023 09:30:26 +0200 Subject: [PATCH 22/26] Remove bad Websock mock in tests This small object will not properly fake a Websock in more complex cases, so let's avoid it and create a real Websock instead. --- tests/test.rfb.js | 173 +++++++++++++++++++++++++++++----------------- 1 file changed, 111 insertions(+), 62 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 5422b995..7eae5605 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -297,77 +297,102 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('#sendCtrlAlDel', function () { it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () { - const expected = {_sQ: new Uint8Array(48), _sQlen: 0, flush: () => {}}; - RFB.messages.keyEvent(expected, 0xFFE3, 1); - RFB.messages.keyEvent(expected, 0xFFE9, 1); - RFB.messages.keyEvent(expected, 0xFFFF, 1); - RFB.messages.keyEvent(expected, 0xFFFF, 0); - RFB.messages.keyEvent(expected, 0xFFE9, 0); - RFB.messages.keyEvent(expected, 0xFFE3, 0); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.keyEvent(esock, 0xFFE3, 1); + RFB.messages.keyEvent(esock, 0xFFE9, 1); + RFB.messages.keyEvent(esock, 0xFFFF, 1); + RFB.messages.keyEvent(esock, 0xFFFF, 0); + RFB.messages.keyEvent(esock, 0xFFE9, 0); + RFB.messages.keyEvent(esock, 0xFFE3, 0); + let expected = ews._getSentData(); client.sendCtrlAltDel(); - expect(client._sock).to.have.sent(expected._sQ); + + expect(client._sock).to.have.sent(expected); }); it('should not send the keys if we are not in a normal state', function () { - sinon.spy(client._sock, 'flush'); client._rfbConnectionState = "connecting"; client.sendCtrlAltDel(); - expect(client._sock.flush).to.not.have.been.called; + expect(client._sock).to.have.sent(new Uint8Array([])); }); it('should not send the keys if we are set as view_only', function () { - sinon.spy(client._sock, 'flush'); client._viewOnly = true; client.sendCtrlAltDel(); - expect(client._sock.flush).to.not.have.been.called; + expect(client._sock).to.have.sent(new Uint8Array([])); }); }); describe('#sendKey', function () { it('should send a single key with the given code and state (down = true)', function () { - const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}}; - RFB.messages.keyEvent(expected, 123, 1); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.keyEvent(esock, 123, 1); + let expected = ews._getSentData(); + client.sendKey(123, 'Key123', true); - expect(client._sock).to.have.sent(expected._sQ); + + expect(client._sock).to.have.sent(expected); }); it('should send both a down and up event if the state is not specified', function () { - const expected = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}}; - RFB.messages.keyEvent(expected, 123, 1); - RFB.messages.keyEvent(expected, 123, 0); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.keyEvent(esock, 123, 1); + RFB.messages.keyEvent(esock, 123, 0); + let expected = ews._getSentData(); + client.sendKey(123, 'Key123'); - expect(client._sock).to.have.sent(expected._sQ); + + expect(client._sock).to.have.sent(expected); }); it('should not send the key if we are not in a normal state', function () { - sinon.spy(client._sock, 'flush'); client._rfbConnectionState = "connecting"; client.sendKey(123, 'Key123'); - expect(client._sock.flush).to.not.have.been.called; + expect(client._sock).to.have.sent(new Uint8Array([])); }); it('should not send the key if we are set as view_only', function () { - sinon.spy(client._sock, 'flush'); client._viewOnly = true; client.sendKey(123, 'Key123'); - expect(client._sock.flush).to.not.have.been.called; + expect(client._sock).to.have.sent(new Uint8Array([])); }); it('should send QEMU extended events if supported', function () { client._qemuExtKeyEventSupported = true; - const expected = {_sQ: new Uint8Array(12), _sQlen: 0, flush: () => {}}; - RFB.messages.QEMUExtendedKeyEvent(expected, 0x20, true, 0x0039); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.QEMUExtendedKeyEvent(esock, 0x20, true, 0x0039); + let expected = ews._getSentData(); + client.sendKey(0x20, 'Space', true); - expect(client._sock).to.have.sent(expected._sQ); + + expect(client._sock).to.have.sent(expected); }); it('should not send QEMU extended events if unknown key code', function () { client._qemuExtKeyEventSupported = true; - const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}}; - RFB.messages.keyEvent(expected, 123, 1); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.keyEvent(esock, 123, 1); + let expected = ews._getSentData(); + client.sendKey(123, 'FooBar', true); - expect(client._sock).to.have.sent(expected._sQ); + + expect(client._sock).to.have.sent(expected); }); }); @@ -2442,23 +2467,31 @@ describe('Remote Frame Buffer Protocol Client', function () { } it('should send an update request if there is sufficient data', function () { - const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}}; - RFB.messages.fbUpdateRequest(expectedMsg, true, 0, 0, 640, 20); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.fbUpdateRequest(esock, true, 0, 0, 640, 20); + let expected = ews._getSentData(); client._framebufferUpdate = () => true; client._sock._websocket._receiveData(new Uint8Array([0])); - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); }); it('should not send an update request if we need more data', function () { client._sock._websocket._receiveData(new Uint8Array([0])); - expect(client._sock._websocket._getSentData()).to.have.length(0); + expect(client._sock).to.have.sent(new Uint8Array([])); }); it('should resume receiving an update if we previously did not have enough data', function () { - const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}}; - RFB.messages.fbUpdateRequest(expectedMsg, true, 0, 0, 640, 20); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.fbUpdateRequest(esock, true, 0, 0, 640, 20); + let expected = ews._getSentData(); // just enough to set FBU.rects client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 3])); @@ -2467,7 +2500,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._framebufferUpdate = function () { this._sock.rQskipBytes(1); return true; }; // we magically have enough data // 247 should *not* be used as the message type here client._sock._websocket._receiveData(new Uint8Array([247])); - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); }); it('should not send a request in continuous updates mode', function () { @@ -2475,7 +2508,7 @@ describe('Remote Frame Buffer Protocol Client', function () { client._framebufferUpdate = () => true; client._sock._websocket._receiveData(new Uint8Array([0])); - expect(client._sock._websocket._getSentData()).to.have.length(0); + expect(client._sock).to.have.sent(new Uint8Array([])); }); it('should fail on an unsupported encoding', function () { @@ -3181,41 +3214,47 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should respond correctly to ServerFence', function () { - const expectedMsg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}}; - const incomingMsg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}}; - const payload = "foo\x00ab9"; + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + // ClientFence and ServerFence are identical in structure - RFB.messages.clientFence(expectedMsg, (1<<0) | (1<<1), payload); - RFB.messages.clientFence(incomingMsg, 0xffffffff, payload); + RFB.messages.clientFence(esock, (1<<0) | (1<<1), payload); + let expected = ews._getSentData(); + RFB.messages.clientFence(esock, 0xffffffff, payload); + let incoming = ews._getSentData(); - client._sock._websocket._receiveData(incomingMsg._sQ); + client._sock._websocket._receiveData(incoming); - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); - expectedMsg._sQlen = 0; - incomingMsg._sQlen = 0; + RFB.messages.clientFence(esock, (1<<0), payload); + expected = ews._getSentData(); + RFB.messages.clientFence(esock, (1<<0) | (1<<31), payload); + incoming = ews._getSentData(); - RFB.messages.clientFence(expectedMsg, (1<<0), payload); - RFB.messages.clientFence(incomingMsg, (1<<0) | (1<<31), payload); + client._sock._websocket._receiveData(incoming); - client._sock._websocket._receiveData(incomingMsg._sQ); - - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); }); it('should enable continuous updates on first EndOfContinousUpdates', function () { - const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}}; - - RFB.messages.enableContinuousUpdates(expectedMsg, true, 0, 0, 640, 20); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.enableContinuousUpdates(esock, true, 0, 0, 640, 20); + let expected = ews._getSentData(); expect(client._enabledContinuousUpdates).to.be.false; client._sock._websocket._receiveData(new Uint8Array([150])); expect(client._enabledContinuousUpdates).to.be.true; - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); }); it('should disable continuous updates on subsequent EndOfContinousUpdates', function () { @@ -3228,18 +3267,22 @@ describe('Remote Frame Buffer Protocol Client', function () { }); it('should update continuous updates on resize', function () { - const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}}; - RFB.messages.enableContinuousUpdates(expectedMsg, true, 0, 0, 90, 700); + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.enableContinuousUpdates(esock, true, 0, 0, 90, 700); + let expected = ews._getSentData(); client._resize(450, 160); - expect(client._sock._websocket._getSentData()).to.have.length(0); + expect(client._sock).to.have.sent(new Uint8Array([])); client._enabledContinuousUpdates = true; client._resize(90, 700); - expect(client._sock).to.have.sent(expectedMsg._sQ); + expect(client._sock).to.have.sent(expected); }); it('should fail on an unknown message type', function () { @@ -3662,10 +3705,16 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('Keyboard Events', function () { it('should send a key message on a key press', function () { + let esock = new Websock(); + let ews = new FakeWebSocket(); + ews._open(); + esock.attach(ews); + RFB.messages.keyEvent(esock, 0x41, 1); + let expected = ews._getSentData(); + client._handleKeyEvent(0x41, 'KeyA', true); - const keyMsg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}}; - RFB.messages.keyEvent(keyMsg, 0x41, 1); - expect(client._sock).to.have.sent(keyMsg._sQ); + + expect(client._sock).to.have.sent(expected); }); it('should not send messages in view-only mode', function () { From 9e02f4d01d85390b565cb041ad8f6c1a033d1740 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 29 May 2023 09:33:30 +0200 Subject: [PATCH 23/26] Return a copy of the data from FakeWebSocket The caller might hang on to the data for multiple calls, so we make sure the returned buffer might not get overwritten. --- tests/fake.websocket.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fake.websocket.js b/tests/fake.websocket.js index e5a2b2d6..a929a71f 100644 --- a/tests/fake.websocket.js +++ b/tests/fake.websocket.js @@ -42,7 +42,7 @@ export default class FakeWebSocket { } _getSentData() { - const res = new Uint8Array(this._sendQueue.buffer, 0, this.bufferedAmount); + const res = this._sendQueue.slice(0, this.bufferedAmount); this.bufferedAmount = 0; return res; } From 8ae789daf04b06c4ddcc6a6663476b03ccd6df58 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 30 May 2023 07:05:43 +0200 Subject: [PATCH 24/26] Add missing tests for message encodings All of these functions should have units tests, even if they are fairly minimal. --- tests/test.rfb.js | 132 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 5 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 7eae5605..14fd3924 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -4697,14 +4697,56 @@ describe('Remote Frame Buffer Protocol Client', function () { describe('RFB messages', function () { let sock; - before(function () { - FakeWebSocket.replace(); + beforeEach(function () { + let websock = new FakeWebSocket(); + websock._open(); sock = new Websock(); - sock.open(); + sock.attach(websock); }); - after(function () { - FakeWebSocket.restore(); + describe('Input Events', function () { + it('should send correct data for keyboard events', function () { + // FIXME: down should be boolean + RFB.messages.keyEvent(sock, 0x12345678, 0); + let expected = + [ 4, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]; + expect(sock).to.have.sent(new Uint8Array(expected)); + + RFB.messages.keyEvent(sock, 0x90abcdef, 1); + expected = + [ 4, 1, 0, 0, 0x90, 0xab, 0xcd, 0xef]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + + it('should send correct data for QEMU keyboard events', function () { + // FIXME: down should be boolean + RFB.messages.QEMUExtendedKeyEvent(sock, 0x12345678, 0, 0x55); + let expected = + [ 255, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x55]; + expect(sock).to.have.sent(new Uint8Array(expected)); + + RFB.messages.QEMUExtendedKeyEvent(sock, 0x90abcdef, 1, 0xe055); + expected = + [ 255, 0, 0, 1, 0x90, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0xd5]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + + it('should send correct data for pointer events', function () { + RFB.messages.pointerEvent(sock, 12345, 54321, 0xab); + let expected = + [ 5, 0xab, 0x30, 0x39, 0xd4, 0x31]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Clipboard Events', function () { + it('should send correct data for clipboard events', function () { + RFB.messages.clientCutText(sock, new Uint8Array([ 0x01, 0x23, 0x45, 0x67 ])); + let expected = + [ 6, 0, 0, 0, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x23, 0x45, 0x67 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); }); describe('Extended Clipboard Handling Send', function () { @@ -4855,4 +4897,84 @@ describe('RFB messages', function () { }); }); }); + + describe('Screen Layout', function () { + it('should send correct data for screen layout changes', function () { + RFB.messages.setDesktopSize(sock, 12345, 54321, 0x12345678, 0x90abcdef); + let expected = + [ 251, 0, 0x30, 0x39, 0xd4, 0x31, 0x01, 0x00, + 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x39, 0xd4, 0x31, 0x90, 0xab, 0xcd, 0xef ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Fences', function () { + it('should send correct data for fences', function () { + // FIXME: Payload should be a byte array + RFB.messages.clientFence(sock, 0x12345678, "text"); + let expected = + [ 248, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, + 4, 0x74, 0x65, 0x78, 0x74 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Continuous Updates', function () { + it('should send correct data for continuous updates configuration', function () { + // FIXME: enable should be boolean + RFB.messages.enableContinuousUpdates(sock, 0, 12345, 54321, 34343, 18181); + let expected = + [ 150, 0, 0x30, 0x39, 0xd4, 0x31, 0x86, 0x27, 0x47, 0x05 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Pixel Format', function () { + it('should send correct data for normal depth', function () { + RFB.messages.pixelFormat(sock, 24, true); + let expected = + [ 0, 0, 0, 0, 32, 24, 0, 1, + 0, 255, 0, 255, 0, 255, 0, 8, 16, 0, 0, 0 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + + it('should send correct data for low depth', function () { + RFB.messages.pixelFormat(sock, 8, true); + let expected = + [ 0, 0, 0, 0, 8, 8, 0, 1, + 0, 3, 0, 3, 0, 3, 0, 2, 4, 0, 0, 0 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Encodings', function () { + it('should send correct data for supported encodings', function () { + RFB.messages.clientEncodings(sock, [ 0x12345678, + 0x90abcdef, + 0x10293847 ]); + let expected = + [ 2, 0, 0, 3, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, + 0xef, 0x10, 0x29, 0x38, 0x47 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('Update request', function () { + it('should send correct data for update request', function () { + RFB.messages.fbUpdateRequest(sock, true, 12345, 54321, 34343, 18181); + let expected = + [ 3, 1, 0x30, 0x39, 0xd4, 0x31, 0x86, 0x27, 0x47, 0x05 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); + + describe('XVP operations', function () { + it('should send correct data for XVP operations', function () { + RFB.messages.xvpOp(sock, 123, 45); + let expected = + [ 250, 0, 123, 45 ]; + expect(sock).to.have.sent(new Uint8Array(expected)); + }); + }); }); From d33f5ce77fa8187dff1aeed55336ee56cb816739 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 30 May 2023 18:51:19 +0200 Subject: [PATCH 25/26] Make extended clipboard tests independent Let's test the full final result instead of assuming specific internal calls. --- tests/test.rfb.js | 152 +++++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 57 deletions(-) diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 14fd3924..01169cad 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -4750,20 +4750,14 @@ describe('RFB messages', function () { }); describe('Extended Clipboard Handling Send', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'clientCutText'); - }); - - afterEach(function () { - RFB.messages.clientCutText.restore(); - }); - it('should call clientCutText with correct Caps data', function () { let formats = { 0: 2, 2: 4121 }; - let expectedData = new Uint8Array([0x1F, 0x00, 0x00, 0x05, + let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xF4, + 0x1F, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19]); let actions = [ @@ -4775,26 +4769,30 @@ describe('RFB messages', function () { ]; RFB.messages.extendedClipboardCaps(sock, actions, formats); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + + expect(sock).to.have.sent(expectedData); }); it('should call clientCutText with correct Request data', function () { let formats = new Uint8Array([0x01]); - let expectedData = new Uint8Array([0x02, 0x00, 0x00, 0x01]); + let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFC, + 0x02, 0x00, 0x00, 0x01]); RFB.messages.extendedClipboardRequest(sock, formats); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + + expect(sock).to.have.sent(expectedData); }); it('should call clientCutText with correct Notify data', function () { let formats = new Uint8Array([0x01]); - let expectedData = new Uint8Array([0x08, 0x00, 0x00, 0x01]); + let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFC, + 0x08, 0x00, 0x00, 0x01]); RFB.messages.extendedClipboardNotify(sock, formats); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData); + + expect(sock).to.have.sent(expectedData); }); it('should call clientCutText with correct Provide data', function () { @@ -4804,16 +4802,24 @@ describe('RFB messages', function () { let deflatedData = deflateWithSize(expectedText); // Build Expected with flags and deflated data - let expectedData = new Uint8Array(4 + deflatedData.length); - expectedData[0] = 0x10; // The client capabilities - expectedData[1] = 0x00; // Reserved flags - expectedData[2] = 0x00; // Reserved flags - expectedData[3] = 0x01; // The formats client supports - expectedData.set(deflatedData, 4); + let expectedData = new Uint8Array(8 + 4 + deflatedData.length); + expectedData[0] = 0x06; // Message type + expectedData[1] = 0x00; + expectedData[2] = 0x00; + expectedData[3] = 0x00; + expectedData[4] = 0xFF; // Size + expectedData[5] = 0xFF; + expectedData[6] = 0xFF; + expectedData[7] = 256 - (4 + deflatedData.length); + expectedData[8] = 0x10; // The client capabilities + expectedData[9] = 0x00; // Reserved flags + expectedData[10] = 0x00; // Reserved flags + expectedData[11] = 0x01; // The formats client supports + expectedData.set(deflatedData, 12); RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + expect(sock).to.have.sent(expectedData); }); @@ -4826,16 +4832,24 @@ describe('RFB messages', function () { let deflatedData = deflateWithSize(expectedText); // Build Expected with flags and deflated data - let expectedData = new Uint8Array(4 + deflatedData.length); - expectedData[0] = 0x10; // The client capabilities - expectedData[1] = 0x00; // Reserved flags - expectedData[2] = 0x00; // Reserved flags - expectedData[3] = 0x01; // The formats client supports - expectedData.set(deflatedData, 4); + let expectedData = new Uint8Array(8 + 4 + deflatedData.length); + expectedData[0] = 0x06; // Message type + expectedData[1] = 0x00; + expectedData[2] = 0x00; + expectedData[3] = 0x00; + expectedData[4] = 0xFF; // Size + expectedData[5] = 0xFF; + expectedData[6] = 0xFF; + expectedData[7] = 256 - (4 + deflatedData.length); + expectedData[8] = 0x10; // The client capabilities + expectedData[9] = 0x00; // Reserved flags + expectedData[10] = 0x00; // Reserved flags + expectedData[11] = 0x01; // The formats client supports + expectedData.set(deflatedData, 12); RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + expect(sock).to.have.sent(expectedData); }); it('Carriage return Line feed', function () { @@ -4846,16 +4860,24 @@ describe('RFB messages', function () { let deflatedData = deflateWithSize(expectedText); // Build Expected with flags and deflated data - let expectedData = new Uint8Array(4 + deflatedData.length); - expectedData[0] = 0x10; // The client capabilities - expectedData[1] = 0x00; // Reserved flags - expectedData[2] = 0x00; // Reserved flags - expectedData[3] = 0x01; // The formats client supports - expectedData.set(deflatedData, 4); + let expectedData = new Uint8Array(8 + 4 + deflatedData.length); + expectedData[0] = 0x06; // Message type + expectedData[1] = 0x00; + expectedData[2] = 0x00; + expectedData[3] = 0x00; + expectedData[4] = 0xFF; // Size + expectedData[5] = 0xFF; + expectedData[6] = 0xFF; + expectedData[7] = 256 - (4 + deflatedData.length); + expectedData[8] = 0x10; // The client capabilities + expectedData[9] = 0x00; // Reserved flags + expectedData[10] = 0x00; // Reserved flags + expectedData[11] = 0x01; // The formats client supports + expectedData.set(deflatedData, 12); RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + expect(sock).to.have.sent(expectedData); }); it('Line feed', function () { @@ -4865,16 +4887,24 @@ describe('RFB messages', function () { let deflatedData = deflateWithSize(expectedText); // Build Expected with flags and deflated data - let expectedData = new Uint8Array(4 + deflatedData.length); - expectedData[0] = 0x10; // The client capabilities - expectedData[1] = 0x00; // Reserved flags - expectedData[2] = 0x00; // Reserved flags - expectedData[3] = 0x01; // The formats client supports - expectedData.set(deflatedData, 4); + let expectedData = new Uint8Array(8 + 4 + deflatedData.length); + expectedData[0] = 0x06; // Message type + expectedData[1] = 0x00; + expectedData[2] = 0x00; + expectedData[3] = 0x00; + expectedData[4] = 0xFF; // Size + expectedData[5] = 0xFF; + expectedData[6] = 0xFF; + expectedData[7] = 256 - (4 + deflatedData.length); + expectedData[8] = 0x10; // The client capabilities + expectedData[9] = 0x00; // Reserved flags + expectedData[10] = 0x00; // Reserved flags + expectedData[11] = 0x01; // The formats client supports + expectedData.set(deflatedData, 12); RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + expect(sock).to.have.sent(expectedData); }); it('Carriage return and Line feed mixed', function () { @@ -4884,16 +4914,24 @@ describe('RFB messages', function () { let deflatedData = deflateWithSize(expectedText); // Build Expected with flags and deflated data - let expectedData = new Uint8Array(4 + deflatedData.length); - expectedData[0] = 0x10; // The client capabilities - expectedData[1] = 0x00; // Reserved flags - expectedData[2] = 0x00; // Reserved flags - expectedData[3] = 0x01; // The formats client supports - expectedData.set(deflatedData, 4); + let expectedData = new Uint8Array(8 + 4 + deflatedData.length); + expectedData[0] = 0x06; // Message type + expectedData[1] = 0x00; + expectedData[2] = 0x00; + expectedData[3] = 0x00; + expectedData[4] = 0xFF; // Size + expectedData[5] = 0xFF; + expectedData[6] = 0xFF; + expectedData[7] = 256 - (4 + deflatedData.length); + expectedData[8] = 0x10; // The client capabilities + expectedData[9] = 0x00; // Reserved flags + expectedData[10] = 0x00; // Reserved flags + expectedData[11] = 0x01; // The formats client supports + expectedData.set(deflatedData, 12); RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true); + + expect(sock).to.have.sent(expectedData); }); }); }); From eb0ad829d23971377dc001dbe0fbdf47a0ea2a0f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 3 Jun 2023 15:36:29 +0200 Subject: [PATCH 26/26] Check that decoders consume all data This is extra important in the tests where we expect no changes to the display, as otherwise we can't tell the difference between success and a decoder that is simply waiting for more data. --- tests/test.copyrect.js | 27 ++++++++----- tests/test.hextile.js | 28 ++++++++----- tests/test.jpeg.js | 18 ++++++--- tests/test.raw.js | 89 +++++++++++++++++++++++++++--------------- tests/test.rre.js | 19 +++++---- tests/test.tight.js | 79 ++++++++++++++++++++++++------------- tests/test.tightpng.js | 10 +++-- tests/test.zrle.js | 54 +++++++++++++++++-------- 8 files changed, 215 insertions(+), 109 deletions(-) diff --git a/tests/test.copyrect.js b/tests/test.copyrect.js index 90ba0c68..a10cddce 100644 --- a/tests/test.copyrect.js +++ b/tests/test.copyrect.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('CopyRect Decoder', function () { @@ -47,12 +50,15 @@ describe('CopyRect Decoder', function () { display.fillRect(0, 0, 2, 2, [ 0x00, 0x00, 0xff ]); display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 0, 2, 2, 2, - [0x00, 0x02, 0x00, 0x00], - display, 24); - testDecodeRect(decoder, 2, 2, 2, 2, - [0x00, 0x00, 0x00, 0x00], - display, 24); + let done; + done = testDecodeRect(decoder, 0, 2, 2, 2, + [0x00, 0x02, 0x00, 0x00], + display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 2, 2, 2, + [0x00, 0x00, 0x00, 0x00], + display, 24); + expect(done).to.be.true; let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -69,7 +75,9 @@ describe('CopyRect Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [0x00, 0x00, 0x00, 0x00], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, + [0x00, 0x00, 0x00, 0x00], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -78,6 +86,7 @@ describe('CopyRect Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); }); diff --git a/tests/test.hextile.js b/tests/test.hextile.js index a7034f05..cbe6f7b5 100644 --- a/tests/test.hextile.js +++ b/tests/test.hextile.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } function push32(arr, num) { @@ -62,7 +65,7 @@ describe('Hextile Decoder', function () { data.push(2 | (2 << 4)); // x: 2, y: 2 data.push(1 | (1 << 4)); // width: 2, height: 2 - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -71,6 +74,7 @@ describe('Hextile Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -92,8 +96,9 @@ describe('Hextile Decoder', function () { data.push(0); } - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -102,13 +107,14 @@ describe('Hextile Decoder', function () { data.push(0x02); push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let expected = []; for (let i = 0; i < 16; i++) { push32(expected, 0x00ff00ff); } + expect(done).to.be.true; expect(display).to.have.displayed(new Uint8Array(expected)); }); @@ -125,7 +131,7 @@ describe('Hextile Decoder', function () { // send an empty frame data.push(0x00); - testDecodeRect(decoder, 0, 0, 32, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 32, 4, data, display, 24); let expected = []; for (let i = 0; i < 16; i++) { @@ -135,6 +141,7 @@ describe('Hextile Decoder', function () { push32(expected, 0x00ff00ff); // rect 2: same bkground color } + expect(done).to.be.true; expect(display).to.have.displayed(new Uint8Array(expected)); }); @@ -156,7 +163,7 @@ describe('Hextile Decoder', function () { data.push(2 | (2 << 4)); // x: 2, y: 2 data.push(1 | (1 << 4)); // width: 2, height: 2 - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -165,6 +172,7 @@ describe('Hextile Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -190,7 +198,7 @@ describe('Hextile Decoder', function () { data.push(0); // x: 0, y: 0 data.push(1 | (1 << 4)); // width: 2, height: 2 - testDecodeRect(decoder, 0, 0, 4, 17, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 17, data, display, 24); let targetData = [ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -205,6 +213,7 @@ describe('Hextile Decoder', function () { } expected = expected.concat(targetData.slice(0, 16)); + expect(done).to.be.true; expect(display).to.have.displayed(new Uint8Array(expected)); }); @@ -218,7 +227,7 @@ describe('Hextile Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -227,6 +236,7 @@ describe('Hextile Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); }); diff --git a/tests/test.jpeg.js b/tests/test.jpeg.js index 7580c617..5211cc7c 100644 --- a/tests/test.jpeg.js +++ b/tests/test.jpeg.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('JPEG Decoder', function () { @@ -131,7 +134,8 @@ describe('JPEG Decoder', function () { 0xff, 0xd9, ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + expect(decodeDone).to.be.true; let targetData = new Uint8Array([ 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, @@ -244,7 +248,10 @@ describe('JPEG Decoder', function () { 0xff, 0xd9, ]; - testDecodeRect(decoder, 0, 0, 4, 4, data1, display, 24); + let decodeDone; + + decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data1, display, 24); + expect(decodeDone).to.be.true; display.fillRect(0, 0, 4, 4, [128, 128, 128, 255]); @@ -265,7 +272,8 @@ describe('JPEG Decoder', function () { 0xcf, 0xff, 0x00, 0x0b, 0xab, 0x1f, 0xff, 0xd9, ]; - testDecodeRect(decoder, 0, 0, 4, 4, data2, display, 24); + decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data2, display, 24); + expect(decodeDone).to.be.true; let targetData = new Uint8Array([ 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, diff --git a/tests/test.raw.js b/tests/test.raw.js index bc7adc78..4a634ccd 100644 --- a/tests/test.raw.js +++ b/tests/test.raw.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('Raw Decoder', function () { @@ -42,22 +45,36 @@ describe('Raw Decoder', function () { }); it('should handle the Raw encoding', function () { - testDecodeRect(decoder, 0, 0, 2, 2, - [0xff, 0x00, 0x00, 0, 0x00, 0xff, 0x00, 0, - 0x00, 0xff, 0x00, 0, 0xff, 0x00, 0x00, 0], - display, 24); - testDecodeRect(decoder, 2, 0, 2, 2, - [0x00, 0x00, 0xff, 0, 0x00, 0x00, 0xff, 0, - 0x00, 0x00, 0xff, 0, 0x00, 0x00, 0xff, 0], - display, 24); - testDecodeRect(decoder, 0, 2, 4, 1, - [0xee, 0x00, 0xff, 0, 0x00, 0xee, 0xff, 0, - 0xaa, 0xee, 0xff, 0, 0xab, 0xee, 0xff, 0], - display, 24); - testDecodeRect(decoder, 0, 3, 4, 1, - [0xee, 0x00, 0xff, 0, 0x00, 0xee, 0xff, 0, - 0xaa, 0xee, 0xff, 0, 0xab, 0xee, 0xff, 0], - display, 24); + let done; + + done = testDecodeRect(decoder, 0, 0, 2, 2, + [0xff, 0x00, 0x00, 0, + 0x00, 0xff, 0x00, 0, + 0x00, 0xff, 0x00, 0, + 0xff, 0x00, 0x00, 0], + display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 0, 2, 2, + [0x00, 0x00, 0xff, 0, + 0x00, 0x00, 0xff, 0, + 0x00, 0x00, 0xff, 0, + 0x00, 0x00, 0xff, 0], + display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 2, 4, 1, + [0xee, 0x00, 0xff, 0, + 0x00, 0xee, 0xff, 0, + 0xaa, 0xee, 0xff, 0, + 0xab, 0xee, 0xff, 0], + display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 3, 4, 1, + [0xee, 0x00, 0xff, 0, + 0x00, 0xee, 0xff, 0, + 0xaa, 0xee, 0xff, 0, + 0xab, 0xee, 0xff, 0], + display, 24); + expect(done).to.be.true; let targetData = new Uint8Array([ 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, @@ -70,18 +87,24 @@ describe('Raw Decoder', function () { }); it('should handle the Raw encoding in low colour mode', function () { - testDecodeRect(decoder, 0, 0, 2, 2, - [0x30, 0x30, 0x30, 0x30], - display, 8); - testDecodeRect(decoder, 2, 0, 2, 2, - [0x0c, 0x0c, 0x0c, 0x0c], - display, 8); - testDecodeRect(decoder, 0, 2, 4, 1, - [0x0c, 0x0c, 0x30, 0x30], - display, 8); - testDecodeRect(decoder, 0, 3, 4, 1, - [0x0c, 0x0c, 0x30, 0x30], - display, 8); + let done; + + done = testDecodeRect(decoder, 0, 0, 2, 2, + [0x30, 0x30, 0x30, 0x30], + display, 8); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 0, 2, 2, + [0x0c, 0x0c, 0x0c, 0x0c], + display, 8); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 2, 4, 1, + [0x0c, 0x0c, 0x30, 0x30], + display, 8); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 3, 4, 1, + [0x0c, 0x0c, 0x30, 0x30], + display, 8); + expect(done).to.be.true; let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -98,7 +121,7 @@ describe('Raw Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -107,6 +130,7 @@ describe('Raw Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -115,7 +139,7 @@ describe('Raw Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [], display, 8); + let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 8); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -124,6 +148,7 @@ describe('Raw Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); }); diff --git a/tests/test.rre.js b/tests/test.rre.js index ac3aabbb..c55d7f39 100644 --- a/tests/test.rre.js +++ b/tests/test.rre.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } function push16(arr, num) { @@ -76,7 +79,7 @@ describe('RRE Decoder', function () { push16(data, 2); // width: 2 push16(data, 2); // height: 2 - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -85,6 +88,7 @@ describe('RRE Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -93,10 +97,10 @@ describe('RRE Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff ], - display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, + [ 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff ], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -105,6 +109,7 @@ describe('RRE Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); }); diff --git a/tests/test.tight.js b/tests/test.tight.js index cc5db36b..cc92c1a2 100644 --- a/tests/test.tight.js +++ b/tests/test.tight.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('Tight Decoder', function () { @@ -42,9 +45,9 @@ describe('Tight Decoder', function () { }); it('should handle fill rects', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x80, 0xff, 0x88, 0x44], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x80, 0xff, 0x88, 0x44], + display, 24); let targetData = new Uint8Array([ 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, @@ -53,21 +56,31 @@ describe('Tight Decoder', function () { 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle uncompressed copy rects', function () { + let done; let blueData = [ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff ]; let greenData = [ 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00 ]; - testDecodeRect(decoder, 0, 0, 2, 1, blueData, display, 24); - testDecodeRect(decoder, 0, 1, 2, 1, blueData, display, 24); - testDecodeRect(decoder, 2, 0, 2, 1, greenData, display, 24); - testDecodeRect(decoder, 2, 1, 2, 1, greenData, display, 24); - testDecodeRect(decoder, 0, 2, 2, 1, greenData, display, 24); - testDecodeRect(decoder, 0, 3, 2, 1, greenData, display, 24); - testDecodeRect(decoder, 2, 2, 2, 1, blueData, display, 24); - testDecodeRect(decoder, 2, 3, 2, 1, blueData, display, 24); + done = testDecodeRect(decoder, 0, 0, 2, 1, blueData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 1, 2, 1, blueData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 0, 2, 1, greenData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 1, 2, 1, greenData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 2, 2, 1, greenData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 3, 2, 1, greenData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 2, 2, 1, blueData, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 2, 3, 2, 1, blueData, display, 24); + expect(done).to.be.true; let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -89,7 +102,7 @@ describe('Tight Decoder', function () { 0x60, 0x82, 0x01, 0x99, 0x8d, 0x29, 0x02, 0xa6, 0x00, 0x7e, 0xbf, 0x0f, 0xf1 ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -98,6 +111,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -110,7 +124,7 @@ describe('Tight Decoder', function () { // Pixels 0x30, 0x30, 0xc0, 0xc0 ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -119,6 +133,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -135,7 +150,7 @@ describe('Tight Decoder', function () { 0x78, 0x9c, 0x33, 0x30, 0x38, 0x70, 0xc0, 0x00, 0x8a, 0x01, 0x21, 0x3c, 0x05, 0xa1 ]; - testDecodeRect(decoder, 0, 0, 4, 12, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 12, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -152,10 +167,12 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle uncompressed palette rects', function () { + let done; let data1 = [ // Control bytes 0x40, 0x01, @@ -171,8 +188,10 @@ describe('Tight Decoder', function () { // Pixels 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00 ]; - testDecodeRect(decoder, 0, 0, 4, 2, data1, display, 24); - testDecodeRect(decoder, 0, 2, 4, 2, data2, display, 24); + done = testDecodeRect(decoder, 0, 0, 4, 2, data1, display, 24); + expect(done).to.be.true; + done = testDecodeRect(decoder, 0, 2, 4, 2, data2, display, 24); + expect(done).to.be.true; let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -196,7 +215,7 @@ describe('Tight Decoder', function () { 0x62, 0x08, 0xc9, 0xc0, 0x00, 0x00, 0x00, 0x54, 0x00, 0x09 ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -205,6 +224,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -221,7 +241,7 @@ describe('Tight Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, [ 0x00 ], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, [ 0x00 ], display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -230,6 +250,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -238,10 +259,10 @@ describe('Tight Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x40, 0x01, 0x01, - 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff ], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, + [ 0x40, 0x01, 0x01, + 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff ], display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -250,6 +271,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -258,8 +280,9 @@ describe('Tight Decoder', function () { display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x80, 0xff, 0xff, 0xff ], display, 24); + let done = testDecodeRect(decoder, 1, 2, 0, 0, + [ 0x80, 0xff, 0xff, 0xff ], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, @@ -268,6 +291,7 @@ describe('Tight Decoder', function () { 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); @@ -369,7 +393,8 @@ describe('Tight Decoder', function () { 0x3f, 0xeb, 0xff, 0x00, 0xff, 0xd9, ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + expect(decodeDone).to.be.true; let targetData = new Uint8Array([ 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, diff --git a/tests/test.tightpng.js b/tests/test.tightpng.js index 253400b8..c72c20d7 100644 --- a/tests/test.tightpng.js +++ b/tests/test.tightpng.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('TightPng Decoder', function () { @@ -119,7 +122,8 @@ describe('TightPng Decoder', function () { 0xae, 0x42, 0x60, 0x82, ]; - testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); + expect(decodeDone).to.be.true; let targetData = new Uint8Array([ 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, diff --git a/tests/test.zrle.js b/tests/test.zrle.js index e09d208d..be046409 100644 --- a/tests/test.zrle.js +++ b/tests/test.zrle.js @@ -9,23 +9,26 @@ import FakeWebSocket from './fake.websocket.js'; function testDecodeRect(decoder, x, y, width, height, data, display, depth) { let sock; + let done = false; sock = new Websock; sock.open("ws://example.com"); sock.on('message', () => { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); }); // Empty messages are filtered at multiple layers, so we need to // do a direct call if (data.length === 0) { - decoder.decodeRect(x, y, width, height, sock, display, depth); + done = decoder.decodeRect(x, y, width, height, sock, display, depth); } else { sock._websocket._receiveData(new Uint8Array(data)); } display.flip(); + + return done; } describe('ZRLE Decoder', function () { @@ -42,9 +45,11 @@ describe('ZRLE Decoder', function () { }); it('should handle the Raw subencoding', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0e, 0x78, 0x5e, 0x62, 0x60, 0x60, 0xf8, 0x4f, 0x12, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x00, 0x00, 0x00, 0x0e, 0x78, 0x5e, + 0x62, 0x60, 0x60, 0xf8, 0x4f, 0x12, + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, @@ -53,13 +58,16 @@ describe('ZRLE Decoder', function () { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle the Solid subencoding', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0c, 0x78, 0x5e, 0x62, 0x64, 0x60, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x00, 0x00, 0x00, 0x0c, 0x78, 0x5e, + 0x62, 0x64, 0x60, 0xf8, 0x0f, 0x00, + 0x00, 0x00, 0xff, 0xff], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, @@ -68,14 +76,18 @@ describe('ZRLE Decoder', function () { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle the Palette Tile subencoding', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x12, 0x78, 0x5E, 0x62, 0x62, 0x60, 248, 0xff, 0x9F, 0x01, 0x08, 0x3E, 0x7C, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x00, 0x00, 0x00, 0x12, 0x78, 0x5E, + 0x62, 0x62, 0x60, 248, 0xff, 0x9F, + 0x01, 0x08, 0x3E, 0x7C, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, @@ -84,13 +96,16 @@ describe('ZRLE Decoder', function () { 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle the RLE Tile subencoding', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0d, 0x78, 0x5e, 0x6a, 0x60, 0x60, 0xf8, 0x2f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x00, 0x00, 0x00, 0x0d, 0x78, 0x5e, + 0x6a, 0x60, 0x60, 0xf8, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, @@ -99,13 +114,17 @@ describe('ZRLE Decoder', function () { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); }); it('should handle the RLE Palette Tile subencoding', function () { - testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x11, 0x78, 0x5e, 0x6a, 0x62, 0x60, 0xf8, 0xff, 0x9f, 0x81, 0xa1, 0x81, 0x1f, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); + let done = testDecodeRect(decoder, 0, 0, 4, 4, + [0x00, 0x00, 0x00, 0x11, 0x78, 0x5e, + 0x6a, 0x62, 0x60, 0xf8, 0xff, 0x9f, + 0x81, 0xa1, 0x81, 0x1f, 0x00, 0x00, + 0x00, 0xff, 0xff], + display, 24); let targetData = new Uint8Array([ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, @@ -114,6 +133,7 @@ describe('ZRLE Decoder', function () { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff ]); + expect(done).to.be.true; expect(display).to.have.displayed(targetData); });