Port Phoenix code to TypeScript
This commit is contained in:
1 parent
4fd486017e
commit
bec3f4027a
3 files changed
+259
-1
No files matched your search
@@ -0,0 +1,27 @@
|
||||
import * as bios from "./phoenix";
|
||||
|
||||
describe("Test Phoenix BIOS", () => {
|
||||
it("check correct passwords", () => {
|
||||
expect(bios.phoenixSolver.calculateHash("abstoou")).toEqual(12345);
|
||||
expect(bios.phoenixHPCompaqSolver.calculateHash("vnflm")).toEqual(12345);
|
||||
expect(bios.phoenixFsiSolver.calculateHash("411113")).toEqual(12345);
|
||||
expect(bios.phoenixFsiLSolver.calculateHash("362153")).toEqual(12345);
|
||||
expect(bios.phoenixFsiPSolver.calculateHash("4465237")).toEqual(12345);
|
||||
expect(bios.phoenixFsiSSolver.calculateHash("71669")).toEqual(12345);
|
||||
expect(bios.phoenixFsiXSolver.calculateHash("739979")).toEqual(12345);
|
||||
});
|
||||
|
||||
it("find password for hash 12345", () => {
|
||||
function phoenixValidate(solver: bios.PhoenixSolver, hash: string): number {
|
||||
return solver.calculateHash(solver(hash)[0]);
|
||||
}
|
||||
|
||||
expect(phoenixValidate(bios.phoenixSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixHPCompaqSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixFsiSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixFsiLSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixFsiPSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixFsiSSolver, "12345")).toEqual(12345);
|
||||
expect(phoenixValidate(bios.phoenixFsiXSolver, "12345")).toEqual(12345);
|
||||
});
|
||||
});
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/* tslint:disable:no-bitwise */
|
||||
import {
|
||||
asciiToKeyboardEnc, keyboardEncToAscii, makeSolver, reversedScanCodes, Solver
|
||||
} from "./utils";
|
||||
|
||||
export const enum PhoenixErrors {
|
||||
BadHash,
|
||||
NotFound
|
||||
}
|
||||
|
||||
export interface PhoenixInfo {
|
||||
shift: number;
|
||||
salt: number;
|
||||
dictionary: string[];
|
||||
minLen: number;
|
||||
maxLen: number;
|
||||
}
|
||||
|
||||
export interface PhoenixBios {
|
||||
name: string;
|
||||
description?: string;
|
||||
shift?: number;
|
||||
salt?: number;
|
||||
dictionary?: string[];
|
||||
minLen?: number;
|
||||
maxLen?: number;
|
||||
}
|
||||
|
||||
export interface PhoenixSolver extends Solver {
|
||||
readonly info: PhoenixInfo;
|
||||
calculateHash(password: string): number;
|
||||
}
|
||||
|
||||
// Without zero
|
||||
const digitsOnly: string[] = "123456789".split("");
|
||||
|
||||
const lettersOnly: string[] = "abcdefghijklmnopqrstuvwxyz".split("");
|
||||
|
||||
const alphaNumeric: string[] = lettersOnly.concat(digitsOnly);
|
||||
|
||||
const defaultPhoenix: PhoenixInfo = {
|
||||
shift: 0,
|
||||
salt: 0,
|
||||
dictionary: lettersOnly,
|
||||
minLen: 3,
|
||||
maxLen: 7
|
||||
};
|
||||
|
||||
/* The phoenix implementation of the CRC-16 contains a rather severe bug
|
||||
* quartering the image space of the function: both the first and second MSB
|
||||
* are always zero regardless of the input.
|
||||
* For a working implementation, you'd have to change the polynom from 0x2001
|
||||
* to e.g. 0xA001.
|
||||
*/
|
||||
|
||||
function badCRC16(pwd: number[], salt: number = 0): number {
|
||||
|
||||
let hash = salt;
|
||||
// tslint:disable-next-line:prefer-for-of
|
||||
for (let c = 0; c < pwd.length; c++) {
|
||||
hash ^= pwd[c];
|
||||
for (let i = 8; i--;) {
|
||||
if (hash & 1) {
|
||||
hash = (hash >> 1) ^ 0x2001;
|
||||
} else {
|
||||
hash = (hash >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
function generatePhoenixPassword(encodedPwd: number[], characters: string[] = lettersOnly): void {
|
||||
let rnd = Math.random() * characters.length;
|
||||
for (let i = 0; i < encodedPwd.length; i++) {
|
||||
let index = Math.floor(rnd % characters.length);
|
||||
encodedPwd[i] = reversedScanCodes[characters[index]];
|
||||
rnd *= encodedPwd.length;
|
||||
}
|
||||
}
|
||||
|
||||
function bruteForce(hash: number, salt: number = 0, characters: string[] = lettersOnly,
|
||||
minLen: number = 3, maxLen: number = 7): string | PhoenixErrors {
|
||||
|
||||
let encodedPwd: number[] = [0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
if (hash > 0x3FFF) {
|
||||
return PhoenixErrors.BadHash;
|
||||
}
|
||||
|
||||
let kk = 0;
|
||||
while (true) {
|
||||
kk++;
|
||||
if (kk > 50000) {
|
||||
return PhoenixErrors.NotFound;
|
||||
}
|
||||
|
||||
generatePhoenixPassword(encodedPwd, characters);
|
||||
for (let i = minLen; i <= maxLen; i++) {
|
||||
if (badCRC16(encodedPwd.slice(0, i), salt) === hash) {
|
||||
return keyboardEncToAscii(encodedPwd.slice(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makePhoenixSolver(description?: PhoenixBios): PhoenixSolver {
|
||||
if (description === void 0) {
|
||||
description = {} as PhoenixBios;
|
||||
}
|
||||
|
||||
for (let key in defaultPhoenix) {
|
||||
if ((description as any)[key] === void 0) {
|
||||
(description as any)[key] = (defaultPhoenix as any)[key];
|
||||
}
|
||||
}
|
||||
|
||||
const info: PhoenixInfo = {
|
||||
shift: description.shift as number,
|
||||
salt: description.salt as number,
|
||||
dictionary: description.dictionary as string[],
|
||||
minLen: description.minLen as number,
|
||||
maxLen: description.maxLen as number
|
||||
};
|
||||
|
||||
let keygen = (code: string) => {
|
||||
let password = bruteForce(parseInt(code, 10) + info.shift, info.salt,
|
||||
info.dictionary, info.minLen, info.maxLen);
|
||||
|
||||
if (typeof password === "string") {
|
||||
return [password];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
let cleaner = (code: string): string => code.trim().replace(/[-\s]/gi, "");
|
||||
let validator = (s: string): boolean => /^[0-9]{5}$/i.test(s);
|
||||
let calculateHash = (password: string): number => {
|
||||
let pwd = asciiToKeyboardEnc(password);
|
||||
return badCRC16(pwd, info.salt) - info.shift;
|
||||
};
|
||||
let solver: any = (code: string): string[] => {
|
||||
let cleanCode = cleaner(code);
|
||||
if (validator(cleanCode)) {
|
||||
return keygen(cleanCode);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
solver.biosName = description.name;
|
||||
solver.validator = validator;
|
||||
solver.cleaner = cleaner;
|
||||
solver.keygen = keygen;
|
||||
solver.examples = ["12345"];
|
||||
solver.info = info;
|
||||
solver.calculateHash = calculateHash;
|
||||
|
||||
if (description.description) {
|
||||
solver.description = description;
|
||||
}
|
||||
return solver as PhoenixSolver;
|
||||
}
|
||||
|
||||
export let phoenixSolver = makePhoenixSolver({
|
||||
name: "Generic Phoenix"
|
||||
});
|
||||
|
||||
export let phoenixHPCompaqSolver = makePhoenixSolver({
|
||||
name: "HP/Compaq Phoenix BIOS",
|
||||
salt: 17232
|
||||
});
|
||||
|
||||
export let phoenixFsiSolver = makePhoenixSolver({
|
||||
name: "Fujitsu-Siemens Phoenix",
|
||||
salt: 65,
|
||||
dictionary: digitsOnly
|
||||
});
|
||||
|
||||
export let phoenixFsiLSolver = makePhoenixSolver({
|
||||
name: "Fujitsu-Siemens (model L) Phoenix",
|
||||
shift: 1,
|
||||
salt: "L".charCodeAt(0),
|
||||
dictionary: digitsOnly
|
||||
});
|
||||
|
||||
export let phoenixFsiPSolver = makePhoenixSolver({
|
||||
name: "Fujitsu-Siemens (model P) Phoenix",
|
||||
shift: 1,
|
||||
salt: "P".charCodeAt(0),
|
||||
dictionary: digitsOnly
|
||||
});
|
||||
|
||||
export let phoenixFsiSSolver = makePhoenixSolver({
|
||||
name: "Fujitsu-Siemens (model S) Phoenix",
|
||||
shift: 1,
|
||||
salt: "S".charCodeAt(0),
|
||||
dictionary: digitsOnly
|
||||
});
|
||||
|
||||
export let phoenixFsiXSolver = makePhoenixSolver({
|
||||
name: "Fujitsu-Siemens (model X) Phoenix",
|
||||
shift: 1,
|
||||
salt: "X".charCodeAt(0),
|
||||
dictionary: digitsOnly
|
||||
});
|
||||
+25
-1
@@ -19,7 +19,7 @@ export interface Solver {
|
||||
keygen(code: string): string[];
|
||||
}
|
||||
|
||||
const keyboardDict: {[key: number]: string} = {
|
||||
export const keyboardDict: {[key: number]: string} = {
|
||||
2: "1", 3: "2", 4: "3", 5: "4", 6: "5", 7: "6", 8: "7", 9: "8",
|
||||
10: "9", 11: "0", 16: "q", 17: "w", 18: "e", 19: "r", 20: "t", 21: "y",
|
||||
22: "u", 23: "i", 24: "o", 25: "p", 30: "a", 31: "s", 32: "d", 33: "f",
|
||||
@@ -27,6 +27,19 @@ const keyboardDict: {[key: number]: string} = {
|
||||
47: "v", 48: "b", 49: "n", 50: "m"
|
||||
};
|
||||
|
||||
// reverse scan code table
|
||||
function generateReverseKeyDict(): {[key: string]: number} {
|
||||
let result: {[key: string]: number} = {};
|
||||
for (let key in keyboardDict) {
|
||||
if (keyboardDict.hasOwnProperty(key)) {
|
||||
result[keyboardDict[key]] = parseInt(key, 10);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const reversedScanCodes = generateReverseKeyDict();
|
||||
|
||||
/* Decode Keyboard code to Ascii symbol */
|
||||
export function keyboardEncToAscii(inKey: number[]): string {
|
||||
let out = "";
|
||||
@@ -44,6 +57,17 @@ export function keyboardEncToAscii(inKey: number[]): string {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function asciiToKeyboardEnc(password: string): number[] {
|
||||
return password.split("").map((c) => {
|
||||
let code = reversedScanCodes[c];
|
||||
if (code === void 0) {
|
||||
throw new Error("Undefined scan code");
|
||||
} else {
|
||||
return code;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanSerial(serial: string): string {
|
||||
return serial.trim().replace(/-/gi, "");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user