mirror of
				https://git.unlock-music.dev/um/web.git
				synced 2025-11-04 08:03:29 +08:00 
			
		
		
		
	feat(QMCv2): add map cipher
(cherry picked from commit 7306bf031f8bc07168197c00e332bf89c8d611dd)
This commit is contained in:
		
							parent
							
								
									8c11f47aa4
								
							
						
					
					
						commit
						fe89710968
					
				@ -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)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								testdata/mflac_map_key.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								testdata/mflac_map_key.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								testdata/mflac_map_key_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								testdata/mflac_map_key_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mflac_map_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mflac_map_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mflac_map_suffix.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mflac_map_suffix.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mflac_map_target.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mflac_map_target.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								testdata/mgg_map_key.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								testdata/mgg_map_key.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								testdata/mgg_map_key_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								testdata/mgg_map_key_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mgg_map_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mgg_map_raw.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mgg_map_suffix.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mgg_map_suffix.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								testdata/mgg_map_target.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testdata/mgg_map_target.bin
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user