From ba503ff46f7f4e70bdd8cb31babc33a3db999791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?= Date: Sat, 10 Jun 2023 14:29:50 +0100 Subject: [PATCH] feat: implement decrypt from user key --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++------- src/decrypt-worker/crypto/CryptoFactory.ts | 3 +- src/decrypt-worker/crypto/qmc/qmc_v2.ts | 35 +++++++++++++++++++++- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index e4d3ef1..8d25a3a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@chakra-ui/react": "^2.7.0", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", - "@jixun/libparakeet": "0.1.1", + "@jixun/libparakeet": "0.1.2", "@reduxjs/toolkit": "^1.9.5", "framer-motion": "^10.12.16", "immer": "^10.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 909dc91..bbd2778 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ dependencies: specifier: ^11.11.0 version: 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.7)(react@18.2.0) '@jixun/libparakeet': - specifier: 0.1.1 - version: 0.1.1 + specifier: 0.1.2 + version: 0.1.2 '@reduxjs/toolkit': specifier: ^1.9.5 version: 1.9.5(react-redux@8.0.5)(react@18.2.0) @@ -3101,8 +3101,8 @@ packages: chalk: 4.1.2 dev: true - /@jixun/libparakeet@0.1.1: - resolution: {integrity: sha512-1e6hFsVga3eDkQvUY/wsX/F1R94SzcrO2u7wK/KPaCs9MD0hej+cgraOixp+VDy4uqGSPKU5iNP1a8hCYze2rg==} + /@jixun/libparakeet@0.1.2: + resolution: {integrity: sha512-PxhA7EtC3ss3ACxaGckgD41wGAhWllfy80LfL8GMiXkdXMipgcfFfeEcJBBpvTZ/FlDfoFLDJSOz0uculhsgnQ==} dev: false /@jridgewell/gen-mapping@0.3.3: @@ -4069,8 +4069,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001497 - electron-to-chromium: 1.4.426 + caniuse-lite: 1.0.30001498 + electron-to-chromium: 1.4.427 node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.7) dev: true @@ -4123,8 +4123,8 @@ packages: resolution: {integrity: sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==} dev: true - /caniuse-lite@1.0.30001497: - resolution: {integrity: sha512-I4/duVK4wL6rAK/aKZl3HXB4g+lIZvaT4VLAn2rCgJ38jVLb0lv2Xug6QuqmxXFVRJMF74SPPWPJ/1Sdm3vCzw==} + /caniuse-lite@1.0.30001498: + resolution: {integrity: sha512-LFInN2zAwx3ANrGCDZ5AKKJroHqNKyjXitdV5zRIVIaQlXKj3GmxUKagoKsjqUfckpAObPCEWnk5EeMlyMWcgw==} dev: true /chai@4.3.7: @@ -4478,8 +4478,8 @@ packages: resolution: {integrity: sha512-5VXLW4Qw89vM2WTICHua/y8v7fKGDRVa2VPOtBB9IpLvW316B+xd8yD1wTmLPY2ot/00P/qt87xdolj4aG/Lzg==} dev: true - /electron-to-chromium@1.4.426: - resolution: {integrity: sha512-dWuNH+XUT9hdFHASfMpcZGW5kUyJvllumJkXaXiswuCkoaFIFI89aykBPuHEi1YUWQGRCqvIO0BUdmeFJ4W4Ww==} + /electron-to-chromium@1.4.427: + resolution: {integrity: sha512-HK3r9l+Jm8dYAm1ctXEWIC+hV60zfcjS9UA5BDlYvnI5S7PU/yytjpvSrTNrSSRRkuu3tDyZhdkwIczh+0DWaw==} dev: true /emoji-regex@8.0.0: diff --git a/src/decrypt-worker/crypto/CryptoFactory.ts b/src/decrypt-worker/crypto/CryptoFactory.ts index c6a7bdc..9fa4999 100644 --- a/src/decrypt-worker/crypto/CryptoFactory.ts +++ b/src/decrypt-worker/crypto/CryptoFactory.ts @@ -1,7 +1,7 @@ import { CryptoFactory } from './CryptoBase'; import { QMC1Crypto } from './qmc/qmc_v1'; -import { QMC2Crypto } from './qmc/qmc_v2'; +import { QMC2Crypto, QMC2CryptoWithKey } from './qmc/qmc_v2'; import { XiamiCrypto } from './xiami/xiami'; import { KGMCrypto } from './kgm/kgm_pc'; import { NCMCrypto } from './ncm/ncm_pc'; @@ -14,6 +14,7 @@ export const allCryptoFactories: CryptoFactory[] = [ XiamiCrypto.make, // QMCv2 (*.mflac) + QMC2CryptoWithKey.make, QMC2Crypto.make, // NCM (*.ncm) diff --git a/src/decrypt-worker/crypto/qmc/qmc_v2.ts b/src/decrypt-worker/crypto/qmc/qmc_v2.ts index 371eb22..af95bc5 100644 --- a/src/decrypt-worker/crypto/qmc/qmc_v2.ts +++ b/src/decrypt-worker/crypto/qmc/qmc_v2.ts @@ -1,16 +1,49 @@ import { transformBlob } from '~/decrypt-worker/util/transformBlob'; import type { CryptoBase } from '../CryptoBase'; +import type { DecryptCommandOptions } from '~/decrypt-worker/types.ts'; import { SEED, ENC_V2_KEY_1, ENC_V2_KEY_2 } from './qmc_v2.key.ts'; +import { fetchParakeet } from '@jixun/libparakeet'; export class QMC2Crypto implements CryptoBase { cryptoName = 'QMC/v2'; checkByDecryptHeader = false; async decrypt(buffer: ArrayBuffer): Promise { - return transformBlob(buffer, (p) => p.make.QMCv2(p.make.QMCv2FooterParser(SEED, ENC_V2_KEY_1, ENC_V2_KEY_2))); + // FIXME: Move the cleanup to transformBlob + const mod = await fetchParakeet(); + const footerParser = mod.make.QMCv2FooterParser(SEED, ENC_V2_KEY_1, ENC_V2_KEY_2); + return transformBlob(buffer, (p) => p.make.QMCv2(footerParser)).finally(() => { + footerParser.delete(); + }); } public static make() { return new QMC2Crypto(); } } + +export class QMC2CryptoWithKey implements CryptoBase { + cryptoName = 'QMC/v2 (key)'; + checkByDecryptHeader = true; + + async checkBySignature(_buffer: ArrayBuffer, options: DecryptCommandOptions): Promise { + return Boolean(options.qmc2Key); + } + + async decrypt(buffer: ArrayBuffer, options: DecryptCommandOptions): Promise { + if (!options.qmc2Key) { + throw new Error('key was not provided'); + } + + // FIXME: Move the cleanup to transformBlob + const mod = await fetchParakeet(); + const textEncoder = new TextEncoder(); + const key = textEncoder.encode(options.qmc2Key); + const keyCrypto = mod.make.QMCv2KeyCrypto(SEED, ENC_V2_KEY_1, ENC_V2_KEY_2); + return transformBlob(buffer, (p) => p.make.QMCv2EKey(key, keyCrypto)); + } + + public static make() { + return new QMC2CryptoWithKey(); + } +}