Add support for ARD Security Type
This commit is contained in:
parent
7730814b8d
commit
ff8e2364a5
|
|
@ -0,0 +1,258 @@
|
|||
import { encodeUTF8 } from './util/strings.js';
|
||||
import EventTargetMixin from './util/eventtarget.js';
|
||||
|
||||
export class AESBlockCipher {
|
||||
constructor() {
|
||||
this._key = null;
|
||||
this._zeroBlock = new Uint8Array(16);
|
||||
}
|
||||
|
||||
async setKey(key) {
|
||||
this._key = await window.crypto.subtle.importKey(
|
||||
"raw", key, {"name": "AES-CBC"}, false, ["encrypt", "decrypt"]);
|
||||
}
|
||||
|
||||
async encrypt(block) {
|
||||
const encrypted = await window.crypto.subtle.encrypt({
|
||||
name: "AES-CBC",
|
||||
iv: this._zeroBlock,
|
||||
}, this._key, block);
|
||||
return new Uint8Array(encrypted).slice(0, 16);
|
||||
}
|
||||
|
||||
async encryptInplace(data, offset) {
|
||||
let block = data.slice(offset, offset + 16);
|
||||
block = await this.encrypt(block);
|
||||
data.set(block, offset);
|
||||
}
|
||||
}
|
||||
|
||||
export default class ARDAuthenticationState extends EventTargetMixin {
|
||||
constructor(sock, getCredentials) {
|
||||
super();
|
||||
this._hasStarted = false;
|
||||
this._checkSock = null;
|
||||
this._checkCredentials = null;
|
||||
this._sockReject = null;
|
||||
this._credentialsReject = null;
|
||||
this._sock = sock;
|
||||
this._getCredentials = getCredentials;
|
||||
}
|
||||
|
||||
_waitSockAsync(len) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hasData = () => !this._sock.rQwait('RA2', len);
|
||||
if (hasData()) {
|
||||
resolve();
|
||||
} else {
|
||||
this._checkSock = () => {
|
||||
if (hasData()) {
|
||||
resolve();
|
||||
this._checkSock = null;
|
||||
this._sockReject = null;
|
||||
}
|
||||
};
|
||||
this._sockReject = reject;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_waitCredentialsAsync() {
|
||||
const hasCredentials = () => (
|
||||
this._getCredentials().username !== undefined && this._getCredentials().password !== undefined
|
||||
);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (hasCredentials()) {
|
||||
resolve();
|
||||
} else {
|
||||
this._checkCredentials = () => {
|
||||
if (hasCredentials()) {
|
||||
resolve();
|
||||
this._checkCredentials = null;
|
||||
this._credentialsReject = null;
|
||||
}
|
||||
};
|
||||
this._credentialsReject = reject;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_md5(msg) {
|
||||
const s = [
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
|
||||
];
|
||||
const k = [
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
|
||||
];
|
||||
let a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
|
||||
const length = msg.length + 1;
|
||||
const padLength = Math.ceil((length - 56) / 64) * 64 + 64;
|
||||
const x = new Uint8Array(padLength);
|
||||
x.set(msg);
|
||||
x[msg.length] = 0x80;
|
||||
let view = new DataView(x.buffer);
|
||||
view.setUint32(padLength - 8, msg.length * 8, true);
|
||||
for (let i = 0; i < padLength; i += 64) {
|
||||
let a = a0, b = b0, c = c0, d = d0;
|
||||
for (let j = 0; j < 64; j++) {
|
||||
let f, g;
|
||||
if (j < 16) {
|
||||
f = (b & c) | (~b & d);
|
||||
g = j;
|
||||
} else if (j < 32) {
|
||||
f = (d & b) | (~d & c);
|
||||
g = (5 * j + 1) % 16;
|
||||
} else if (j < 48) {
|
||||
f = b ^ c ^ d;
|
||||
g = (3 * j + 5) % 16;
|
||||
} else {
|
||||
f = c ^ (b | ~d);
|
||||
g = (7 * j) % 16;
|
||||
}
|
||||
f = (f + a + k[j] + view.getUint32(i + g * 4, true)) >>> 0;
|
||||
a = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = (b + ((f << s[j]) | (f >>> (32 - s[j])))) >>> 0;
|
||||
}
|
||||
a0 = (a0 + a) >>> 0;
|
||||
b0 = (b0 + b) >>> 0;
|
||||
c0 = (c0 + c) >>> 0;
|
||||
d0 = (d0 + d) >>> 0;
|
||||
}
|
||||
const digest = new Uint8Array(16);
|
||||
view = new DataView(digest.buffer);
|
||||
view.setUint32(0, a0, true);
|
||||
view.setUint32(4, b0, true);
|
||||
view.setUint32(8, c0, true);
|
||||
view.setUint32(12, d0, true);
|
||||
return digest;
|
||||
}
|
||||
|
||||
_u8ArrayToBigInt(arr) {
|
||||
let hex = '0x';
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
hex += arr[i].toString(16).padStart(2, '0');
|
||||
}
|
||||
return BigInt(hex);
|
||||
}
|
||||
|
||||
_bigIntToU8Array(bigint, padLength) {
|
||||
let hex = bigint.toString(16);
|
||||
hex = hex.padStart(padLength * 2, '0');
|
||||
const length = hex.length / 2;
|
||||
const arr = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
_modPow(b, e, m) {
|
||||
if (m === 1n) {
|
||||
return 0;
|
||||
}
|
||||
let r = 1n;
|
||||
b = b % m;
|
||||
while (e > 0) {
|
||||
if (e % 2n === 1n) {
|
||||
r = (r * b) % m;
|
||||
}
|
||||
e = e / 2n;
|
||||
b = (b * b) % m;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
checkInternalEvents() {
|
||||
if (this._checkSock !== null) {
|
||||
this._checkSock();
|
||||
}
|
||||
if (this._checkCredentials !== null) {
|
||||
this._checkCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (this._sockReject !== null) {
|
||||
this._sockReject(new Error("disconnect normally"));
|
||||
this._sockReject = null;
|
||||
}
|
||||
if (this._credentialsReject !== null) {
|
||||
this._credentialsReject(new Error("disconnect normally"));
|
||||
this._credentialsReject = null;
|
||||
}
|
||||
}
|
||||
|
||||
async negotiateARDAuthAsync() {
|
||||
this._hasStarted = true;
|
||||
await this._waitSockAsync(4);
|
||||
const generator = this._sock.rQshift16();
|
||||
const keyLength = this._sock.rQshift16();
|
||||
await this._waitSockAsync(keyLength * 2);
|
||||
const primeModulus = this._sock.rQshiftBytes(keyLength);
|
||||
const serverPublickey = this._sock.rQshiftBytes(keyLength);
|
||||
const primeModulusBigInt = this._u8ArrayToBigInt(primeModulus);
|
||||
const serverPublickeyBigInt = this._u8ArrayToBigInt(serverPublickey);
|
||||
const generatorBigInt = BigInt(generator);
|
||||
|
||||
const clientPrivatekey = new Uint8Array(keyLength);
|
||||
window.crypto.getRandomValues(clientPrivatekey);
|
||||
const clientPrivatekeyBigInt = this._u8ArrayToBigInt(clientPrivatekey);
|
||||
const sharedSecret = this._bigIntToU8Array(
|
||||
this._modPow(serverPublickeyBigInt, clientPrivatekeyBigInt, primeModulusBigInt), keyLength);
|
||||
const clientPublickey = this._bigIntToU8Array(
|
||||
this._modPow(generatorBigInt, clientPrivatekeyBigInt, primeModulusBigInt), keyLength);
|
||||
const aesKey = this._md5(sharedSecret);
|
||||
const cipher = new AESBlockCipher;
|
||||
await cipher.setKey(aesKey);
|
||||
|
||||
const credentials = new Uint8Array(128);
|
||||
window.crypto.getRandomValues(credentials);
|
||||
this.dispatchEvent(new CustomEvent("credentialsrequired", {
|
||||
detail: { types: ["username", "password"] }
|
||||
}));
|
||||
await this._waitCredentialsAsync();
|
||||
const username = encodeUTF8(this._getCredentials().username).slice(0, 63);
|
||||
const password = encodeUTF8(this._getCredentials().password).slice(0, 63);
|
||||
for (let i = 0; i < username.length; i++) {
|
||||
credentials[i] = username.charCodeAt(i);
|
||||
}
|
||||
for (let i = 0; i < password.length; i++) {
|
||||
credentials[64 + i] = password.charCodeAt(i);
|
||||
}
|
||||
credentials[username.length] = 0;
|
||||
credentials[64 + password.length] = 0;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
await cipher.encryptInplace(credentials, i * 16);
|
||||
}
|
||||
this._sock.send(credentials);
|
||||
this._sock.send(clientPublickey);
|
||||
}
|
||||
|
||||
get hasStarted() {
|
||||
return this._hasStarted;
|
||||
}
|
||||
|
||||
set hasStarted(s) {
|
||||
this._hasStarted = s;
|
||||
}
|
||||
}
|
||||
43
core/rfb.js
43
core/rfb.js
|
|
@ -26,6 +26,7 @@ import KeyTable from "./input/keysym.js";
|
|||
import XtScancode from "./input/xtscancodes.js";
|
||||
import { encodings } from "./encodings.js";
|
||||
import RSAAESAuthenticationState from "./ra2.js";
|
||||
import ARDAuthenticationState from "./ard.js";
|
||||
|
||||
import RawDecoder from "./decoders/raw.js";
|
||||
import CopyRectDecoder from "./decoders/copyrect.js";
|
||||
|
|
@ -102,6 +103,7 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbAuthScheme = -1;
|
||||
this._rfbCleanDisconnect = true;
|
||||
this._rfbRSAAESAuthenticationState = null;
|
||||
this._rfbARDAuthenticationState = null;
|
||||
|
||||
// Server capabilities
|
||||
this._rfbVersion = 0;
|
||||
|
|
@ -182,6 +184,7 @@ export default class RFB extends EventTargetMixin {
|
|||
handleGesture: this._handleGesture.bind(this),
|
||||
handleRSAAESCredentialsRequired: this._handleRSAAESCredentialsRequired.bind(this),
|
||||
handleRSAAESServerVerification: this._handleRSAAESServerVerification.bind(this),
|
||||
handleARDCredentialsRequired: this._handleARDCredentialsRequired.bind(this),
|
||||
};
|
||||
|
||||
// main setup
|
||||
|
|
@ -385,6 +388,9 @@ export default class RFB extends EventTargetMixin {
|
|||
if (this._rfbRSAAESAuthenticationState !== null) {
|
||||
this._rfbRSAAESAuthenticationState.disconnect();
|
||||
}
|
||||
if (this._rfbARDAuthenticationState != null) {
|
||||
this._rfbARDAuthenticationState.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
approveServer() {
|
||||
|
|
@ -1288,13 +1294,13 @@ export default class RFB extends EventTargetMixin {
|
|||
break;
|
||||
case "003.003":
|
||||
case "003.006": // UltraVNC
|
||||
case "003.889": // Apple Remote Desktop
|
||||
this._rfbVersion = 3.3;
|
||||
break;
|
||||
case "003.007":
|
||||
this._rfbVersion = 3.7;
|
||||
break;
|
||||
case "003.008":
|
||||
case "003.889": // Apple Remote Desktop
|
||||
case "004.000": // Intel AMT KVM
|
||||
case "004.001": // RealVNC 4.6
|
||||
case "005.000": // RealVNC 5.3
|
||||
|
|
@ -1354,6 +1360,8 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbAuthScheme = 2; // VNC Auth
|
||||
} else if (types.includes(19)) {
|
||||
this._rfbAuthScheme = 19; // VeNCrypt Auth
|
||||
} else if (types.includes(30)) {
|
||||
this._rfbAuthScheme = 30; // ARD Auth
|
||||
} else {
|
||||
return this._fail("Unsupported security types (types: " + types + ")");
|
||||
}
|
||||
|
|
@ -1705,6 +1713,36 @@ export default class RFB extends EventTargetMixin {
|
|||
return false;
|
||||
}
|
||||
|
||||
_handleARDCredentialsRequired(event) {
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
_negotiateARDAuth() {
|
||||
if (this._rfbARDAuthenticationState === null) {
|
||||
this._rfbARDAuthenticationState = new ARDAuthenticationState(this._sock, () => this._rfbCredentials);
|
||||
this._rfbARDAuthenticationState.addEventListener(
|
||||
"credentialsrequired", this._eventHandlers.handleARDCredentialsRequired);
|
||||
}
|
||||
this._rfbARDAuthenticationState.checkInternalEvents();
|
||||
if (!this._rfbARDAuthenticationState.hasStarted) {
|
||||
this._rfbARDAuthenticationState.negotiateARDAuthAsync()
|
||||
.catch((e) => {
|
||||
if (e.message !== "disconnect normally") {
|
||||
this._fail(e.message);
|
||||
}
|
||||
}).then(() => {
|
||||
this.dispatchEvent(new CustomEvent('securityresult'));
|
||||
this._rfbInitState = "SecurityResult";
|
||||
this._initMsg();
|
||||
}).finally(() => {
|
||||
this._rfbARDAuthenticationState.removeEventListener(
|
||||
"credentialsrequired", this._eventHandlers.handleARDCredentialsRequired);
|
||||
this._rfbARDAuthenticationState = null;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_negotiateAuthentication() {
|
||||
switch (this._rfbAuthScheme) {
|
||||
case 1: // no auth
|
||||
|
|
@ -1733,6 +1771,9 @@ export default class RFB extends EventTargetMixin {
|
|||
case 6: // RA2ne Security Type
|
||||
return this._negotiateRA2neAuth();
|
||||
|
||||
case 30: // ARD Security Type:
|
||||
return this._negotiateARDAuth();
|
||||
|
||||
default:
|
||||
return this._fail("Unsupported auth scheme (scheme: " +
|
||||
this._rfbAuthScheme + ")");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
const expect = chai.expect;
|
||||
|
||||
import RFB from '../core/rfb.js';
|
||||
|
||||
import FakeWebSocket from './fake.websocket.js';
|
||||
|
||||
const receiveData = new Uint8Array([
|
||||
0x00, 0x02, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2,
|
||||
0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b,
|
||||
0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08,
|
||||
0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
|
||||
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79,
|
||||
0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3,
|
||||
0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
|
||||
0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d,
|
||||
0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76,
|
||||
0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9,
|
||||
0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6,
|
||||
0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb,
|
||||
0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11,
|
||||
0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51,
|
||||
0xec, 0xe6, 0x53, 0x81, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x2e, 0xa9, 0xdc, 0x27,
|
||||
0x9a, 0xa2, 0xb8, 0x7a, 0x9b, 0xd6, 0x35, 0x20,
|
||||
0xfd, 0x3b, 0x83, 0x6f, 0xdc, 0x05, 0xe8, 0xfe,
|
||||
0xd8, 0xaf, 0x2d, 0x19, 0xbb, 0x66, 0xb9, 0xfd,
|
||||
0xf4, 0x0f, 0x43, 0xe1, 0xe7, 0xbd, 0x11, 0xe3,
|
||||
0x18, 0xcf, 0x96, 0x0a, 0x54, 0x68, 0x0e, 0x69,
|
||||
0x39, 0xf2, 0x26, 0xd1, 0xaf, 0x76, 0x11, 0x99,
|
||||
0xb1, 0x9c, 0xd8, 0x36, 0x4a, 0x46, 0xcf, 0x23,
|
||||
0xce, 0xd5, 0x19, 0x26, 0xe7, 0xc8, 0xfe, 0xb9,
|
||||
0x97, 0xc4, 0xbf, 0x83, 0x13, 0xe6, 0xa3, 0xd1,
|
||||
0x5a, 0x4f, 0x9a, 0x91, 0xc8, 0xc1, 0x19, 0x2e,
|
||||
0x1d, 0x9c, 0x72, 0x31, 0x78, 0xae, 0x18, 0x17,
|
||||
0x13, 0x32, 0xe9, 0xf6, 0x8f, 0x72, 0x96, 0x27,
|
||||
0x35, 0x46, 0xa7, 0x6d, 0x0d, 0x82, 0x24, 0x5a,
|
||||
0x80, 0xcb, 0xb0, 0x3c, 0x82, 0x9f, 0xb2, 0x77,
|
||||
0x3e, 0x8a, 0xea, 0x5e, 0xcf, 0xbe, 0x6c, 0x66,
|
||||
0x06, 0xca, 0x15, 0x82,
|
||||
]);
|
||||
|
||||
const sendData = new Uint8Array([
|
||||
0x00, 0x55, 0xd1, 0x5e, 0x65, 0x69, 0x25, 0x25,
|
||||
0xc2, 0xb8, 0x0b, 0x68, 0x9c, 0x1b, 0x2c, 0xd9,
|
||||
0xa0, 0xe8, 0xb1, 0x26, 0x8c, 0x7b, 0x10, 0x4e,
|
||||
0xf6, 0x39, 0xbc, 0xc2, 0x17, 0x58, 0x92, 0xf0,
|
||||
0x5c, 0x87, 0x63, 0x62, 0x5f, 0xea, 0x29, 0x3e,
|
||||
0x12, 0xb2, 0x3e, 0xb4, 0x9b, 0x8a, 0x95, 0x5d,
|
||||
0xac, 0x3b, 0xa3, 0x9f, 0x2f, 0xde, 0x45, 0xea,
|
||||
0x45, 0xb8, 0x41, 0x0b, 0x9e, 0xfd, 0xad, 0x66,
|
||||
0x04, 0x34, 0xca, 0xa2, 0xf4, 0xd2, 0xc3, 0x33,
|
||||
0x5c, 0xad, 0x63, 0x8b, 0xae, 0x9a, 0x4f, 0xea,
|
||||
0xf4, 0x1f, 0x19, 0x1b, 0xb8, 0x86, 0x9c, 0x4f,
|
||||
0x39, 0x9c, 0xc3, 0x67, 0xe3, 0x02, 0x9b, 0x2a,
|
||||
0x55, 0x7c, 0x8a, 0xb0, 0xc8, 0xf7, 0x92, 0x8b,
|
||||
0x9d, 0xc7, 0xb6, 0xb1, 0x90, 0x7b, 0xb7, 0x4c,
|
||||
0xcc, 0x73, 0x4c, 0x84, 0x0b, 0xdf, 0x91, 0xb6,
|
||||
0xc5, 0xb3, 0xeb, 0x81, 0xe7, 0x49, 0x2f, 0xe3,
|
||||
0xf6, 0x90, 0x56, 0xe7, 0xa2, 0xa5, 0x88, 0x04,
|
||||
0x5f, 0x7b, 0x1a, 0x08, 0xcc, 0xb8, 0xa8, 0xec,
|
||||
0x62, 0xfd, 0x1e, 0xc2, 0x07, 0x7c, 0x97, 0xfd,
|
||||
0x77, 0x94, 0x13, 0x80, 0x11, 0xc9, 0xef, 0x4d,
|
||||
0x01, 0x14, 0x7d, 0x0d, 0x08, 0x1c, 0x6e, 0x14,
|
||||
0x5b, 0x4d, 0x77, 0x4e, 0xc4, 0xba, 0xb9, 0x44,
|
||||
0x36, 0xd6, 0xcc, 0x3c, 0xdf, 0xfd, 0xc6, 0xf2,
|
||||
0x12, 0x0b, 0xdd, 0x3e, 0x57, 0x1b, 0x03, 0x97,
|
||||
0xe2, 0x17, 0x80, 0x46, 0xaf, 0xa8, 0xad, 0x0e,
|
||||
0x55, 0x88, 0x38, 0x78, 0xa1, 0xce, 0x8c, 0x1d,
|
||||
0x2b, 0xa9, 0x8f, 0xbd, 0x9f, 0x95, 0x42, 0x92,
|
||||
0xf7, 0xcb, 0xdf, 0x94, 0xd6, 0xdf, 0x1b, 0x9a,
|
||||
0x15, 0x5b, 0x8f, 0xe3, 0x5f, 0x2d, 0x54, 0x7e,
|
||||
0xf9, 0x95, 0x84, 0xee, 0x0e, 0x16, 0x73, 0x81,
|
||||
0xbe, 0xb5, 0xf0, 0xe2, 0x6f, 0x65, 0xb0, 0xe9,
|
||||
0x29, 0x7d, 0xba, 0x8d, 0x78, 0x11, 0xb1, 0x54,
|
||||
]);
|
||||
|
||||
describe('ARD handshake', function () {
|
||||
let sock;
|
||||
let rfb;
|
||||
let sentData;
|
||||
|
||||
before(() => {
|
||||
FakeWebSocket.replace();
|
||||
sinon.stub(window.crypto, "getRandomValues")
|
||||
.onFirstCall().callsFake((arr) => {
|
||||
arr.set(new Uint8Array([
|
||||
0x40, 0x26, 0x17, 0x7c, 0xca, 0x81, 0xdb, 0xe5,
|
||||
0x51, 0x46, 0xdd, 0x82, 0x5a, 0xcf, 0xaa, 0xfe,
|
||||
0xe6, 0x78, 0x5b, 0x6b, 0x63, 0xe4, 0x14, 0x9c,
|
||||
0xa8, 0x41, 0xec, 0x64, 0x78, 0x04, 0x47, 0xc2,
|
||||
0xc3, 0x76, 0x2d, 0x86, 0xe4, 0x7a, 0xd6, 0x45,
|
||||
0x9d, 0x45, 0x3e, 0x81, 0x8f, 0x04, 0x77, 0x92,
|
||||
0x82, 0xd6, 0x1d, 0xb4, 0x21, 0x24, 0xf3, 0x13,
|
||||
0x84, 0x5b, 0xc5, 0x00, 0xa0, 0x9f, 0xd0, 0x8d,
|
||||
0xd7, 0x20, 0xe0, 0xe3, 0x6c, 0xec, 0xab, 0x91,
|
||||
0x88, 0x09, 0x5b, 0xd5, 0xa7, 0x6e, 0xbf, 0xdb,
|
||||
0x93, 0xd9, 0x89, 0x0c, 0xa1, 0xe1, 0x48, 0xc8,
|
||||
0xf5, 0x50, 0x76, 0xa1, 0x47, 0x02, 0x0e, 0x97,
|
||||
0x92, 0x84, 0x25, 0x6a, 0x61, 0x03, 0x8e, 0x08,
|
||||
0x90, 0xe4, 0x7c, 0x8e, 0x17, 0x59, 0xc7, 0xcc,
|
||||
0xf6, 0x86, 0x26, 0xe3, 0xfc, 0xce, 0x17, 0x80,
|
||||
0x25, 0xb4, 0x97, 0x4a, 0x75, 0x52, 0xfa, 0xcc,
|
||||
]));
|
||||
}).onSecondCall().callsFake((arr) => {
|
||||
arr.set(new Uint8Array([
|
||||
0xbb, 0x65, 0xa8, 0x06, 0xe9, 0xbf, 0xa5, 0xd9,
|
||||
0x6d, 0x45, 0x39, 0xf2, 0xf9, 0xb0, 0xb1, 0x76,
|
||||
0x8a, 0x7b, 0xff, 0xd9, 0xf2, 0x70, 0x6c, 0xa3,
|
||||
0xdf, 0xbf, 0x49, 0x89, 0x18, 0xe0, 0x5f, 0xc4,
|
||||
0x47, 0x9e, 0x0c, 0xd0, 0xa1, 0x70, 0x5e, 0xe2,
|
||||
0xf3, 0xe0, 0x31, 0xe3, 0x94, 0x04, 0xd6, 0x6e,
|
||||
0x15, 0xe2, 0xba, 0xe3, 0xe4, 0xa5, 0xfd, 0x52,
|
||||
0xb3, 0x1d, 0xf0, 0x91, 0x0b, 0xe9, 0x8b, 0x1b,
|
||||
0x6c, 0xa6, 0x49, 0x82, 0x51, 0xc3, 0x69, 0x8c,
|
||||
0x60, 0x77, 0xd2, 0x25, 0x1b, 0x1d, 0x38, 0x03,
|
||||
0x06, 0x69, 0xdc, 0xa2, 0xc7, 0xa4, 0x9f, 0x21,
|
||||
0xa3, 0xcd, 0x3e, 0x70, 0xb2, 0xca, 0x54, 0x74,
|
||||
0x63, 0x50, 0xe1, 0x55, 0x5c, 0xb2, 0x5a, 0x16,
|
||||
0x80, 0x48, 0x7b, 0xf5, 0x06, 0x57, 0x26, 0x70,
|
||||
0x65, 0x83, 0xf3, 0xdd, 0xa6, 0xd0, 0x72, 0xc1,
|
||||
0xd8, 0x8e, 0x96, 0x3b, 0xdb, 0x44, 0xce, 0x34,
|
||||
]));
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
FakeWebSocket.restore();
|
||||
window.crypto.getRandomValues.restore();
|
||||
});
|
||||
|
||||
it('should fire the credentialsrequired 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, 30]));
|
||||
rfb.addEventListener("credentialsrequired", (e) => {
|
||||
expect(e.detail.types).to.eql(["username", "password"]);
|
||||
done();
|
||||
});
|
||||
sock._websocket._receiveData(receiveData);
|
||||
});
|
||||
|
||||
it('should match sendData after sending credentials', function (done) {
|
||||
rfb.addEventListener("securityresult", (event) => {
|
||||
expect(sentData.slice(1)).to.eql(sendData);
|
||||
done();
|
||||
});
|
||||
rfb.sendCredentials({ "username": "test", "password": "123456" });
|
||||
});
|
||||
});
|
||||
|
|
@ -1048,11 +1048,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
expect(client._rfbVersion).to.equal(3.3);
|
||||
});
|
||||
|
||||
it('should interpret version 003.889 as version 3.3', function () {
|
||||
sendVer('003.889', client);
|
||||
expect(client._rfbVersion).to.equal(3.3);
|
||||
});
|
||||
|
||||
it('should interpret version 003.007 as version 3.7', function () {
|
||||
sendVer('003.007', client);
|
||||
expect(client._rfbVersion).to.equal(3.7);
|
||||
|
|
@ -1063,6 +1058,11 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
expect(client._rfbVersion).to.equal(3.8);
|
||||
});
|
||||
|
||||
it('should interpret version 003.889 as version 3.8', function () {
|
||||
sendVer('003.889', client);
|
||||
expect(client._rfbVersion).to.equal(3.8);
|
||||
});
|
||||
|
||||
it('should interpret version 004.000 as version 3.8', function () {
|
||||
sendVer('004.000', client);
|
||||
expect(client._rfbVersion).to.equal(3.8);
|
||||
|
|
|
|||
Loading…
Reference in New Issue