feat: pass options to downstream decryptor

This commit is contained in:
鲁树人 2023-06-10 12:06:02 +01:00
parent f169a036d0
commit 4de0d5304d
7 changed files with 49 additions and 16 deletions

View File

@ -1,3 +1,5 @@
import type { DecryptCommandOptions } from '~/decrypt-worker/types';
export interface CryptoBase { export interface CryptoBase {
cryptoName: string; cryptoName: string;
checkByDecryptHeader: boolean; checkByDecryptHeader: boolean;
@ -8,8 +10,8 @@ export interface CryptoBase {
*/ */
overrideExtension?: string; overrideExtension?: string;
checkBySignature?: (buffer: ArrayBuffer) => Promise<boolean>; checkBySignature?: (buffer: ArrayBuffer, options: DecryptCommandOptions) => Promise<boolean>;
decrypt(buffer: ArrayBuffer, blob: Blob): Promise<Blob | ArrayBuffer>; decrypt(buffer: ArrayBuffer, options: DecryptCommandOptions): Promise<Blob | ArrayBuffer>;
} }
export type CryptoFactory = () => CryptoBase; export type CryptoFactory = () => CryptoBase;

View File

@ -0,0 +1,9 @@
export interface DecryptCommandOptions {
qmc2Key?: string;
}
export interface DecryptCommandPayload {
id: string;
blobURI: string;
options: DecryptCommandOptions;
}

View File

@ -1,5 +1,6 @@
import { Parakeet, fetchParakeet } from '@jixun/libparakeet'; import { Parakeet, fetchParakeet } from '@jixun/libparakeet';
import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils'; import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils';
import type { DecryptCommandOptions, DecryptCommandPayload } from '~/decrypt-worker/types';
import { allCryptoFactories } from '../../crypto/CryptoFactory'; import { allCryptoFactories } from '../../crypto/CryptoFactory';
import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer'; import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer';
import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase'; import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase';
@ -11,8 +12,13 @@ const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024;
class DecryptCommandHandler { class DecryptCommandHandler {
private label: string; private label: string;
constructor(label: string, private parakeet: Parakeet, private blob: Blob, private buffer: ArrayBuffer) { constructor(
this.label = `DecryptCommandHandler( ${label} )`; label: string,
private parakeet: Parakeet,
private buffer: ArrayBuffer,
private options: DecryptCommandOptions
) {
this.label = `DecryptCommandHandler(${label})`;
} }
log<R>(label: string, fn: () => R): R { log<R>(label: string, fn: () => R): R {
@ -42,7 +48,7 @@ class DecryptCommandHandler {
} }
async decryptFile(crypto: CryptoBase) { async decryptFile(crypto: CryptoBase) {
if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer))) { if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer, this.options))) {
return null; return null;
} }
@ -50,7 +56,7 @@ class DecryptCommandHandler {
return null; return null;
} }
const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.blob)); const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.options));
// Check if we had a successful decryption // Check if we had a successful decryption
const audioExt = crypto.overrideExtension ?? (await this.detectAudioExtension(decrypted)); const audioExt = crypto.overrideExtension ?? (await this.detectAudioExtension(decrypted));
@ -76,23 +82,21 @@ class DecryptCommandHandler {
// Check by decrypt max first 8MiB // Check by decrypt max first 8MiB
const decryptedBuffer = await this.log(`${crypto.cryptoName}/decrypt-header-test`, async () => const decryptedBuffer = await this.log(`${crypto.cryptoName}/decrypt-header-test`, async () =>
toArrayBuffer( toArrayBuffer(await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.options))
await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.blob.slice(0, TEST_FILE_HEADER_LEN))
)
); );
return this.parakeet.detectAudioExtension(decryptedBuffer) !== 'bin'; return this.parakeet.detectAudioExtension(decryptedBuffer) !== 'bin';
} }
} }
export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => { export const workerDecryptHandler = async ({ id, blobURI, options }: DecryptCommandPayload) => {
const label = `decrypt( ${id} )`; const label = `decrypt(${id})`;
return withTimeGroupedLogs(label, async () => { return withTimeGroupedLogs(label, async () => {
const parakeet = await timedLogger(`${label}/init`, fetchParakeet); const parakeet = await timedLogger(`${label}/init`, fetchParakeet);
const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob())); const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob()));
const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer()); const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer());
const handler = new DecryptCommandHandler(id, parakeet, blob, buffer); const handler = new DecryptCommandHandler(id, parakeet, buffer, options);
return handler.decrypt(allCryptoFactories); return handler.decrypt(allCryptoFactories);
}); });
}; };

View File

@ -5,6 +5,7 @@ import { decryptionQueue } from '~/decrypt-worker/client';
import type { DecryptionResult } from '~/decrypt-worker/constants'; import type { DecryptionResult } from '~/decrypt-worker/constants';
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError'; import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
import { selectDecryptOptionByFile } from '../settings/settingsSelector';
export enum ProcessState { export enum ProcessState {
QUEUED = 'QUEUED', QUEUED = 'QUEUED',
@ -51,7 +52,8 @@ export const processFile = createAsyncThunk<
{ fileId: string }, { fileId: string },
{ rejectValue: { message: string; stack?: string } } { rejectValue: { message: string; stack?: string } }
>('fileListing/processFile', async ({ fileId }, thunkAPI) => { >('fileListing/processFile', async ({ fileId }, thunkAPI) => {
const file = selectFiles(thunkAPI.getState() as RootState)[fileId]; const state = thunkAPI.getState() as RootState;
const file = selectFiles(state)[fileId];
if (!file) { if (!file) {
const { message, stack } = new Error('ERROR: File not found'); const { message, stack } = new Error('ERROR: File not found');
return thunkAPI.rejectWithValue({ message, stack }); return thunkAPI.rejectWithValue({ message, stack });
@ -61,7 +63,8 @@ export const processFile = createAsyncThunk<
thunkAPI.dispatch(setFileAsProcessing({ id: fileId })); thunkAPI.dispatch(setFileAsProcessing({ id: fileId }));
}; };
return decryptionQueue.add({ id: fileId, blobURI: file.raw }, onPreProcess); const options = selectDecryptOptionByFile(state, file.fileName);
return decryptionQueue.add({ id: fileId, blobURI: file.raw, options }, onPreProcess);
}); });
export const fileListingSlice = createSlice({ export const fileListingSlice = createSlice({

View File

@ -0,0 +1,11 @@
import type { DecryptCommandOptions } from '~/decrypt-worker/types';
import type { RootState } from '~/store';
import { hasOwn } from '~/util/objects';
export const selectDecryptOptionByFile = (state: RootState, name: string): DecryptCommandOptions => {
const qmc2Keys = state.settings.qmc2.keys;
return {
qmc2Key: hasOwn(qmc2Keys, name) ? qmc2Keys[name] : undefined,
};
};

View File

@ -1,13 +1,15 @@
import type { DecryptCommandPayload } from '~/decrypt-worker/types';
import { DECRYPTION_WORKER_ACTION_NAME, DecryptionResult } from '~/decrypt-worker/constants'; import { DECRYPTION_WORKER_ACTION_NAME, DecryptionResult } from '~/decrypt-worker/constants';
import { ConcurrentQueue } from './ConcurrentQueue'; import { ConcurrentQueue } from './ConcurrentQueue';
import { WorkerClientBus } from './WorkerEventBus'; import { WorkerClientBus } from './WorkerEventBus';
export class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }, DecryptionResult> { export class DecryptionQueue extends ConcurrentQueue<DecryptCommandPayload, DecryptionResult> {
constructor(private workerClientBus: WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>, maxQueue?: number) { constructor(private workerClientBus: WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>, maxQueue?: number) {
super(maxQueue); super(maxQueue);
} }
async handler(item: { id: string; blobURI: string }): Promise<DecryptionResult> { async handler(item: DecryptCommandPayload): Promise<DecryptionResult> {
return this.workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, item); return this.workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, item);
} }
} }

View File

@ -5,3 +5,5 @@ export function* enumObject<T>(obj: Record<string, T> | null | void): Generator<
} }
} }
} }
export const { hasOwn } = Object;