34 changed files with 1656 additions and 1709 deletions
			
			
		@ -1,26 +1,25 @@ | 
				
			|||
export interface DecryptResult { | 
				
			|||
    title: string | 
				
			|||
    album?: string | 
				
			|||
    artist?: string | 
				
			|||
  title: string; | 
				
			|||
  album?: string; | 
				
			|||
  artist?: string; | 
				
			|||
 | 
				
			|||
    mime: string | 
				
			|||
    ext: string | 
				
			|||
  mime: string; | 
				
			|||
  ext: string; | 
				
			|||
 | 
				
			|||
    file: string | 
				
			|||
    blob: Blob | 
				
			|||
    picture?: string | 
				
			|||
 | 
				
			|||
    message?: string | 
				
			|||
    rawExt?: string | 
				
			|||
    rawFilename?: string | 
				
			|||
  file: string; | 
				
			|||
  blob: Blob; | 
				
			|||
  picture?: string; | 
				
			|||
 | 
				
			|||
  message?: string; | 
				
			|||
  rawExt?: string; | 
				
			|||
  rawFilename?: string; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export interface FileInfo { | 
				
			|||
    status: string | 
				
			|||
    name: string, | 
				
			|||
    size: number, | 
				
			|||
    percentage: number, | 
				
			|||
    uid: number, | 
				
			|||
    raw: File | 
				
			|||
  status: string; | 
				
			|||
  name: string; | 
				
			|||
  size: number; | 
				
			|||
  percentage: number; | 
				
			|||
  uid: number; | 
				
			|||
  raw: File; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
@ -1,115 +1,117 @@ | 
				
			|||
import {QmcMapCipher, QmcRC4Cipher, QmcStaticCipher} from "@/decrypt/qmc_cipher"; | 
				
			|||
import fs from 'fs' | 
				
			|||
import { QmcMapCipher, QmcRC4Cipher, QmcStaticCipher } from '@/decrypt/qmc_cipher'; | 
				
			|||
import fs from 'fs'; | 
				
			|||
 | 
				
			|||
test("static cipher [0x7ff8,0x8000) ", () => { | 
				
			|||
test('static cipher [0x7ff8,0x8000) ', () => { | 
				
			|||
  //prettier-ignore
 | 
				
			|||
  const expected = new Uint8Array([ | 
				
			|||
    0xD8, 0x52, 0xF7, 0x67, 0x90, 0xCA, 0xD6, 0x4A, | 
				
			|||
    0x4A, 0xD6, 0xCA, 0x90, 0x67, 0xF7, 0x52, 0xD8, | 
				
			|||
  ]) | 
				
			|||
 | 
				
			|||
  const c = new QmcStaticCipher() | 
				
			|||
  const buf = new Uint8Array(16) | 
				
			|||
  c.decrypt(buf, 0x7ff8) | 
				
			|||
  const c = new QmcStaticCipher(); | 
				
			|||
  const buf = new Uint8Array(16); | 
				
			|||
  c.decrypt(buf, 0x7ff8); | 
				
			|||
 | 
				
			|||
  expect(buf).toStrictEqual(expected) | 
				
			|||
}) | 
				
			|||
  expect(buf).toStrictEqual(expected); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
test("static cipher [0,0x10) ", () => { | 
				
			|||
test('static cipher [0,0x10) ', () => { | 
				
			|||
  //prettier-ignore
 | 
				
			|||
  const expected = new Uint8Array([ | 
				
			|||
    0xC3, 0x4A, 0xD6, 0xCA, 0x90, 0x67, 0xF7, 0x52, | 
				
			|||
    0xD8, 0xA1, 0x66, 0x62, 0x9F, 0x5B, 0x09, 0x00, | 
				
			|||
  ]) | 
				
			|||
 | 
				
			|||
  const c = new QmcStaticCipher() | 
				
			|||
  const buf = new Uint8Array(16) | 
				
			|||
  c.decrypt(buf, 0) | 
				
			|||
  const c = new QmcStaticCipher(); | 
				
			|||
  const buf = new Uint8Array(16); | 
				
			|||
  c.decrypt(buf, 0); | 
				
			|||
 | 
				
			|||
  expect(buf).toStrictEqual(expected) | 
				
			|||
}) | 
				
			|||
  expect(buf).toStrictEqual(expected); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
 | 
				
			|||
test("map cipher: get mask", () => { | 
				
			|||
test('map cipher: get mask', () => { | 
				
			|||
  //prettier-ignore
 | 
				
			|||
  const expected = new Uint8Array([ | 
				
			|||
    0xBB, 0x7D, 0x80, 0xBE, 0xFF, 0x38, 0x81, 0xFB, | 
				
			|||
    0xBB, 0xFF, 0x82, 0x3C, 0xFF, 0xBA, 0x83, 0x79, | 
				
			|||
  ]) | 
				
			|||
  const key = new Uint8Array(256) | 
				
			|||
  for (let i = 0; i < 256; i++) key[i] = i | 
				
			|||
  const buf = new Uint8Array(16) | 
				
			|||
  const key = new Uint8Array(256); | 
				
			|||
  for (let i = 0; i < 256; i++) key[i] = i; | 
				
			|||
  const buf = new Uint8Array(16); | 
				
			|||
 | 
				
			|||
  const c = new QmcMapCipher(key) | 
				
			|||
  c.decrypt(buf, 0) | 
				
			|||
  expect(buf).toStrictEqual(expected) | 
				
			|||
}) | 
				
			|||
  const c = new QmcMapCipher(key); | 
				
			|||
  c.decrypt(buf, 0); | 
				
			|||
  expect(buf).toStrictEqual(expected); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
function loadTestDataCipher(name: string): { | 
				
			|||
  key: Uint8Array, | 
				
			|||
  cipherText: Uint8Array, | 
				
			|||
  clearText: Uint8Array | 
				
			|||
  key: Uint8Array; | 
				
			|||
  cipherText: Uint8Array; | 
				
			|||
  clearText: Uint8Array; | 
				
			|||
} { | 
				
			|||
  return { | 
				
			|||
    key: fs.readFileSync(`testdata/${name}_key.bin`), | 
				
			|||
    cipherText: fs.readFileSync(`testdata/${name}_raw.bin`), | 
				
			|||
    clearText: fs.readFileSync(`testdata/${name}_target.bin`) | 
				
			|||
  } | 
				
			|||
    clearText: fs.readFileSync(`testdata/${name}_target.bin`), | 
				
			|||
  }; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
test("map cipher: real file", async () => { | 
				
			|||
  const cases = ["mflac_map", "mgg_map"] | 
				
			|||
test('map cipher: real file', async () => { | 
				
			|||
  const cases = ['mflac_map', 'mgg_map']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {key, clearText, cipherText} = loadTestDataCipher(name) | 
				
			|||
    const c = new QmcMapCipher(key) | 
				
			|||
    const { key, clearText, cipherText } = loadTestDataCipher(name); | 
				
			|||
    const c = new QmcMapCipher(key); | 
				
			|||
 | 
				
			|||
    c.decrypt(cipherText, 0) | 
				
			|||
    c.decrypt(cipherText, 0); | 
				
			|||
 | 
				
			|||
    expect(cipherText).toStrictEqual(clearText) | 
				
			|||
    expect(cipherText).toStrictEqual(clearText); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
test("rc4 cipher: real file", async () => { | 
				
			|||
  const cases = ["mflac0_rc4"] | 
				
			|||
test('rc4 cipher: real file', async () => { | 
				
			|||
  const cases = ['mflac0_rc4']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {key, clearText, cipherText} = loadTestDataCipher(name) | 
				
			|||
    const c = new QmcRC4Cipher(key) | 
				
			|||
    const { key, clearText, cipherText } = loadTestDataCipher(name); | 
				
			|||
    const c = new QmcRC4Cipher(key); | 
				
			|||
 | 
				
			|||
    c.decrypt(cipherText, 0) | 
				
			|||
    c.decrypt(cipherText, 0); | 
				
			|||
 | 
				
			|||
    expect(cipherText).toStrictEqual(clearText) | 
				
			|||
    expect(cipherText).toStrictEqual(clearText); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
test("rc4 cipher: first segment", async () => { | 
				
			|||
  const cases = ["mflac0_rc4"] | 
				
			|||
test('rc4 cipher: first segment', async () => { | 
				
			|||
  const cases = ['mflac0_rc4']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {key, clearText, cipherText} = loadTestDataCipher(name) | 
				
			|||
    const c = new QmcRC4Cipher(key) | 
				
			|||
    const { key, clearText, cipherText } = loadTestDataCipher(name); | 
				
			|||
    const c = new QmcRC4Cipher(key); | 
				
			|||
 | 
				
			|||
    const buf = cipherText.slice(0, 128) | 
				
			|||
    c.decrypt(buf, 0) | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(0, 128)) | 
				
			|||
    const buf = cipherText.slice(0, 128); | 
				
			|||
    c.decrypt(buf, 0); | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(0, 128)); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
test("rc4 cipher: align block (128~5120)", async () => { | 
				
			|||
  const cases = ["mflac0_rc4"] | 
				
			|||
test('rc4 cipher: align block (128~5120)', async () => { | 
				
			|||
  const cases = ['mflac0_rc4']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {key, clearText, cipherText} = loadTestDataCipher(name) | 
				
			|||
    const c = new QmcRC4Cipher(key) | 
				
			|||
    const { key, clearText, cipherText } = loadTestDataCipher(name); | 
				
			|||
    const c = new QmcRC4Cipher(key); | 
				
			|||
 | 
				
			|||
    const buf = cipherText.slice(128, 5120) | 
				
			|||
    c.decrypt(buf, 128) | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(128, 5120)) | 
				
			|||
    const buf = cipherText.slice(128, 5120); | 
				
			|||
    c.decrypt(buf, 128); | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(128, 5120)); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
test("rc4 cipher: simple block (5120~10240)", async () => { | 
				
			|||
  const cases = ["mflac0_rc4"] | 
				
			|||
test('rc4 cipher: simple block (5120~10240)', async () => { | 
				
			|||
  const cases = ['mflac0_rc4']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {key, clearText, cipherText} = loadTestDataCipher(name) | 
				
			|||
    const c = new QmcRC4Cipher(key) | 
				
			|||
    const { key, clearText, cipherText } = loadTestDataCipher(name); | 
				
			|||
    const c = new QmcRC4Cipher(key); | 
				
			|||
 | 
				
			|||
    const buf = cipherText.slice(5120, 10240) | 
				
			|||
    c.decrypt(buf, 5120) | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(5120, 10240)) | 
				
			|||
    const buf = cipherText.slice(5120, 10240); | 
				
			|||
    c.decrypt(buf, 5120); | 
				
			|||
    expect(buf).toStrictEqual(clearText.slice(5120, 10240)); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
@ -1,30 +1,26 @@ | 
				
			|||
import {QmcDeriveKey, simpleMakeKey} from "@/decrypt/qmc_key"; | 
				
			|||
import fs from "fs"; | 
				
			|||
import { QmcDeriveKey, simpleMakeKey } from '@/decrypt/qmc_key'; | 
				
			|||
import fs from 'fs'; | 
				
			|||
 | 
				
			|||
test("key dec: make simple key", () => { | 
				
			|||
  expect( | 
				
			|||
    simpleMakeKey(106, 8) | 
				
			|||
  ).toStrictEqual( | 
				
			|||
    [0x69, 0x56, 0x46, 0x38, 0x2b, 0x20, 0x15, 0x0b] | 
				
			|||
  ) | 
				
			|||
}) | 
				
			|||
test('key dec: make simple key', () => { | 
				
			|||
  expect(simpleMakeKey(106, 8)).toStrictEqual([0x69, 0x56, 0x46, 0x38, 0x2b, 0x20, 0x15, 0x0b]); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
function loadTestDataKeyDecrypt(name: string): { | 
				
			|||
  cipherText: Uint8Array, | 
				
			|||
  clearText: Uint8Array | 
				
			|||
  cipherText: Uint8Array; | 
				
			|||
  clearText: Uint8Array; | 
				
			|||
} { | 
				
			|||
  return { | 
				
			|||
    cipherText: fs.readFileSync(`testdata/${name}_key_raw.bin`), | 
				
			|||
    clearText: fs.readFileSync(`testdata/${name}_key.bin`) | 
				
			|||
  } | 
				
			|||
    clearText: fs.readFileSync(`testdata/${name}_key.bin`), | 
				
			|||
  }; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
test("key dec: real file", async () => { | 
				
			|||
  const cases = ["mflac_map", "mgg_map", "mflac0_rc4"] | 
				
			|||
test('key dec: real file', async () => { | 
				
			|||
  const cases = ['mflac_map', 'mgg_map', 'mflac0_rc4']; | 
				
			|||
  for (const name of cases) { | 
				
			|||
    const {clearText, cipherText} = loadTestDataKeyDecrypt(name) | 
				
			|||
    const buf = QmcDeriveKey(cipherText) | 
				
			|||
    const { clearText, cipherText } = loadTestDataKeyDecrypt(name); | 
				
			|||
    const buf = QmcDeriveKey(cipherText); | 
				
			|||
 | 
				
			|||
    expect(buf).toStrictEqual(clearText) | 
				
			|||
    expect(buf).toStrictEqual(clearText); | 
				
			|||
  } | 
				
			|||
}) | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
@ -1,5 +1,2 @@ | 
				
			|||
const bs = chrome || browser | 
				
			|||
bs.tabs.create({ | 
				
			|||
    url: bs.runtime.getURL('./index.html') | 
				
			|||
}, tab => console.log(tab)) | 
				
			|||
 | 
				
			|||
const bs = chrome || browser; | 
				
			|||
bs.tabs.create({ url: bs.runtime.getURL('./index.html') }, (tab) => console.log(tab)); | 
				
			|||
 | 
				
			|||
@ -1,31 +1,30 @@ | 
				
			|||
/* eslint-disable no-console */ | 
				
			|||
 | 
				
			|||
import {register} from 'register-service-worker' | 
				
			|||
 | 
				
			|||
if (process.env.NODE_ENV === 'production' && window.location.protocol === "https:") { | 
				
			|||
import { register } from 'register-service-worker'; | 
				
			|||
 | 
				
			|||
if (process.env.NODE_ENV === 'production' && window.location.protocol === 'https:') { | 
				
			|||
  register(`${process.env.BASE_URL}service-worker.js`, { | 
				
			|||
    ready() { | 
				
			|||
            console.log('App is being served from cache by a service worker.') | 
				
			|||
      console.log('App is being served from cache by a service worker.'); | 
				
			|||
    }, | 
				
			|||
    registered() { | 
				
			|||
            console.log('Service worker has been registered.') | 
				
			|||
      console.log('Service worker has been registered.'); | 
				
			|||
    }, | 
				
			|||
    cached() { | 
				
			|||
            console.log('Content has been cached for offline use.') | 
				
			|||
      console.log('Content has been cached for offline use.'); | 
				
			|||
    }, | 
				
			|||
    updatefound() { | 
				
			|||
            console.log('New content is downloading.') | 
				
			|||
      console.log('New content is downloading.'); | 
				
			|||
    }, | 
				
			|||
    updated() { | 
				
			|||
      console.log('New content is available.'); | 
				
			|||
      window.location.reload(); | 
				
			|||
    }, | 
				
			|||
    offline() { | 
				
			|||
            console.log('No internet connection found. App is running in offline mode.') | 
				
			|||
      console.log('No internet connection found. App is running in offline mode.'); | 
				
			|||
    }, | 
				
			|||
    error(error) { | 
				
			|||
            console.error('Error during service worker registration:', error) | 
				
			|||
        } | 
				
			|||
    }) | 
				
			|||
      console.error('Error during service worker registration:', error); | 
				
			|||
    }, | 
				
			|||
  }); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
@ -1,17 +1,15 @@ | 
				
			|||
import Vue, {VNode} from 'vue' | 
				
			|||
import Vue, { VNode } from 'vue'; | 
				
			|||
 | 
				
			|||
declare global { | 
				
			|||
  namespace JSX { | 
				
			|||
    // tslint:disable no-empty-interface
 | 
				
			|||
    interface Element extends VNode { | 
				
			|||
    } | 
				
			|||
    interface Element extends VNode {} | 
				
			|||
 | 
				
			|||
    // tslint:disable no-empty-interface
 | 
				
			|||
    interface ElementClass extends Vue { | 
				
			|||
    } | 
				
			|||
    interface ElementClass extends Vue {} | 
				
			|||
 | 
				
			|||
    interface IntrinsicElements { | 
				
			|||
      [elem: string]: any | 
				
			|||
      [elem: string]: any; | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
@ -1,4 +1,4 @@ | 
				
			|||
declare module '*.vue' { | 
				
			|||
  import Vue from 'vue' | 
				
			|||
  export default Vue | 
				
			|||
  import Vue from 'vue'; | 
				
			|||
  export default Vue; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
@ -1,56 +1,73 @@ | 
				
			|||
import {fromByteArray as Base64Encode} from "base64-js"; | 
				
			|||
import { fromByteArray as Base64Encode } from 'base64-js'; | 
				
			|||
 | 
				
			|||
export const IXAREA_API_ENDPOINT = "https://um-api.ixarea.com" | 
				
			|||
export const IXAREA_API_ENDPOINT = 'https://um-api.ixarea.com'; | 
				
			|||
 | 
				
			|||
export interface UpdateInfo { | 
				
			|||
    Found: boolean | 
				
			|||
    HttpsFound: boolean | 
				
			|||
    Version: string | 
				
			|||
    URL: string | 
				
			|||
    Detail: string | 
				
			|||
  Found: boolean; | 
				
			|||
  HttpsFound: boolean; | 
				
			|||
  Version: string; | 
				
			|||
  URL: string; | 
				
			|||
  Detail: string; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export async function checkUpdate(version: string): Promise<UpdateInfo> { | 
				
			|||
    const resp = await fetch(IXAREA_API_ENDPOINT + "/music/app-version", { | 
				
			|||
        method: "POST", | 
				
			|||
        headers: {"Content-Type": "application/json"}, | 
				
			|||
        body: JSON.stringify({"Version": version}) | 
				
			|||
  const resp = await fetch(IXAREA_API_ENDPOINT + '/music/app-version', { | 
				
			|||
    method: 'POST', | 
				
			|||
    headers: { 'Content-Type': 'application/json' }, | 
				
			|||
    body: JSON.stringify({ Version: version }), | 
				
			|||
  }); | 
				
			|||
  return await resp.json(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export function reportKeyUsage(keyData: Uint8Array, maskData: number[], filename: string, format: string, title: string, artist?: string, album?: string) { | 
				
			|||
    return fetch(IXAREA_API_ENDPOINT + "/qmcmask/usage", { | 
				
			|||
        method: "POST", | 
				
			|||
        headers: {"Content-Type": "application/json"}, | 
				
			|||
export function reportKeyUsage( | 
				
			|||
  keyData: Uint8Array, | 
				
			|||
  maskData: number[], | 
				
			|||
  filename: string, | 
				
			|||
  format: string, | 
				
			|||
  title: string, | 
				
			|||
  artist?: string, | 
				
			|||
  album?: string, | 
				
			|||
) { | 
				
			|||
  return fetch(IXAREA_API_ENDPOINT + '/qmcmask/usage', { | 
				
			|||
    method: 'POST', | 
				
			|||
    headers: { 'Content-Type': 'application/json' }, | 
				
			|||
    body: JSON.stringify({ | 
				
			|||
            Mask: Base64Encode(new Uint8Array(maskData)), Key: Base64Encode(keyData), | 
				
			|||
            Artist: artist, Title: title, Album: album, Filename: filename, Format: format | 
				
			|||
      Mask: Base64Encode(new Uint8Array(maskData)), | 
				
			|||
      Key: Base64Encode(keyData), | 
				
			|||
      Artist: artist, | 
				
			|||
      Title: title, | 
				
			|||
      Album: album, | 
				
			|||
      Filename: filename, | 
				
			|||
      Format: format, | 
				
			|||
    }), | 
				
			|||
    }) | 
				
			|||
  }); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
interface KeyInfo { | 
				
			|||
    Matrix44: string | 
				
			|||
  Matrix44: string; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export async function queryKeyInfo(keyData: Uint8Array, filename: string, format: string): Promise<KeyInfo> { | 
				
			|||
    const resp = await fetch(IXAREA_API_ENDPOINT + "/qmcmask/query", { | 
				
			|||
        method: "POST", | 
				
			|||
        headers: {"Content-Type": "application/json"}, | 
				
			|||
        body: JSON.stringify({Format: format, Key: Base64Encode(keyData), Filename: filename, Type: 44}), | 
				
			|||
  const resp = await fetch(IXAREA_API_ENDPOINT + '/qmcmask/query', { | 
				
			|||
    method: 'POST', | 
				
			|||
    headers: { 'Content-Type': 'application/json' }, | 
				
			|||
    body: JSON.stringify({ Format: format, Key: Base64Encode(keyData), Filename: filename, Type: 44 }), | 
				
			|||
  }); | 
				
			|||
  return await resp.json(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export interface CoverInfo { | 
				
			|||
    Id: string | 
				
			|||
    Type: number | 
				
			|||
  Id: string; | 
				
			|||
  Type: number; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
export async function queryAlbumCover(title: string, artist?: string, album?: string): Promise<CoverInfo> { | 
				
			|||
    const endpoint = IXAREA_API_ENDPOINT + "/music/qq-cover" | 
				
			|||
    const params = new URLSearchParams([["Title", title], ["Artist", artist ?? ""], ["Album", album ?? ""]]) | 
				
			|||
    const resp = await fetch(`${endpoint}?${params.toString()}`) | 
				
			|||
    return await resp.json() | 
				
			|||
  const endpoint = IXAREA_API_ENDPOINT + '/music/qq-cover'; | 
				
			|||
  const params = new URLSearchParams([ | 
				
			|||
    ['Title', title], | 
				
			|||
    ['Artist', artist ?? ''], | 
				
			|||
    ['Album', album ?? ''], | 
				
			|||
  ]); | 
				
			|||
  const resp = await fetch(`${endpoint}?${params.toString()}`); | 
				
			|||
  return await resp.json(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
@ -1,4 +1,4 @@ | 
				
			|||
import {expose} from "threads/worker"; | 
				
			|||
import {CommonDecrypt} from "@/decrypt/common"; | 
				
			|||
import { expose } from 'threads/worker'; | 
				
			|||
import { CommonDecrypt } from '@/decrypt/common'; | 
				
			|||
 | 
				
			|||
expose(CommonDecrypt) | 
				
			|||
expose(CommonDecrypt); | 
				
			|||
 | 
				
			|||
					Loading…
					
					
				
		Reference in new issue