fix mac screenshare vnc

This commit is contained in:
Ali Ardestani 2025-11-05 14:11:57 -08:00
parent d44f7e04fc
commit af5814942b
1 changed files with 206 additions and 12 deletions

View File

@ -1,6 +1,7 @@
export class AESECBCipher {
constructor() {
this._key = null;
this._jsCipher = null;
}
get algorithm() {
@ -14,24 +15,217 @@ export class AESECBCipher {
}
async _importKey(key, extractable, keyUsages) {
if (window?.crypto?.subtle) {
try {
this._key = await window.crypto.subtle.importKey(
"raw", key, { name: "AES-CBC" }, extractable, keyUsages);
this._jsCipher = null;
return;
} catch (_e) {
// Fall through to JS fallback
}
}
this._key = null;
this._jsCipher = new JSAESECB(key);
}
async encrypt(_algorithm, plaintext) {
const x = new Uint8Array(plaintext);
if (x.length % 16 !== 0 || this._key === null) {
const input = new Uint8Array(plaintext);
if (input.length % 16 !== 0) {
return null;
}
const n = x.length / 16;
for (let i = 0; i < n; i++) {
const y = new Uint8Array(await window.crypto.subtle.encrypt({
// WebCrypto fast path
if (this._key !== null && window?.crypto?.subtle) {
try {
const blocks = input.length / 16;
const out = new Uint8Array(input.length);
for (let i = 0; i < blocks; i++) {
const block = input.slice(i * 16, i * 16 + 16);
const enc = await window.crypto.subtle.encrypt({
name: "AES-CBC",
iv: new Uint8Array(16),
}, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
x.set(y, i * 16);
}, this._key, block);
const truncated = new Uint8Array(enc).slice(0, 16);
out.set(truncated, i * 16);
}
return x;
return out;
} catch (_e) {
// Fallback handled below
}
}
// JS fallback for non-secure contexts (no SubtleCrypto)
if (this._jsCipher !== null) {
const blocks = input.length / 16;
const out = new Uint8Array(input.length);
for (let i = 0; i < blocks; i++) {
const block = input.slice(i * 16, i * 16 + 16);
const enc = this._jsCipher.encryptBlock(block);
out.set(enc, i * 16);
}
return out;
}
return null;
}
}
// Minimal AES-128 ECB implementation used as a fallback when SubtleCrypto
// is not available (e.g. non-secure contexts). Only encryption is implemented.
class JSAESECB {
constructor(keyBytes) {
if (!(keyBytes instanceof Uint8Array)) {
throw new Error("AES key must be Uint8Array");
}
if (keyBytes.length !== 16) {
// ARD uses MD5-derived 16-byte key (AES-128)
throw new Error("Only AES-128 is supported in JS fallback");
}
this._sbox = JSAESECB._SBOX;
this._rcon = JSAESECB._RCON;
this._roundKeys = this._keyExpansion(keyBytes);
}
encryptBlock(block) {
if (!(block instanceof Uint8Array) || block.length !== 16) {
throw new Error("AES block must be 16 bytes");
}
const state = new Uint8Array(block);
const rk = this._roundKeys;
this._addRoundKey(state, rk, 0);
for (let round = 1; round <= 9; round++) {
this._subBytes(state);
this._shiftRows(state);
this._mixColumns(state);
this._addRoundKey(state, rk, round);
}
this._subBytes(state);
this._shiftRows(state);
this._addRoundKey(state, rk, 10);
return state;
}
_addRoundKey(state, roundKeys, round) {
const offset = round * 16;
for (let i = 0; i < 16; i++) {
state[i] ^= roundKeys[offset + i];
}
}
_subBytes(state) {
const s = this._sbox;
for (let i = 0; i < 16; i++) {
state[i] = s[state[i]];
}
}
_shiftRows(state) {
// Row 0 stays
// Row 1: shift left by 1
let t = state[1];
state[1] = state[5];
state[5] = state[9];
state[9] = state[13];
state[13] = t;
// Row 2: shift left by 2
t = state[2];
let t2 = state[6];
state[2] = state[10];
state[6] = state[14];
state[10] = t;
state[14] = t2;
// Row 3: shift left by 3
t = state[3];
state[3] = state[15];
state[15] = state[11];
state[11] = state[7];
state[7] = t;
}
_mixColumns(state) {
for (let c = 0; c < 4; c++) {
const i = c * 4;
const a0 = state[i];
const a1 = state[i + 1];
const a2 = state[i + 2];
const a3 = state[i + 3];
const a0x = JSAESECB._xtime(a0);
const a1x = JSAESECB._xtime(a1);
const a2x = JSAESECB._xtime(a2);
const a3x = JSAESECB._xtime(a3);
state[i] = a0x ^ (a1 ^ a1x) ^ a2 ^ a3;
state[i + 1] = a0 ^ a1x ^ (a2 ^ a2x) ^ a3;
state[i + 2] = a0 ^ a1 ^ a2x ^ (a3 ^ a3x);
state[i + 3] = (a0 ^ a0x) ^ a1 ^ a2 ^ a3x;
}
}
_keyExpansion(key) {
const sbox = this._sbox;
const rcon = this._rcon;
const w = new Uint8Array(176); // 11 * 16
// first 16 bytes are the key
w.set(key);
let bytesGenerated = 16;
let rconIter = 1;
const temp = new Uint8Array(4);
while (bytesGenerated < 176) {
for (let i = 0; i < 4; i++) {
temp[i] = w[bytesGenerated - 4 + i];
}
if (bytesGenerated % 16 === 0) {
// RotWord
const t = temp[0];
temp[0] = temp[1];
temp[1] = temp[2];
temp[2] = temp[3];
temp[3] = t;
// SubWord
for (let i = 0; i < 4; i++) {
temp[i] = sbox[temp[i]];
}
temp[0] ^= rcon[rconIter++];
}
for (let i = 0; i < 4; i++) {
w[bytesGenerated] = w[bytesGenerated - 16] ^ temp[i];
bytesGenerated++;
}
}
return w;
}
static _xtime(a) {
return ((a << 1) & 0xff) ^ ((a & 0x80) ? 0x1b : 0x00);
}
// Precomputed S-box and Rcon tables
static get _SBOX() {
return new Uint8Array([
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
]);
}
static get _RCON() {
return new Uint8Array([
0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36
]);
}
}