fix mac screenshare vnc
This commit is contained in:
parent
d44f7e04fc
commit
af5814942b
|
|
@ -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) {
|
||||
this._key = await window.crypto.subtle.importKey(
|
||||
"raw", key, {name: "AES-CBC"}, 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({
|
||||
name: "AES-CBC",
|
||||
iv: new Uint8Array(16),
|
||||
}, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
|
||||
x.set(y, i * 16);
|
||||
|
||||
// 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, block);
|
||||
const truncated = new Uint8Array(enc).slice(0, 16);
|
||||
out.set(truncated, i * 16);
|
||||
}
|
||||
return out;
|
||||
} catch (_e) {
|
||||
// Fallback handled below
|
||||
}
|
||||
}
|
||||
return x;
|
||||
|
||||
// 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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue