diff --git a/gulpfile.js b/gulpfile.js index e658768..d66ae16 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -23,7 +23,7 @@ gulp.task("pre-coverage", ["coverage-build"], function() { }); gulp.task('coverage', ['coverage-build', 'pre-coverage'], function() { - return gulp.src("coverage-dist/*.spec.js") + return gulp.src("coverage-dist/**/*.spec.js") .pipe(jasmine()) .pipe(istanbul.writeReports({ dir: './coverage', @@ -58,7 +58,7 @@ gulp.task('test-build', function() { }); gulp.task('test', ['test-build'], function() { - return gulp.src("test-dist/*.spec.js") + return gulp.src("test-dist/**/*.spec.js") .pipe(jasmine()); }); diff --git a/package.json b/package.json index 30e0f68..877c8da 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,18 @@ "description": "Password generator for BIOS", "main": "decrypt_bios.js", "dependencies": { + "@types/long": "^3.0.32", "@types/jasmine": "^2.8.3", "gulp-jasmine": "^3.0.0", - "typescript": "^2.6.2" + "typescript": "^2.6.2", + "long": "dcodeIO/long.js" }, "devDependencies": { "clean-webpack-plugin": "^0.1.18", "copy-webpack-plugin": "^4.3.1", "coveralls": "^3.0.0", - "gulp": "^3.9.1", "del": "^3.0.0", + "gulp": "^3.9.1", "gulp-istanbul": "^1.1.3", "gulp-sourcemaps": "^2.6.4", "gulp-tslint": "^8.1.2", diff --git a/src/keygen/index.ts b/src/keygen/index.ts index e3a8217..f8b247a 100644 --- a/src/keygen/index.ts +++ b/src/keygen/index.ts @@ -1,15 +1,16 @@ +import { monotonicTime } from "../polyfills/performancePolyfill"; import { asusSolver } from "./asus"; import { dellHddSolver, dellSolver, hddOldSolver } from "./dell"; import { fsi20DecNewSolver, fsi20DecOldSolver, fsiHexSolver } from "./fsi"; import { hpMiniSolver } from "./hpmini"; import { insydeSolver } from "./insyde"; -import { monotonicTime } from "../polyfills/performancePolyfill"; import { phoenixFsiLSolver, phoenixFsiPSolver, phoenixFsiSolver, phoenixFsiSSolver, phoenixFsiXSolver, phoenixHPCompaqSolver, phoenixSolver } from "./phoenix"; import { samsungSolver } from "./samsung"; import { sonySolver } from "./sony"; +import { sony4x4Solver } from "./sony_4x4"; import { Solver } from "./utils"; export type KeygenResult = [Solver, string[], number]; @@ -17,6 +18,7 @@ export type KeygenResult = [Solver, string[], number]; export const solvers: Solver[] = [ asusSolver, sonySolver, + sony4x4Solver, samsungSolver, hddOldSolver, dellSolver, diff --git a/src/keygen/sony_4x4.spec.ts b/src/keygen/sony_4x4.spec.ts new file mode 100644 index 0000000..2111460 --- /dev/null +++ b/src/keygen/sony_4x4.spec.ts @@ -0,0 +1,38 @@ +import * as Long from "long"; +import { modularPow, sony4x4Keygen, sony4x4Solver } from "./sony_4x4"; + +describe("Sony 4x4 BIOS Keygen", () => { + it("Sony 4x4 key for 73KR3FP9PVKHK29R is 32799624", () => { + expect(sony4x4Keygen("73KR3FP9PVKHK29R")).toEqual("32799624"); + }); + it("Sony 4x4 key for 73KR-3FP9-PVKH-K299 is 69423778", () => { + expect(sony4x4Keygen("73KR3FP9PVKHK299")).toEqual("69423778"); + }); + it("Sony 4x4 key for 9DPK-73KR-8JHX-F3RT is 54746568", () => { + expect(sony4x4Keygen("9DPK73KR8JHXF3RT")).toEqual("54746568"); + }); + it("Sony 4x4 key for 3RT6-8JV2-6HX8-K7FX is 32969527", () => { + expect(sony4x4Keygen("3RT68JV26HX8K7FX")).toEqual("32969527"); + }); + it("Sony 4x4 key for K29R-PVKH-3FP9-73KR is 65395983", () => { + expect(sony4x4Keygen("K29RPVKH3FP973KR")).toEqual("65395983"); + }); +}); + +describe("Sony 4x4 BIOS Solver", () => { + it("Sony 4x4 key for 73KR-3FP9-PVKH-K29R is 32799624", () => { + expect(sony4x4Solver("73KR-3FP9-PVKH-K29R")).toEqual(["32799624"]); + }); + it("Sony 4x4 invalid key 73KR-3FP9-PVKH-K29C", () => { + expect(sony4x4Solver("73KR-3FP9-PVKH-K29C")).toEqual([]); + }); +}); + +describe("test modularPow", () => { + it("4 ^ 13 mod 497 == 445", () => { + expect(modularPow(Long.fromInt(4), 13, 497)).toEqual(445); + }); + it("100500 ^ 100500 mod 14 == 8", () => { + expect(modularPow(Long.fromInt(100500), 100500, 14)).toEqual(8); + }); +}); diff --git a/src/keygen/sony_4x4.ts b/src/keygen/sony_4x4.ts new file mode 100644 index 0000000..913ae58 --- /dev/null +++ b/src/keygen/sony_4x4.ts @@ -0,0 +1,123 @@ +// based on dogbert's pwgen-sony-4x4.py +/* tslint:disable:no-bitwise */ +import * as Long from "long"; +import { makeSolver } from "./utils"; + +const otpChars: string = "9DPK7V2F3RT6HX8J"; +const pwdChars: string = "47592836"; +const inputRe: RegExp = new RegExp(`^[${otpChars}]{16}\$`); + +function arrayToNumber(arr: number[]): number { + // same as python struct.unpack(">> 0; +} + +function numberToArray(num: number): number[] { + return [num & 0xFF, (num >> 8) & 0xFF, (num >> 16) & 0xFF, (num >> 24) & 0xFF]; +} + +function decodeHash(hash: string): number[] { + let temp: number[] = []; + for (let i = 0; i < hash.length; i += 2) { + // TODO: check values + temp.unshift(otpChars.indexOf(hash[i]) * 16 + otpChars.indexOf(hash[i + 1])); + } + return temp; +} + +function encodePassword(pwd: number[]): string { + let n: number = arrayToNumber(pwd); + let result: string = ""; + for (let i = 0; i < 8; i++) { + result += pwdChars.charAt((n >> (21 - i * 3)) & 0x7); + } + return result; +} + +// http://numericalrecipes.blogspot.com/2009/03/modular-multiplicative-inverse.html +function extEuclideanAlg(a: Long, b: Long): [Long, Long, Long] { + if (b.isZero()) { + return [new Long(1), new Long(0), a]; + } else { + let [x, y, gcd] = extEuclideanAlg(b, a.mod(b)); + return [y, x.sub(y.mul(a.div(b))), gcd]; + } +} + +function modInvEuclid(a: Long, m: Long): Long | undefined { + let [x, , gcd] = extEuclideanAlg(a, m); + if (gcd.eq(1)) { + // hack for javascript modulo operation + // https://stackoverflow.com/questions/4467539/javascript-modulo-gives-a-negative-result-for-negative-numbers + const temp = x.mod(m); + return temp.gte(0) ? temp : temp.add(m); + } else { + return undefined; + } +} + +// https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method +export function modularPow(base: Long, exponent: number, modulus: number): number { + let result: Long = new Long(1, 0, true); + + if (modulus === 1) { + return 0; + } + + base = base.mod(modulus); + + while (exponent > 0) { + if ((exponent & 1) === 1) { + result = result.mul(base).mod(modulus); + } + exponent = exponent >> 1; + base = base.mul(base).mod(modulus); + } + return result.toNumber(); +} + +function rsaDecrypt(code: number[]): number[] { + const low: number = arrayToNumber(code.slice(0, 4)); + const high: number = arrayToNumber(code.slice(4, 8)); + const c: Long = Long.fromBits(low, high, true); + + const p: number = 2795287379; + const q: number = 3544934711; + const e = 41; + const phi = (new Long(p - 1, 0, true)).mul(q - 1); + const d: Long = modInvEuclid(new Long(e), phi) as Long; + + const dp = d.mod(p - 1); + const dq = d.mod(q - 1); + const qinv: Long = modInvEuclid(new Long(q), new Long(p)) as Long; + + const m1 = modularPow(c, dp.toNumber(), p); + const m2 = modularPow(c, dq.toNumber(), q); + let h: Long; + + if (m1 < m2) { + h = Long.fromValue(m1 - m2).add(p).mul(qinv).mod(p); + } else { + h = Long.fromValue(m1 - m2).mul(qinv).mod(p); + } + const m = h.mul(q).add(m2); + return numberToArray(m.low).concat(numberToArray(m.high)); +} + +export function sony4x4Keygen(hash: string): string { + const numHash = decodeHash(hash); + const pwd = rsaDecrypt(numHash); + return encodePassword(pwd); +} + +export let sony4x4Solver = makeSolver({ + name: "sony4x4", + description: "Sony 4x4", + examples: ["73KR-3FP9-PVKH-K29R"], + cleaner: (input: string) => input.trim().replace(/[-\s]/gi, "").toUpperCase(), + inputValidator: (s) => inputRe.test(s), + fun: (code: string) => { + let res = sony4x4Keygen(code); + return res ? [res] : []; + } +}); diff --git a/src/keygen/utils.ts b/src/keygen/utils.ts index 60c67b6..fdcadab 100644 --- a/src/keygen/utils.ts +++ b/src/keygen/utils.ts @@ -7,6 +7,7 @@ export interface SolverDescription { readonly examples?: string[]; fun: SolverFunction; inputValidator: Predicate; + cleaner?: (val: string) => string; } export interface Solver { @@ -75,7 +76,7 @@ function cleanSerial(serial: string): string { export function makeSolver(description: SolverDescription): Solver { let solver: any = (code: string) => { - let cleanCode = cleanSerial(code); + let cleanCode = solver.cleaner(code); if (description.inputValidator(cleanCode)) { return description.fun(cleanCode); } else { @@ -85,7 +86,13 @@ export function makeSolver(description: SolverDescription): Solver { solver.biosName = description.name; solver.validator = description.inputValidator; - solver.cleaner = cleanSerial; + + if (description.cleaner) { + solver.cleaner = description.cleaner; + } else { + solver.cleaner = cleanSerial; + } + solver.keygen = description.fun; if (description.examples) {