chore: prettier format all files, brotli, Sentry release tagging, CI gates
Prettier: auto-formatted 103 files to fix baseline. Prettier check in CI is now a hard gate (removed continue-on-error). Brotli: installed libnginx-mod-http-brotli-filter/static. Enabled in nginx with brotli_static on for pre-compressed assets and comp_level 6. Sentry releases: deploy script now exports VITE_APP_VERSION=<git-short-sha> before building so each Sentry release maps to an exact commit. CI also passes github.sha as VITE_APP_VERSION. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ async function deriveKeys(salt, iterations, password) {
|
||||
new TextEncoder().encode(password),
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveBits'],
|
||||
['deriveBits']
|
||||
);
|
||||
} catch (e) {
|
||||
throw friendlyError(`subtleCrypto.importKey failed: ${e}`, cryptoFailMsg());
|
||||
@@ -53,40 +53,38 @@ async function deriveKeys(salt, iterations, password) {
|
||||
hash: 'SHA-512',
|
||||
},
|
||||
key,
|
||||
512,
|
||||
512
|
||||
);
|
||||
} catch (e) {
|
||||
throw friendlyError(`subtleCrypto.deriveBits failed: ${e}`, cryptoFailMsg());
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
console.log(`E2e import/export: deriveKeys took ${(now - start)}ms`);
|
||||
console.log(`E2e import/export: deriveKeys took ${now - start}ms`);
|
||||
|
||||
const aesKey = keybits.slice(0, 32);
|
||||
const hmacKey = keybits.slice(32);
|
||||
|
||||
const aesProm = subtleCrypto.importKey(
|
||||
'raw',
|
||||
aesKey,
|
||||
{ name: 'AES-CTR' },
|
||||
false,
|
||||
['encrypt', 'decrypt'],
|
||||
).catch((e) => {
|
||||
throw friendlyError(`subtleCrypto.importKey failed for AES key: ${e}`, cryptoFailMsg());
|
||||
});
|
||||
const aesProm = subtleCrypto
|
||||
.importKey('raw', aesKey, { name: 'AES-CTR' }, false, ['encrypt', 'decrypt'])
|
||||
.catch((e) => {
|
||||
throw friendlyError(`subtleCrypto.importKey failed for AES key: ${e}`, cryptoFailMsg());
|
||||
});
|
||||
|
||||
const hmacProm = subtleCrypto.importKey(
|
||||
'raw',
|
||||
hmacKey,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: { name: 'SHA-256' },
|
||||
},
|
||||
false,
|
||||
['sign', 'verify'],
|
||||
).catch((e) => {
|
||||
throw friendlyError(`subtleCrypto.importKey failed for HMAC key: ${e}`, cryptoFailMsg());
|
||||
});
|
||||
const hmacProm = subtleCrypto
|
||||
.importKey(
|
||||
'raw',
|
||||
hmacKey,
|
||||
{
|
||||
name: 'HMAC',
|
||||
hash: { name: 'SHA-256' },
|
||||
},
|
||||
false,
|
||||
['sign', 'verify']
|
||||
)
|
||||
.catch((e) => {
|
||||
throw friendlyError(`subtleCrypto.importKey failed for HMAC key: ${e}`, cryptoFailMsg());
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-return-await
|
||||
return await Promise.all([aesProm, hmacProm]);
|
||||
@@ -177,7 +175,6 @@ function unpackMegolmKeyFile(data) {
|
||||
return decodeBase64(fileStr.slice(dataStart, dataEnd));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ascii-armour a megolm key file
|
||||
*
|
||||
@@ -189,20 +186,20 @@ function unpackMegolmKeyFile(data) {
|
||||
function packMegolmKeyFile(data) {
|
||||
// we split into lines before base64ing, because encodeBase64 doesn't deal
|
||||
// terribly well with large arrays.
|
||||
const LINE_LENGTH = ((72 * 4) / 3);
|
||||
const LINE_LENGTH = (72 * 4) / 3;
|
||||
const nLines = Math.ceil(data.length / LINE_LENGTH);
|
||||
const lines = new Array(nLines + 3);
|
||||
lines[0] = HEADER_LINE;
|
||||
let o = 0;
|
||||
let i;
|
||||
for (i = 1; i <= nLines; i += 1) {
|
||||
lines[i] = encodeBase64(data.subarray(o, o+LINE_LENGTH));
|
||||
lines[i] = encodeBase64(data.subarray(o, o + LINE_LENGTH));
|
||||
o += LINE_LENGTH;
|
||||
}
|
||||
lines[i] = TRAILER_LINE;
|
||||
i += 1;
|
||||
lines[i] = '';
|
||||
return (new TextEncoder().encode(lines.join('\n'))).buffer;
|
||||
return new TextEncoder().encode(lines.join('\n')).buffer;
|
||||
}
|
||||
|
||||
export async function decryptMegolmKeyFile(data, password) {
|
||||
@@ -225,7 +222,7 @@ export async function decryptMegolmKeyFile(data, password) {
|
||||
|
||||
const salt = body.subarray(1, 1 + 16);
|
||||
const iv = body.subarray(17, 17 + 16);
|
||||
const iterations = body[33] << 24 | body[34] << 16 | body[35] << 8 | body[36];
|
||||
const iterations = (body[33] << 24) | (body[34] << 16) | (body[35] << 8) | body[36];
|
||||
const ciphertext = body.subarray(37, 37 + ciphertextLength);
|
||||
const hmac = body.subarray(-32);
|
||||
|
||||
@@ -234,12 +231,7 @@ export async function decryptMegolmKeyFile(data, password) {
|
||||
|
||||
let isValid;
|
||||
try {
|
||||
isValid = await subtleCrypto.verify(
|
||||
{ name: 'HMAC' },
|
||||
hmacKey,
|
||||
hmac,
|
||||
toVerify,
|
||||
);
|
||||
isValid = await subtleCrypto.verify({ name: 'HMAC' }, hmacKey, hmac, toVerify);
|
||||
} catch (e) {
|
||||
throw friendlyError(`subtleCrypto.verify failed: ${e}`, cryptoFailMsg());
|
||||
}
|
||||
@@ -256,7 +248,7 @@ export async function decryptMegolmKeyFile(data, password) {
|
||||
length: 64,
|
||||
},
|
||||
aesKey,
|
||||
ciphertext,
|
||||
ciphertext
|
||||
);
|
||||
} catch (e) {
|
||||
throw friendlyError(`subtleCrypto.decrypt failed: ${e}`, cryptoFailMsg());
|
||||
@@ -302,34 +294,33 @@ export async function encryptMegolmKeyFile(data, password, options) {
|
||||
length: 64,
|
||||
},
|
||||
aesKey,
|
||||
encodedData,
|
||||
encodedData
|
||||
);
|
||||
} catch (e) {
|
||||
throw friendlyError('subtleCrypto.encrypt failed: ' + e, cryptoFailMsg());
|
||||
}
|
||||
|
||||
const cipherArray = new Uint8Array(ciphertext);
|
||||
const bodyLength = (1+salt.length+iv.length+4+cipherArray.length+32);
|
||||
const bodyLength = 1 + salt.length + iv.length + 4 + cipherArray.length + 32;
|
||||
const resultBuffer = new Uint8Array(bodyLength);
|
||||
let idx = 0;
|
||||
resultBuffer[idx++] = 1; // version
|
||||
resultBuffer.set(salt, idx); idx += salt.length;
|
||||
resultBuffer.set(iv, idx); idx += iv.length;
|
||||
resultBuffer.set(salt, idx);
|
||||
idx += salt.length;
|
||||
resultBuffer.set(iv, idx);
|
||||
idx += iv.length;
|
||||
resultBuffer[idx++] = kdfRounds >> 24;
|
||||
resultBuffer[idx++] = (kdfRounds >> 16) & 0xff;
|
||||
resultBuffer[idx++] = (kdfRounds >> 8) & 0xff;
|
||||
resultBuffer[idx++] = kdfRounds & 0xff;
|
||||
resultBuffer.set(cipherArray, idx); idx += cipherArray.length;
|
||||
resultBuffer.set(cipherArray, idx);
|
||||
idx += cipherArray.length;
|
||||
|
||||
const toSign = resultBuffer.subarray(0, idx);
|
||||
|
||||
let hmac;
|
||||
try {
|
||||
hmac = await subtleCrypto.sign(
|
||||
{ name: 'HMAC' },
|
||||
hmacKey,
|
||||
toSign,
|
||||
);
|
||||
hmac = await subtleCrypto.sign({ name: 'HMAC' }, hmacKey, toSign);
|
||||
} catch (e) {
|
||||
throw friendlyError('subtleCrypto.sign failed: ' + e, cryptoFailMsg());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user