feat(QMCv2): add map cipher

This commit is contained in:
MengYX
2021-12-16 23:07:59 +08:00
parent c2c89a423f
commit 7306bf031f
12 changed files with 74 additions and 3 deletions
+40 -1
View File
@@ -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)
}
})
+34 -2
View File
@@ -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)
}
}
View File
View File
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
View File
View File
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.