summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2022-08-19 10:10:10 +0200
committerPierre Ossman <ossman@cendio.se>2022-08-19 10:10:10 +0200
commit1d148a8478f54084c9be433b2f35aee3e0a906a9 (patch)
tree063a081f67677576f830675fd9aabbae61ed1cee
parentcdfb33665195eb9a73fb00feb6ebaccd1068cd50 (diff)
parentdf8d005de960e2133ded0610656af591e92fc6b7 (diff)
downloadnovnc-1d148a8478f54084c9be433b2f35aee3e0a906a9.tar.gz
Merge branch 'authprio' of https://github.com/CendioOssman/noVNC
-rw-r--r--core/rfb.js223
-rw-r--r--tests/test.rfb.js231
2 files changed, 260 insertions, 194 deletions
diff --git a/core/rfb.js b/core/rfb.js
index 578a898..4b3526f 100644
--- a/core/rfb.js
+++ b/core/rfb.js
@@ -54,6 +54,21 @@ const GESTURE_SCRLSENS = 50;
const DOUBLE_TAP_TIMEOUT = 1000;
const DOUBLE_TAP_THRESHOLD = 50;
+// Security types
+const securityTypeNone = 1;
+const securityTypeVNCAuth = 2;
+const securityTypeRA2ne = 6;
+const securityTypeTight = 16;
+const securityTypeVeNCrypt = 19;
+const securityTypeXVP = 22;
+const securityTypeARD = 30;
+
+// Special Tight security types
+const securityTypeUnixLogon = 129;
+
+// VeNCrypt security types
+const securityTypePlain = 256;
+
// Extended clipboard pseudo-encoding formats
const extendedClipboardFormatText = 1;
/*eslint-disable no-unused-vars */
@@ -402,7 +417,7 @@ export default class RFB extends EventTargetMixin {
sendCredentials(creds) {
this._rfbCredentials = creds;
- setTimeout(this._initMsg.bind(this), 0);
+ this._resumeAuthentication();
}
sendCtrlAltDel() {
@@ -922,8 +937,15 @@ export default class RFB extends EventTargetMixin {
}
}
break;
+ case 'connecting':
+ while (this._rfbConnectionState === 'connecting') {
+ if (!this._initMsg()) {
+ break;
+ }
+ }
+ break;
default:
- this._initMsg();
+ Log.Error("Got data while in an invalid state");
break;
}
}
@@ -1332,6 +1354,21 @@ export default class RFB extends EventTargetMixin {
this._rfbInitState = 'Security';
}
+ _isSupportedSecurityType(type) {
+ const clientTypes = [
+ securityTypeNone,
+ securityTypeVNCAuth,
+ securityTypeRA2ne,
+ securityTypeTight,
+ securityTypeVeNCrypt,
+ securityTypeXVP,
+ securityTypeARD,
+ securityTypePlain,
+ ];
+
+ return clientTypes.includes(type);
+ }
+
_negotiateSecurity() {
if (this._rfbVersion >= 3.7) {
// Server sends supported list, client decides
@@ -1342,28 +1379,23 @@ export default class RFB extends EventTargetMixin {
this._rfbInitState = "SecurityReason";
this._securityContext = "no security types";
this._securityStatus = 1;
- return this._initMsg();
+ return true;
}
const types = this._sock.rQshiftBytes(numTypes);
Log.Debug("Server security types: " + types);
- // Look for each auth in preferred order
- if (types.includes(1)) {
- this._rfbAuthScheme = 1; // None
- } else if (types.includes(22)) {
- this._rfbAuthScheme = 22; // XVP
- } else if (types.includes(16)) {
- this._rfbAuthScheme = 16; // Tight
- } else if (types.includes(6)) {
- this._rfbAuthScheme = 6; // RA2ne Auth
- } else if (types.includes(2)) {
- this._rfbAuthScheme = 2; // VNC Auth
- } else if (types.includes(30)) {
- this._rfbAuthScheme = 30; // ARD Auth
- } else if (types.includes(19)) {
- this._rfbAuthScheme = 19; // VeNCrypt Auth
- } else {
+ // Look for a matching security type in the order that the
+ // server prefers
+ this._rfbAuthScheme = -1;
+ for (let type of types) {
+ if (this._isSupportedSecurityType(type)) {
+ this._rfbAuthScheme = type;
+ break;
+ }
+ }
+
+ if (this._rfbAuthScheme === -1) {
return this._fail("Unsupported security types (types: " + types + ")");
}
@@ -1377,14 +1409,14 @@ export default class RFB extends EventTargetMixin {
this._rfbInitState = "SecurityReason";
this._securityContext = "authentication scheme";
this._securityStatus = 1;
- return this._initMsg();
+ return true;
}
}
this._rfbInitState = 'Authentication';
Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
- return this._initMsg(); // jump to authentication
+ return true;
}
_handleSecurityReason() {
@@ -1434,7 +1466,7 @@ export default class RFB extends EventTargetMixin {
this._rfbCredentials.username +
this._rfbCredentials.target;
this._sock.sendString(xvpAuthStr);
- this._rfbAuthScheme = 2;
+ this._rfbAuthScheme = securityTypeVNCAuth;
return this._negotiateAuthentication();
}
@@ -1492,49 +1524,66 @@ export default class RFB extends EventTargetMixin {
subtypes.push(this._sock.rQshift32());
}
- // 256 = Plain subtype
- if (subtypes.indexOf(256) != -1) {
- // 0x100 = 256
- this._sock.send([0, 0, 1, 0]);
- this._rfbVeNCryptState = 4;
- } else {
- return this._fail("VeNCrypt Plain subtype not offered by server");
+ // Look for a matching security type in the order that the
+ // server prefers
+ this._rfbAuthScheme = -1;
+ for (let type of subtypes) {
+ // Avoid getting in to a loop
+ if (type === securityTypeVeNCrypt) {
+ continue;
+ }
+
+ if (this._isSupportedSecurityType(type)) {
+ this._rfbAuthScheme = type;
+ break;
+ }
}
- }
- // negotiated Plain subtype, server waits for password
- if (this._rfbVeNCryptState == 4) {
- if (this._rfbCredentials.username === undefined ||
- this._rfbCredentials.password === undefined) {
- this.dispatchEvent(new CustomEvent(
- "credentialsrequired",
- { detail: { types: ["username", "password"] } }));
- return false;
+ if (this._rfbAuthScheme === -1) {
+ return this._fail("Unsupported security types (types: " + subtypes + ")");
}
- const user = encodeUTF8(this._rfbCredentials.username);
- const pass = encodeUTF8(this._rfbCredentials.password);
-
- this._sock.send([
- (user.length >> 24) & 0xFF,
- (user.length >> 16) & 0xFF,
- (user.length >> 8) & 0xFF,
- user.length & 0xFF
- ]);
- this._sock.send([
- (pass.length >> 24) & 0xFF,
- (pass.length >> 16) & 0xFF,
- (pass.length >> 8) & 0xFF,
- pass.length & 0xFF
- ]);
- this._sock.sendString(user);
- this._sock.sendString(pass);
+ this._sock.send([this._rfbAuthScheme >> 24,
+ this._rfbAuthScheme >> 16,
+ this._rfbAuthScheme >> 8,
+ this._rfbAuthScheme]);
- this._rfbInitState = "SecurityResult";
+ this._rfbVeNCryptState == 4;
return true;
}
}
+ _negotiatePlainAuth() {
+ if (this._rfbCredentials.username === undefined ||
+ this._rfbCredentials.password === undefined) {
+ this.dispatchEvent(new CustomEvent(
+ "credentialsrequired",
+ { detail: { types: ["username", "password"] } }));
+ return false;
+ }
+
+ const user = encodeUTF8(this._rfbCredentials.username);
+ const pass = encodeUTF8(this._rfbCredentials.password);
+
+ this._sock.send([
+ (user.length >> 24) & 0xFF,
+ (user.length >> 16) & 0xFF,
+ (user.length >> 8) & 0xFF,
+ user.length & 0xFF
+ ]);
+ this._sock.send([
+ (pass.length >> 24) & 0xFF,
+ (pass.length >> 16) & 0xFF,
+ (pass.length >> 8) & 0xFF,
+ pass.length & 0xFF
+ ]);
+ this._sock.sendString(user);
+ this._sock.sendString(pass);
+
+ this._rfbInitState = "SecurityResult";
+ return true;
+ }
+
_negotiateStdVNCAuth() {
if (this._sock.rQwait("auth challenge", 16)) { return false; }
@@ -1661,7 +1710,7 @@ export default class RFB extends EventTargetMixin {
this._rfbCredentials.ardCredentials = encrypted;
this._rfbCredentials.ardPublicKey = clientPublicKey;
- setTimeout(this._initMsg.bind(this), 0);
+ this._resumeAuthentication();
}
_negotiateTightUnixAuth() {
@@ -1771,12 +1820,12 @@ export default class RFB extends EventTargetMixin {
case 'STDVNOAUTH__': // no auth
this._rfbInitState = 'SecurityResult';
return true;
- case 'STDVVNCAUTH_': // VNC auth
- this._rfbAuthScheme = 2;
- return this._initMsg();
- case 'TGHTULGNAUTH': // UNIX auth
- this._rfbAuthScheme = 129;
- return this._initMsg();
+ case 'STDVVNCAUTH_':
+ this._rfbAuthScheme = securityTypeVNCAuth;
+ return true;
+ case 'TGHTULGNAUTH':
+ this._rfbAuthScheme = securityTypeUnixLogon;
+ return true;
default:
return this._fail("Unsupported tiny auth scheme " +
"(scheme: " + authType + ")");
@@ -1813,7 +1862,7 @@ export default class RFB extends EventTargetMixin {
}).then(() => {
this.dispatchEvent(new CustomEvent('securityresult'));
this._rfbInitState = "SecurityResult";
- this._initMsg();
+ return true;
}).finally(() => {
this._rfbRSAAESAuthenticationState.removeEventListener(
"serververification", this._eventHandlers.handleRSAAESServerVerification);
@@ -1827,33 +1876,32 @@ export default class RFB extends EventTargetMixin {
_negotiateAuthentication() {
switch (this._rfbAuthScheme) {
- case 1: // no auth
- if (this._rfbVersion >= 3.8) {
- this._rfbInitState = 'SecurityResult';
- return true;
- }
- this._rfbInitState = 'ClientInitialisation';
- return this._initMsg();
+ case securityTypeNone:
+ this._rfbInitState = 'SecurityResult';
+ return true;
- case 22: // XVP auth
+ case securityTypeXVP:
return this._negotiateXvpAuth();
- case 30: // ARD auth
+ case securityTypeARD:
return this._negotiateARDAuth();
- case 2: // VNC authentication
+ case securityTypeVNCAuth:
return this._negotiateStdVNCAuth();
- case 16: // TightVNC Security Type
+ case securityTypeTight:
return this._negotiateTightAuth();
- case 19: // VeNCrypt Security Type
+ case securityTypeVeNCrypt:
return this._negotiateVeNCryptAuth();
- case 129: // TightVNC UNIX Security Type
+ case securityTypePlain:
+ return this._negotiatePlainAuth();
+
+ case securityTypeUnixLogon:
return this._negotiateTightUnixAuth();
- case 6: // RA2ne Security Type
+ case securityTypeRA2ne:
return this._negotiateRA2neAuth();
default:
@@ -1863,6 +1911,13 @@ export default class RFB extends EventTargetMixin {
}
_handleSecurityResult() {
+ // There is no security choice, and hence no security result
+ // until RFB 3.7
+ if (this._rfbVersion < 3.7) {
+ this._rfbInitState = 'ClientInitialisation';
+ return true;
+ }
+
if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
const status = this._sock.rQshift32();
@@ -1870,13 +1925,13 @@ export default class RFB extends EventTargetMixin {
if (status === 0) { // OK
this._rfbInitState = 'ClientInitialisation';
Log.Debug('Authentication OK');
- return this._initMsg();
+ return true;
} else {
if (this._rfbVersion >= 3.8) {
this._rfbInitState = "SecurityReason";
this._securityContext = "security result";
this._securityStatus = status;
- return this._initMsg();
+ return true;
} else {
this.dispatchEvent(new CustomEvent(
"securityfailure",
@@ -2052,6 +2107,14 @@ export default class RFB extends EventTargetMixin {
}
}
+ // Resume authentication handshake after it was paused for some
+ // reason, e.g. waiting for a password from the user
+ _resumeAuthentication() {
+ // We use setTimeout() so it's run in its own context, just like
+ // it originally did via the WebSocket's event handler
+ setTimeout(this._initMsg.bind(this), 0);
+ }
+
_handleSetColourMapMsg() {
Log.Debug("SetColorMapEntries");
diff --git a/tests/test.rfb.js b/tests/test.rfb.js
index 3f79bc0..75d1e11 100644
--- a/tests/test.rfb.js
+++ b/tests/test.rfb.js
@@ -1026,17 +1026,21 @@ describe('Remote Frame Buffer Protocol Client', function () {
client._rfbConnectionState = 'connecting';
});
- describe('ProtocolVersion', function () {
- function sendVer(ver, client) {
- const arr = new Uint8Array(12);
- for (let i = 0; i < ver.length; i++) {
- arr[i+4] = ver.charCodeAt(i);
- }
- arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
- arr[11] = '\n';
- client._sock._websocket._receiveData(arr);
+ function sendVer(ver, client) {
+ const arr = new Uint8Array(12);
+ for (let i = 0; i < ver.length; i++) {
+ arr[i+4] = ver.charCodeAt(i);
}
+ arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
+ arr[11] = '\n';
+ client._sock._websocket._receiveData(arr);
+ }
+ function sendSecurity(type, cl) {
+ cl._sock._websocket._receiveData(new Uint8Array([1, type]));
+ }
+
+ describe('ProtocolVersion', function () {
describe('version parsing', function () {
it('should interpret version 003.003 as version 3.3', function () {
sendVer('003.003', client);
@@ -1127,44 +1131,24 @@ describe('Remote Frame Buffer Protocol Client', function () {
describe('Security', function () {
beforeEach(function () {
- client._rfbInitState = 'Security';
- });
-
- it('should simply receive the auth scheme when for versions < 3.7', function () {
- client._rfbVersion = 3.6;
- const authSchemeRaw = [1, 2, 3, 4];
- const authScheme = (authSchemeRaw[0] << 24) + (authSchemeRaw[1] << 16) +
- (authSchemeRaw[2] << 8) + authSchemeRaw[3];
- client._sock._websocket._receiveData(new Uint8Array(authSchemeRaw));
- expect(client._rfbAuthScheme).to.equal(authScheme);
- });
-
- it('should prefer no authentication is possible', function () {
- client._rfbVersion = 3.7;
- const authSchemes = [2, 1, 3];
- client._sock._websocket._receiveData(new Uint8Array(authSchemes));
- expect(client._rfbAuthScheme).to.equal(1);
- expect(client._sock).to.have.sent(new Uint8Array([1, 1]));
+ sendVer('003.008\n', client);
+ client._sock._websocket._getSentData();
});
- it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
- client._rfbVersion = 3.7;
- const authSchemes = [2, 22, 16];
+ it('should respect server preference order', function () {
+ const authSchemes = [ 6, 79, 30, 188, 16, 6, 1 ];
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
- expect(client._rfbAuthScheme).to.equal(22);
- expect(client._sock).to.have.sent(new Uint8Array([22]));
+ expect(client._sock).to.have.sent(new Uint8Array([30]));
});
- it('should fail if there are no supported schemes for versions >= 3.7', function () {
+ it('should fail if there are no supported schemes', function () {
sinon.spy(client, "_fail");
- client._rfbVersion = 3.7;
const authSchemes = [1, 32];
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
expect(client._fail).to.have.been.calledOnce;
});
- it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
- client._rfbVersion = 3.7;
+ 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');
client._sock._websocket._receiveData(new Uint8Array(failureData));
@@ -1175,7 +1159,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
it('should transition to the Authentication state and continue on successful negotiation', function () {
- client._rfbVersion = 3.7;
const authSchemes = [1, 1];
client._negotiateAuthentication = sinon.spy();
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
@@ -1184,17 +1167,8 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
});
- describe('Authentication', function () {
- beforeEach(function () {
- client._rfbInitState = 'Security';
- });
-
- function sendSecurity(type, cl) {
- cl._sock._websocket._receiveData(new Uint8Array([1, type]));
- }
-
+ describe('Legacy Authentication', function () {
it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
- client._rfbVersion = 3.6;
const errMsg = "Whoopsies";
const data = [0, 0, 0, 0];
const errLen = errMsg.length;
@@ -1203,37 +1177,42 @@ describe('Remote Frame Buffer Protocol Client', function () {
data.push(errMsg.charCodeAt(i));
}
+ sendVer('003.006\n', client);
+ client._sock._websocket._getSentData();
+
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)');
});
- it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
- client._rfbVersion = 3.8;
- sendSecurity(1, client);
- expect(client._rfbInitState).to.equal('SecurityResult');
+ it('should transition straight to ServerInitialisation on "no auth" for versions < 3.7', function () {
+ sendVer('003.006\n', client);
+ client._sock._websocket._getSentData();
+
+ client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1]));
+ expect(client._rfbInitState).to.equal('ServerInitialisation');
+ });
+ });
+
+ describe('Authentication', function () {
+ beforeEach(function () {
+ sendVer('003.008\n', client);
+ client._sock._websocket._getSentData();
});
- it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
- client._rfbVersion = 3.7;
+ it('should transition straight to SecurityResult on "no auth" (1)', function () {
sendSecurity(1, client);
- expect(client._rfbInitState).to.equal('ServerInitialisation');
+ expect(client._rfbInitState).to.equal('SecurityResult');
});
it('should fail on an unknown auth scheme', function () {
sinon.spy(client, "_fail");
- client._rfbVersion = 3.8;
sendSecurity(57, client);
expect(client._fail).to.have.been.calledOnce;
});
describe('VNC Authentication (type 2) Handler', function () {
- beforeEach(function () {
- client._rfbInitState = 'Security';
- client._rfbVersion = 3.8;
- });
-
it('should fire the credentialsrequired event if missing a password', function () {
const spy = sinon.spy();
client.addEventListener("credentialsrequired", spy);
@@ -1274,12 +1253,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
describe('ARD Authentication (type 30) Handler', function () {
-
- beforeEach(function () {
- client._rfbInitState = 'Security';
- client._rfbVersion = 3.8;
- });
-
it('should fire the credentialsrequired event if all credentials are missing', function () {
const spy = sinon.spy();
client.addEventListener("credentialsrequired", spy);
@@ -1347,11 +1320,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
describe('XVP Authentication (type 22) Handler', function () {
- beforeEach(function () {
- client._rfbInitState = 'Security';
- client._rfbVersion = 3.8;
- });
-
it('should fall through to standard VNC authentication upon completion', function () {
client._rfbCredentials = { username: 'user',
target: 'target',
@@ -1400,8 +1368,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
describe('TightVNC Authentication (type 16) Handler', function () {
beforeEach(function () {
- client._rfbInitState = 'Security';
- client._rfbVersion = 3.8;
sendSecurity(16, client);
client._sock._websocket._getSentData(); // skip the security reply
});
@@ -1487,8 +1453,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
describe('VeNCrypt Authentication (type 19) Handler', function () {
beforeEach(function () {
- client._rfbInitState = 'Security';
- client._rfbVersion = 3.8;
sendSecurity(19, client);
expect(client._sock).to.have.sent(new Uint8Array([19]));
});
@@ -1499,18 +1463,70 @@ describe('Remote Frame Buffer Protocol Client', function () {
expect(client._fail).to.have.been.calledOnce;
});
- it('should fail if the Plain authentication is not present', function () {
+ it('should fail if there are no supported subtypes', function () {
// 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, only list subtype 1.
+ // Subtype list
sinon.spy(client, "_fail");
- client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 0, 1]));
+ client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 9, 0, 0, 1, 4]));
expect(client._fail).to.have.been.calledOnce;
});
+ it('should support standard types', function () {
+ // 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([2, 0, 0, 0, 2, 0, 0, 1, 4]));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 2); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
+ it('should respect server preference order', function () {
+ // 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
+ let subtypes = [ 6 ];
+ push32(subtypes, 79);
+ push32(subtypes, 30);
+ push32(subtypes, 188);
+ push32(subtypes, 256);
+ push32(subtypes, 6);
+ push32(subtypes, 1);
+ client._sock._websocket._receiveData(new Uint8Array(subtypes));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 30); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
+ it('should ignore redundant VeNCrypt subtype', function () {
+ // 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([2, 0, 0, 0, 19, 0, 0, 0, 2]));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 2); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
it('should support Plain authentication', function () {
client._rfbCredentials = { username: 'username', password: 'password' };
// VeNCrypt version
@@ -1582,70 +1598,57 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
});
- describe('SecurityResult', function () {
+ describe('Legacy SecurityResult', function () {
beforeEach(function () {
- client._rfbInitState = 'SecurityResult';
- });
-
- it('should fall through to ServerInitialisation on a response code of 0', function () {
- client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
- expect(client._rfbInitState).to.equal('ServerInitialisation');
- });
-
- it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
- client._rfbVersion = 3.8;
- sinon.spy(client, '_fail');
- const failureData = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
- client._sock._websocket._receiveData(new Uint8Array(failureData));
- expect(client._fail).to.have.been.calledWith(
- 'Security negotiation failed on security result (reason: whoops)');
- });
-
- it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
- sinon.spy(client, '_fail');
- client._rfbVersion = 3.7;
- client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1]));
- expect(client._fail).to.have.been.calledWith(
- 'Security handshake failed');
+ sendVer('003.007\n', client);
+ client._sock._websocket._getSentData();
+ sendSecurity(1, client);
+ client._sock._websocket._getSentData();
});
- it('should result in securityfailure event when receiving a non zero status', function () {
+ it('should not include reason in securityfailure event', function () {
const spy = sinon.spy();
client.addEventListener("securityfailure", spy);
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
expect(spy).to.have.been.calledOnce;
expect(spy.args[0][0].detail.status).to.equal(2);
+ expect('reason' in spy.args[0][0].detail).to.be.false;
+ });
+ });
+
+ describe('SecurityResult', function () {
+ beforeEach(function () {
+ sendVer('003.008\n', client);
+ client._sock._websocket._getSentData();
+ sendSecurity(1, client);
+ client._sock._websocket._getSentData();
+ });
+
+ it('should fall through to ServerInitialisation on a response code of 0', function () {
+ client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
+ expect(client._rfbInitState).to.equal('ServerInitialisation');
});
it('should include reason when provided in securityfailure event', function () {
- client._rfbVersion = 3.8;
const spy = sinon.spy();
client.addEventListener("securityfailure", spy);
const failureData = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
32, 102, 97, 105, 108, 117, 114, 101];
client._sock._websocket._receiveData(new Uint8Array(failureData));
+ expect(spy).to.have.been.calledOnce;
expect(spy.args[0][0].detail.status).to.equal(1);
expect(spy.args[0][0].detail.reason).to.equal('such failure');
});
it('should not include reason when length is zero in securityfailure event', function () {
- client._rfbVersion = 3.9;
const spy = sinon.spy();
client.addEventListener("securityfailure", spy);
const failureData = [0, 0, 0, 1, 0, 0, 0, 0];
client._sock._websocket._receiveData(new Uint8Array(failureData));
+ expect(spy).to.have.been.calledOnce;
expect(spy.args[0][0].detail.status).to.equal(1);
expect('reason' in spy.args[0][0].detail).to.be.false;
});
-
- it('should not include reason in securityfailure event for version < 3.8', function () {
- client._rfbVersion = 3.6;
- const spy = sinon.spy();
- client.addEventListener("securityfailure", spy);
- client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
- expect(spy.args[0][0].detail.status).to.equal(2);
- expect('reason' in spy.args[0][0].detail).to.be.false;
- });
});
describe('ClientInitialisation', function () {