Page MenuHomePhorge

D8602.1765340110.diff
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

D8602.1765340110.diff

diff --git a/web/account/qr-code-login.react.js b/web/account/qr-code-login.react.js
--- a/web/account/qr-code-login.react.js
+++ b/web/account/qr-code-login.react.js
@@ -4,10 +4,10 @@
import * as React from 'react';
import { qrCodeLinkURL } from 'lib/facts/links.js';
+import { generateKeyCommon } from 'lib/media/aes-crypto-utils-common.js';
import { uintArrayToHexString } from 'lib/media/data-utils.js';
import css from './qr-code-login.css';
-import { generateKey } from '../media/aes-crypto-utils.js';
import { useSelector } from '../redux/redux-utils.js';
function QrCodeLogin(): React.Node {
@@ -22,7 +22,7 @@
return;
}
- const rawAESKey: Uint8Array = await generateKey();
+ const rawAESKey: Uint8Array = await generateKeyCommon(crypto);
const aesKeyAsHexString: string = uintArrayToHexString(rawAESKey);
const url = qrCodeLinkURL(aesKeyAsHexString, ed25519Key);
diff --git a/web/media/aes-crypto-utils.js b/web/media/aes-crypto-utils.js
deleted file mode 100644
--- a/web/media/aes-crypto-utils.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// @flow
-
-const KEY_SIZE = 32; // bytes
-const IV_LENGTH = 12; // bytes - unique Initialization Vector (nonce)
-const TAG_LENGTH = 16; // bytes - GCM auth tag
-
-async function generateKey(): Promise<Uint8Array> {
- const algorithm = { name: 'AES-GCM', length: 256 };
- const key = await crypto.subtle.generateKey(algorithm, true, [
- 'encrypt',
- 'decrypt',
- ]);
- const keyData = await crypto.subtle.exportKey('raw', key);
- return new Uint8Array(keyData);
-}
-
-async function encrypt(
- keyBytes: Uint8Array,
- plaintext: Uint8Array,
-): Promise<Uint8Array> {
- if (keyBytes.length !== KEY_SIZE) {
- throw new Error('Invalid AES key size');
- }
-
- // we're creating the buffer now so we can avoid reallocating it later
- const outputBuffer = new ArrayBuffer(
- plaintext.length + IV_LENGTH + TAG_LENGTH,
- );
- const ivBytes = new Uint8Array(outputBuffer, 0, IV_LENGTH);
- const iv = crypto.getRandomValues(ivBytes);
-
- const algorithm = { name: 'AES-GCM', iv: iv, tagLength: TAG_LENGTH * 8 };
- const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, [
- 'encrypt',
- ]);
- const ciphertextWithTag = await crypto.subtle.encrypt(
- algorithm,
- key,
- plaintext,
- );
-
- const result = new Uint8Array(outputBuffer);
- result.set(new Uint8Array(ciphertextWithTag), iv.length);
- return result;
-}
-
-async function decrypt(
- keyBytes: Uint8Array,
- sealedData: Uint8Array,
-): Promise<Uint8Array> {
- if (keyBytes.length !== KEY_SIZE) {
- throw new Error('Invalid AES key size');
- }
- if (sealedData.length < IV_LENGTH + TAG_LENGTH) {
- throw new Error('Invalid ciphertext size');
- }
-
- const iv = sealedData.subarray(0, IV_LENGTH);
- const ciphertextWithTag = sealedData.subarray(IV_LENGTH);
-
- const algorithm = { name: 'AES-GCM', iv, tagLength: TAG_LENGTH * 8 };
- const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, [
- 'decrypt',
- ]);
-
- const plaintextBuffer = await crypto.subtle.decrypt(
- algorithm,
- key,
- ciphertextWithTag,
- );
- return new Uint8Array(plaintextBuffer);
-}
-
-export { generateKey, encrypt, decrypt };
diff --git a/web/media/aes-crypto-utils.test.js b/web/media/aes-crypto-utils.test.js
--- a/web/media/aes-crypto-utils.test.js
+++ b/web/media/aes-crypto-utils.test.js
@@ -1,6 +1,10 @@
// @flow
-import { generateKey, encrypt, decrypt } from './aes-crypto-utils.js';
+import {
+ generateKeyCommon,
+ encryptCommon,
+ decryptCommon,
+} from 'lib/media/aes-crypto-utils-common.js';
// some mock data
const testPlaintext = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
@@ -20,69 +24,77 @@
describe('generateKey', () => {
it('generates 32-byte AES key', async () => {
- const key = await generateKey();
+ const key = await generateKeyCommon(crypto);
expect(key.length).toBe(32);
});
});
describe('encrypt', () => {
it('generates ciphertext with IV and tag included', async () => {
- const encrypted = await encrypt(testEncryptionKey, testPlaintext);
+ const encrypted = await encryptCommon(
+ crypto,
+ testEncryptionKey,
+ testPlaintext,
+ );
// IV and tag are randomly generated, so we can't check the exact value
// IV + plaintext + tag = 12 + 11 + 16 = 39
expect(encrypted.length).toBe(testPlaintext.length + 12 + 16);
});
it('is decryptable by decrypt()', async () => {
- const key = await generateKey();
- const encrypted = await encrypt(key, randomData);
- const decrypted = await decrypt(key, encrypted);
+ const key = await generateKeyCommon(crypto);
+ const encrypted = await encryptCommon(crypto, key, randomData);
+ const decrypted = await decryptCommon(crypto, key, encrypted);
expect(decrypted).toEqual(randomData);
});
});
describe('decrypt', () => {
it('decrypts ciphertext', async () => {
- const decrypted = await decrypt(testEncryptionKey, testSealedData);
+ const decrypted = await decryptCommon(
+ crypto,
+ testEncryptionKey,
+ testSealedData,
+ );
expect(decrypted).toEqual(testPlaintext);
});
it('fails with wrong key', async () => {
- const key = await generateKey();
- const encrypted = await encrypt(key, randomData);
+ const key = await generateKeyCommon(crypto);
+ const encrypted = await encryptCommon(crypto, key, randomData);
- const wrongKey = await generateKey();
- await expect(decrypt(wrongKey, encrypted)).rejects.toThrow();
+ const wrongKey = await generateKeyCommon(crypto);
+ await expect(decryptCommon(crypto, wrongKey, encrypted)).rejects.toThrow();
});
it('fails with wrong ciphertext', async () => {
- const key = await generateKey();
- const encrypted = await encrypt(key, randomData);
+ const key = await generateKeyCommon(crypto);
+ const encrypted = await encryptCommon(crypto, key, randomData);
// change the first byte of the ciphertext (it's 13th byte in the buffer)
// first 12 bytes are IV, so changing the first byte of the ciphertext
encrypted[12] = encrypted[12] ^ 1;
- await expect(decrypt(key, encrypted)).rejects.toThrow();
+ await expect(decryptCommon(crypto, key, encrypted)).rejects.toThrow();
});
it('fails with wrong IV', async () => {
- const key = await generateKey();
- const encrypted = await encrypt(key, randomData);
+ const key = await generateKeyCommon(crypto);
+ const encrypted = await encryptCommon(crypto, key, randomData);
// change the first byte of the IV (it's 1st byte in the buffer)
encrypted[0] = encrypted[0] ^ 1;
- await expect(decrypt(key, encrypted)).rejects.toThrow();
+ await expect(decryptCommon(crypto, key, encrypted)).rejects.toThrow();
});
it('fails with wrong tag', async () => {
- const key = await generateKey();
- const encrypted = await encrypt(key, randomData);
+ const key = await generateKeyCommon(crypto);
+ const encrypted = await encryptCommon(crypto, key, randomData);
// change the last byte of the tag (tag is the last 16 bytes of the buffer)
encrypted[encrypted.length - 1] = encrypted[encrypted.length - 1] ^ 1;
- await expect(decrypt(key, encrypted)).rejects.toThrow();
+ await expect(decryptCommon(crypto, key, encrypted)).rejects.toThrow();
});
});
diff --git a/web/media/encryption-utils.js b/web/media/encryption-utils.js
--- a/web/media/encryption-utils.js
+++ b/web/media/encryption-utils.js
@@ -3,6 +3,7 @@
import invariant from 'invariant';
import { thumbHashToDataURL } from 'thumbhash';
+import * as AES from 'lib/media/aes-crypto-utils-common.js';
import { hexToUintArray, uintArrayToHexString } from 'lib/media/data-utils.js';
import { fileInfoFromData } from 'lib/media/file-utils.js';
import { fetchableMediaURI } from 'lib/media/media-utils.js';
@@ -13,7 +14,6 @@
import { getMessageForException } from 'lib/utils/errors.js';
import { calculatePaddedLength, pad, unpad } from 'lib/utils/pkcs7-padding.js';
-import * as AES from './aes-crypto-utils.js';
import { base64DecodeBuffer } from '../utils/base64-utils.js';
const PADDING_THRESHOLD = 5000000; // 5MB
@@ -61,8 +61,8 @@
let key, encryptedData, sha256;
try {
const plaintextData = shouldPad ? pad(data) : data;
- key = await AES.generateKey();
- encryptedData = await AES.encrypt(key, plaintextData);
+ key = await AES.generateKeyCommon(crypto);
+ encryptedData = await AES.encryptCommon(crypto, key, plaintextData);
const hashBytes = await crypto.subtle.digest('SHA-256', encryptedData);
sha256 = btoa(String.fromCharCode(...new Uint8Array(hashBytes)));
@@ -182,7 +182,7 @@
const decryptStartTime = Date.now();
try {
const keyBytes = hexToUintArray(encryptionKey);
- const plaintext = await AES.decrypt(keyBytes, data);
+ const plaintext = await AES.decryptCommon(crypto, keyBytes, data);
decryptedData =
plaintext.byteLength > PADDING_THRESHOLD ? plaintext : unpad(plaintext);
} catch (e) {
@@ -243,7 +243,8 @@
keyHex: string,
): Promise<string> {
const encryptedData = base64DecodeBuffer(encryptedThumbHash);
- const thumbhashBytes = await AES.decrypt(
+ const thumbhashBytes = await AES.decryptCommon(
+ crypto,
hexToUintArray(keyHex),
encryptedData,
);
diff --git a/web/media/image-utils.js b/web/media/image-utils.js
--- a/web/media/image-utils.js
+++ b/web/media/image-utils.js
@@ -3,6 +3,7 @@
import EXIF from 'exif-js';
import { rgbaToThumbHash } from 'thumbhash';
+import * as AES from 'lib/media/aes-crypto-utils-common.js';
import { hexToUintArray } from 'lib/media/data-utils.js';
import type {
GetOrientationMediaMissionStep,
@@ -11,7 +12,6 @@
} from 'lib/types/media-types.js';
import { getMessageForException } from 'lib/utils/errors.js';
-import * as AES from './aes-crypto-utils.js';
import { preloadImage } from './media-utils.js';
import { base64EncodeBuffer } from '../utils/base64-utils.js';
@@ -103,7 +103,8 @@
if (encryptionKey) {
try {
- const encryptedThumbHash = await AES.encrypt(
+ const encryptedThumbHash = await AES.encryptCommon(
+ crypto,
hexToUintArray(encryptionKey),
binaryThumbHash,
);

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 10, 4:15 AM (17 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5859993
Default Alt Text
D8602.1765340110.diff (10 KB)

Event Timeline