feat(QMCv2): add map cipher
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {QmcStaticCipher} from "@/decrypt/qmc_cipher";
|
||||
import {QmcMapCipher, QmcStaticCipher} from "@/decrypt/qmc_cipher";
|
||||
import fs from 'fs'
|
||||
|
||||
test("static cipher [0x7ff8,0x8000) ", () => {
|
||||
const expected = new Uint8Array([
|
||||
@@ -25,3 +26,41 @@ test("static cipher [0,0x10) ", () => {
|
||||
|
||||
expect(buf).toStrictEqual(expected)
|
||||
})
|
||||
|
||||
function loadTestDataMapCipher(name: string): {
|
||||
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`)
|
||||
}
|
||||
}
|
||||
|
||||
test("map cipher: get mask", () => {
|
||||
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 c = new QmcMapCipher(key)
|
||||
c.decrypt(buf, 0)
|
||||
expect(buf).toStrictEqual(expected)
|
||||
})
|
||||
|
||||
test("map cipher: real file", async () => {
|
||||
const cases = ["mflac_map", "mgg_map"]
|
||||
for (const name of cases) {
|
||||
const {key, clearText, cipherText} = loadTestDataMapCipher(name)
|
||||
const c = new QmcMapCipher(key)
|
||||
|
||||
c.decrypt(cipherText, 0)
|
||||
|
||||
expect(cipherText).toStrictEqual(clearText)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -33,11 +33,11 @@ const staticCipherBox = new Uint8Array([
|
||||
0xA5, 0x47, 0xF7, 0xF6, 0x00, 0x79, 0x4A, 0x11, //0xF8
|
||||
])
|
||||
|
||||
interface streamCipher {
|
||||
export interface StreamCipher {
|
||||
decrypt(buf: Uint8Array, offset: number): void
|
||||
}
|
||||
|
||||
export class QmcStaticCipher implements streamCipher {
|
||||
export class QmcStaticCipher implements StreamCipher {
|
||||
|
||||
public getMask(offset: number) {
|
||||
if (offset > 0x7FFF) offset %= 0x7FFF
|
||||
@@ -51,3 +51,35 @@ export class QmcStaticCipher implements streamCipher {
|
||||
}
|
||||
}
|
||||
|
||||
export class QmcMapCipher implements StreamCipher {
|
||||
key: Uint8Array
|
||||
n: number
|
||||
|
||||
constructor(key: Uint8Array) {
|
||||
if (key.length == 0) throw Error("qmc/cipher_map: invalid key size")
|
||||
|
||||
this.key = key
|
||||
this.n = key.length
|
||||
}
|
||||
|
||||
private static rotate(value: number, bits: number) {
|
||||
let rotate = (bits + 4) % 8;
|
||||
let left = value << rotate;
|
||||
let right = value >> rotate;
|
||||
return (left | right) & 0xff;
|
||||
}
|
||||
|
||||
decrypt(buf: Uint8Array, offset: number): void {
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
buf[i] ^= this.getMask(offset + i)
|
||||
}
|
||||
}
|
||||
|
||||
private getMask(offset: number) {
|
||||
if (offset > 0x7fff) offset %= 0x7fff;
|
||||
|
||||
const idx = (offset * offset + 71214) % this.n;
|
||||
return QmcMapCipher.rotate(this.key[idx], idx & 0x7)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
Vendored
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
Vendored
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user