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) ", () => { test("static cipher [0x7ff8,0x8000) ", () => {
const expected = new Uint8Array([ const expected = new Uint8Array([
@@ -25,3 +26,41 @@ test("static cipher [0,0x10) ", () => {
expect(buf).toStrictEqual(expected) 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 0xA5, 0x47, 0xF7, 0xF6, 0x00, 0x79, 0x4A, 0x11, //0xF8
]) ])
interface streamCipher { export interface StreamCipher {
decrypt(buf: Uint8Array, offset: number): void decrypt(buf: Uint8Array, offset: number): void
} }
export class QmcStaticCipher implements streamCipher { export class QmcStaticCipher implements StreamCipher {
public getMask(offset: number) { public getMask(offset: number) {
if (offset > 0x7FFF) offset %= 0x7FFF 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.