Merge branch 'appleremotedesktop' of https://github.com/pauldumais/noVNC
This commit is contained in:
commit
42ec5f3321
|
@ -5,7 +5,7 @@
|
|||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 11
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
|
|
108
core/rfb.js
108
core/rfb.js
|
@ -26,6 +26,8 @@ import KeyTable from "./input/keysym.js";
|
|||
import XtScancode from "./input/xtscancodes.js";
|
||||
import { encodings } from "./encodings.js";
|
||||
import RSAAESAuthenticationState from "./ra2.js";
|
||||
import { MD5 } from "./util/md5.js";
|
||||
import { modPow } from "./util/bigint-mod-arith.js";
|
||||
|
||||
import RawDecoder from "./decoders/raw.js";
|
||||
import CopyRectDecoder from "./decoders/copyrect.js";
|
||||
|
@ -1288,13 +1290,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
|
||||
|
@ -1352,6 +1354,8 @@ export default class RFB extends EventTargetMixin {
|
|||
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 {
|
||||
|
@ -1544,6 +1548,105 @@ export default class RFB extends EventTargetMixin {
|
|||
return true;
|
||||
}
|
||||
|
||||
_negotiateARDAuth() {
|
||||
|
||||
if (this._rfbCredentials.username === undefined ||
|
||||
this._rfbCredentials.password === undefined) {
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
"credentialsrequired",
|
||||
{ detail: { types: ["username", "password"] } }));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._rfbCredentials.ardPublicKey != undefined &&
|
||||
this._rfbCredentials.ardCredentials != undefined) {
|
||||
// if the async web crypto is done return the results
|
||||
this._sock.send(this._rfbCredentials.ardCredentials);
|
||||
this._sock.send(this._rfbCredentials.ardPublicKey);
|
||||
this._rfbCredentials.ardCredentials = null;
|
||||
this._rfbCredentials.ardPublicKey = null;
|
||||
this._rfbInitState = "SecurityResult";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._sock.rQwait("read ard", 4)) { return false; }
|
||||
|
||||
let generator = this._sock.rQshiftBytes(2); // DH base generator value
|
||||
|
||||
let keyLength = this._sock.rQshift16();
|
||||
|
||||
if (this._sock.rQwait("read ard keylength", keyLength*2, 4)) { return false; }
|
||||
|
||||
// read the server values
|
||||
let prime = this._sock.rQshiftBytes(keyLength); // predetermined prime modulus
|
||||
let serverPublicKey = this._sock.rQshiftBytes(keyLength); // other party's public key
|
||||
|
||||
let clientPrivateKey = window.crypto.getRandomValues(new Uint8Array(keyLength));
|
||||
let padding = Array.from(window.crypto.getRandomValues(new Uint8Array(64)), byte => String.fromCharCode(65+byte%26)).join('');
|
||||
|
||||
this._negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_modPow(base, exponent, modulus) {
|
||||
|
||||
let baseHex = "0x"+Array.from(base, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
||||
let exponentHex = "0x"+Array.from(exponent, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
||||
let modulusHex = "0x"+Array.from(modulus, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
||||
|
||||
let hexResult = modPow(BigInt(baseHex), BigInt(exponentHex), BigInt(modulusHex)).toString(16);
|
||||
|
||||
while (hexResult.length/2<exponent.length || (hexResult.length%2 != 0)) {
|
||||
hexResult = "0"+hexResult;
|
||||
}
|
||||
|
||||
let bytesResult = [];
|
||||
for (let c = 0; c < hexResult.length; c += 2) {
|
||||
bytesResult.push(parseInt(hexResult.substr(c, 2), 16));
|
||||
}
|
||||
return bytesResult;
|
||||
}
|
||||
|
||||
async _aesEcbEncrypt(string, key) {
|
||||
// perform AES-ECB blocks
|
||||
let keyString = Array.from(key, byte => String.fromCharCode(byte)).join('');
|
||||
let aesKey = await window.crypto.subtle.importKey("raw", MD5(keyString), {name: "AES-CBC"}, false, ["encrypt"]);
|
||||
let data = new Uint8Array(string.length);
|
||||
for (let i = 0; i < string.length; ++i) {
|
||||
data[i] = string.charCodeAt(i);
|
||||
}
|
||||
let encrypted = new Uint8Array(data.length);
|
||||
for (let i=0;i<data.length;i+=16) {
|
||||
let block = data.slice(i, i+16);
|
||||
let encryptedBlock = await window.crypto.subtle.encrypt({name: "AES-CBC", iv: block},
|
||||
aesKey, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
);
|
||||
encrypted.set((new Uint8Array(encryptedBlock)).slice(0, 16), i);
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
async _negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding) {
|
||||
// calculate the DH keys
|
||||
let clientPublicKey = this._modPow(generator, clientPrivateKey, prime);
|
||||
let sharedKey = this._modPow(serverPublicKey, clientPrivateKey, prime);
|
||||
|
||||
let username = encodeUTF8(this._rfbCredentials.username).substring(0, 63);
|
||||
let password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);
|
||||
|
||||
let paddedUsername = username + '\0' + padding.substring(0, 63);
|
||||
let paddedPassword = password + '\0' + padding.substring(0, 63);
|
||||
let credentials = paddedUsername.substring(0, 64) + paddedPassword.substring(0, 64);
|
||||
|
||||
let encrypted = await this._aesEcbEncrypt(credentials, sharedKey);
|
||||
|
||||
this._rfbCredentials.ardCredentials = encrypted;
|
||||
this._rfbCredentials.ardPublicKey = clientPublicKey;
|
||||
|
||||
setTimeout(this._initMsg.bind(this), 0);
|
||||
}
|
||||
|
||||
_negotiateTightUnixAuth() {
|
||||
if (this._rfbCredentials.username === undefined ||
|
||||
this._rfbCredentials.password === undefined) {
|
||||
|
@ -1718,6 +1821,9 @@ export default class RFB extends EventTargetMixin {
|
|||
case 22: // XVP auth
|
||||
return this._negotiateXvpAuth();
|
||||
|
||||
case 30: // ARD auth
|
||||
return this._negotiateARDAuth();
|
||||
|
||||
case 2: // VNC authentication
|
||||
return this._negotiateStdVNCAuth();
|
||||
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* bigint-mod-arith implementation:
|
||||
* https://github.com/juanelas/bigint-mod-arith
|
||||
*
|
||||
* Full attribution follows:
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Juan Hernández Serrano
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0
|
||||
*
|
||||
* @param a
|
||||
*
|
||||
* @returns The absolute value of a
|
||||
*/
|
||||
function abs(a) {
|
||||
return (a >= 0) ? a : -a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitlength of a number
|
||||
*
|
||||
* @param a
|
||||
* @returns The bit length
|
||||
*/
|
||||
function bitLength(a) {
|
||||
if (typeof a === 'number') {
|
||||
a = BigInt(a);
|
||||
}
|
||||
if (a === 1n) {
|
||||
return 1;
|
||||
}
|
||||
let bits = 1;
|
||||
do {
|
||||
bits++;
|
||||
} while ((a >>= 1n) > 1n);
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm.
|
||||
* Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
*
|
||||
* @throws {RangeError}
|
||||
* This excepction is thrown if a or b are less than 0
|
||||
*
|
||||
* @returns A triple (g, x, y), such that ax + by = g = gcd(a, b).
|
||||
*/
|
||||
function eGcd(a, b) {
|
||||
if (typeof a === 'number') {
|
||||
a = BigInt(a);
|
||||
}
|
||||
if (typeof b === 'number') {
|
||||
b = BigInt(b);
|
||||
}
|
||||
if (a <= 0n || b <= 0n) {
|
||||
throw new RangeError('a and b MUST be > 0'); // a and b MUST be positive
|
||||
}
|
||||
let x = 0n;
|
||||
let y = 1n;
|
||||
let u = 1n;
|
||||
let v = 0n;
|
||||
while (a !== 0n) {
|
||||
const q = b / a;
|
||||
const r = b % a;
|
||||
const m = x - (u * q);
|
||||
const n = y - (v * q);
|
||||
b = a;
|
||||
a = r;
|
||||
x = u;
|
||||
y = v;
|
||||
u = m;
|
||||
v = n;
|
||||
}
|
||||
return {
|
||||
g: b,
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Greatest-common divisor of two integers based on the iterative binary algorithm.
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
*
|
||||
* @returns The greatest common divisor of a and b
|
||||
*/
|
||||
function gcd(a, b) {
|
||||
let aAbs = (typeof a === 'number') ? BigInt(abs(a)) : abs(a);
|
||||
let bAbs = (typeof b === 'number') ? BigInt(abs(b)) : abs(b);
|
||||
if (aAbs === 0n) {
|
||||
return bAbs;
|
||||
} else if (bAbs === 0n) {
|
||||
return aAbs;
|
||||
}
|
||||
let shift = 0n;
|
||||
while (((aAbs | bAbs) & 1n) === 0n) {
|
||||
aAbs >>= 1n;
|
||||
bAbs >>= 1n;
|
||||
shift++;
|
||||
}
|
||||
while ((aAbs & 1n) === 0n) {
|
||||
aAbs >>= 1n;
|
||||
}
|
||||
do {
|
||||
while ((bAbs & 1n) === 0n) {
|
||||
bAbs >>= 1n;
|
||||
}
|
||||
if (aAbs > bAbs) {
|
||||
const x = aAbs;
|
||||
aAbs = bAbs;
|
||||
bAbs = x;
|
||||
}
|
||||
bAbs -= aAbs;
|
||||
} while (bAbs !== 0n);
|
||||
// rescale
|
||||
return aAbs << shift;
|
||||
}
|
||||
|
||||
/**
|
||||
* The least common multiple computed as abs(a*b)/gcd(a,b)
|
||||
* @param a
|
||||
* @param b
|
||||
*
|
||||
* @returns The least common multiple of a and b
|
||||
*/
|
||||
function lcm(a, b) {
|
||||
if (typeof a === 'number') {
|
||||
a = BigInt(a);
|
||||
}
|
||||
if (typeof b === 'number') {
|
||||
b = BigInt(b);
|
||||
}
|
||||
if (a === 0n && b === 0n) {
|
||||
return BigInt(0);
|
||||
}
|
||||
return abs(a * b) / gcd(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum. max(a,b)==a if a>=b. max(a,b)==b if a<=b
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
*
|
||||
* @returns Maximum of numbers a and b
|
||||
*/
|
||||
function max(a, b) {
|
||||
return (a >= b) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
*
|
||||
* @returns Minimum of numbers a and b
|
||||
*/
|
||||
function min(a, b) {
|
||||
return (a >= b) ? b : a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the smallest positive element that is congruent to a in modulo n
|
||||
*
|
||||
* @remarks
|
||||
* a and b must be the same type, either number or bigint
|
||||
*
|
||||
* @param a - An integer
|
||||
* @param n - The modulo
|
||||
*
|
||||
* @throws {RangeError}
|
||||
* Excpeption thrown when n is not > 0
|
||||
*
|
||||
* @returns A bigint with the smallest positive representation of a modulo n
|
||||
*/
|
||||
function toZn(a, n) {
|
||||
if (typeof a === 'number') {
|
||||
a = BigInt(a);
|
||||
}
|
||||
if (typeof n === 'number') {
|
||||
n = BigInt(n);
|
||||
}
|
||||
if (n <= 0n) {
|
||||
throw new RangeError('n must be > 0');
|
||||
}
|
||||
const aZn = a % n;
|
||||
return (aZn < 0n) ? aZn + n : aZn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular inverse.
|
||||
*
|
||||
* @param a The number to find an inverse for
|
||||
* @param n The modulo
|
||||
*
|
||||
* @throws {RangeError}
|
||||
* Excpeption thorwn when a does not have inverse modulo n
|
||||
*
|
||||
* @returns The inverse modulo n
|
||||
*/
|
||||
function modInv(a, n) {
|
||||
const egcd = eGcd(toZn(a, n), n);
|
||||
if (egcd.g !== 1n) {
|
||||
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`); // modular inverse does not exist
|
||||
} else {
|
||||
return toZn(egcd.x, n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method
|
||||
*
|
||||
* @param b base
|
||||
* @param e exponent
|
||||
* @param n modulo
|
||||
*
|
||||
* @throws {RangeError}
|
||||
* Excpeption thrown when n is not > 0
|
||||
*
|
||||
* @returns b**e mod n
|
||||
*/
|
||||
function modPow(b, e, n) {
|
||||
if (typeof b === 'number') {
|
||||
b = BigInt(b);
|
||||
}
|
||||
if (typeof e === 'number') {
|
||||
e = BigInt(e);
|
||||
}
|
||||
if (typeof n === 'number') {
|
||||
n = BigInt(n);
|
||||
}
|
||||
if (n <= 0n) {
|
||||
throw new RangeError('n must be > 0');
|
||||
} else if (n === 1n) {
|
||||
return 0n;
|
||||
}
|
||||
b = toZn(b, n);
|
||||
if (e < 0n) {
|
||||
return modInv(modPow(b, abs(e), n), n);
|
||||
}
|
||||
let r = 1n;
|
||||
while (e > 0) {
|
||||
if ((e % 2n) === 1n) {
|
||||
r = r * b % n;
|
||||
}
|
||||
e = e / 2n;
|
||||
b = b ** 2n % n;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn };
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2021 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Performs MD5 hashing on a string of binary characters, returns an array of bytes
|
||||
*/
|
||||
|
||||
export function MD5(d) {
|
||||
let r = M(V(Y(X(d), 8 * d.length)));
|
||||
return r;
|
||||
}
|
||||
|
||||
function M(d) {
|
||||
let f = new Uint8Array(d.length);
|
||||
for (let i=0;i<d.length;i++) {
|
||||
f[i] = d.charCodeAt(i);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
function X(d) {
|
||||
let r = Array(d.length >> 2);
|
||||
for (let m = 0; m < r.length; m++) r[m] = 0;
|
||||
for (let m = 0; m < 8 * d.length; m += 8) r[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32;
|
||||
return r;
|
||||
}
|
||||
|
||||
function V(d) {
|
||||
let r = "";
|
||||
for (let m = 0; m < 32 * d.length; m += 8) r += String.fromCharCode(d[m >> 5] >>> m % 32 & 255);
|
||||
return r;
|
||||
}
|
||||
|
||||
function Y(d, g) {
|
||||
d[g >> 5] |= 128 << g % 32, d[14 + (g + 64 >>> 9 << 4)] = g;
|
||||
let m = 1732584193, f = -271733879, r = -1732584194, i = 271733878;
|
||||
for (let n = 0; n < d.length; n += 16) {
|
||||
let h = m,
|
||||
t = f,
|
||||
g = r,
|
||||
e = i;
|
||||
f = ii(f = ii(f = ii(f = ii(f = hh(f = hh(f = hh(f = hh(f = gg(f = gg(f = gg(f = gg(f = ff(f = ff(f = ff(f = ff(f, r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = add(m, h), f = add(f, t), r = add(r, g), i = add(i, e);
|
||||
}
|
||||
return Array(m, f, r, i);
|
||||
}
|
||||
|
||||
function cmn(d, g, m, f, r, i) {
|
||||
return add(rol(add(add(g, d), add(f, i)), r), m);
|
||||
}
|
||||
|
||||
function ff(d, g, m, f, r, i, n) {
|
||||
return cmn(g & m | ~g & f, d, g, r, i, n);
|
||||
}
|
||||
|
||||
function gg(d, g, m, f, r, i, n) {
|
||||
return cmn(g & f | m & ~f, d, g, r, i, n);
|
||||
}
|
||||
|
||||
function hh(d, g, m, f, r, i, n) {
|
||||
return cmn(g ^ m ^ f, d, g, r, i, n);
|
||||
}
|
||||
|
||||
function ii(d, g, m, f, r, i, n) {
|
||||
return cmn(m ^ (g | ~f), d, g, r, i, n);
|
||||
}
|
||||
|
||||
function add(d, g) {
|
||||
let m = (65535 & d) + (65535 & g);
|
||||
return (d >> 16) + (g >> 16) + (m >> 16) << 16 | 65535 & m;
|
||||
}
|
||||
|
||||
function rol(d, g) {
|
||||
return d << g | d >>> 32 - g;
|
||||
}
|
|
@ -1048,9 +1048,9 @@ 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 () {
|
||||
it('should interpret version 003.889 as version 3.8', function () {
|
||||
sendVer('003.889', client);
|
||||
expect(client._rfbVersion).to.equal(3.3);
|
||||
expect(client._rfbVersion).to.equal(3.8);
|
||||
});
|
||||
|
||||
it('should interpret version 003.007 as version 3.7', function () {
|
||||
|
@ -1273,6 +1273,79 @@ 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);
|
||||
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"]);
|
||||
});
|
||||
|
||||
it('should fire the credentialsrequired event if some credentials are missing', function () {
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("credentialsrequired", spy);
|
||||
client._rfbCredentials = { password: 'password'};
|
||||
sendSecurity(30, client);
|
||||
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
expect(spy.args[0][0].detail.types).to.have.members(["username", "password"]);
|
||||
});
|
||||
|
||||
it('should return properly encrypted credentials and public key', async function () {
|
||||
client._rfbCredentials = { username: 'user',
|
||||
password: 'password' };
|
||||
sendSecurity(30, client);
|
||||
|
||||
expect(client._sock).to.have.sent([30]);
|
||||
|
||||
function byteArray(length) {
|
||||
return Array.from(new Uint8Array(length).keys());
|
||||
}
|
||||
|
||||
let generator = [127, 255];
|
||||
let prime = byteArray(128);
|
||||
let serverPrivateKey = byteArray(128);
|
||||
let serverPublicKey = client._modPow(generator, serverPrivateKey, prime);
|
||||
|
||||
let clientPrivateKey = byteArray(128);
|
||||
let clientPublicKey = client._modPow(generator, clientPrivateKey, prime);
|
||||
|
||||
let padding = Array.from(byteArray(64), byte => String.fromCharCode(65+byte%26)).join('');
|
||||
|
||||
await client._negotiateARDAuthAsync(generator, 128, prime, serverPublicKey, clientPrivateKey, padding);
|
||||
|
||||
client._negotiateARDAuth();
|
||||
|
||||
expect(client._rfbInitState).to.equal('SecurityResult');
|
||||
|
||||
let expectEncrypted = new Uint8Array([
|
||||
232, 234, 159, 162, 170, 180, 138, 104, 164, 49, 53, 96, 20, 36, 21, 15,
|
||||
217, 219, 107, 173, 196, 60, 96, 142, 215, 71, 13, 185, 185, 47, 5, 175,
|
||||
151, 30, 194, 55, 173, 214, 141, 161, 36, 138, 146, 3, 178, 89, 43, 248,
|
||||
131, 134, 205, 174, 9, 150, 171, 74, 222, 201, 20, 2, 30, 168, 162, 123,
|
||||
46, 86, 81, 221, 44, 211, 180, 247, 221, 61, 95, 155, 157, 241, 76, 76,
|
||||
49, 217, 234, 75, 147, 237, 199, 159, 93, 140, 191, 174, 52, 90, 133, 58,
|
||||
243, 81, 112, 182, 64, 62, 149, 7, 151, 28, 36, 161, 247, 247, 36, 96,
|
||||
230, 95, 58, 207, 46, 183, 100, 139, 143, 155, 224, 43, 219, 3, 71, 139]);
|
||||
|
||||
let output = new Uint8Array(256);
|
||||
output.set(expectEncrypted, 0);
|
||||
output.set(clientPublicKey, 128);
|
||||
|
||||
expect(client._sock).to.have.sent(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('XVP Authentication (type 22) Handler', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
|
|
Loading…
Reference in New Issue