feat: add secret derivation for KeePass key files
This commit is contained in:
@@ -13,3 +13,20 @@ export function deriveKey(password: string, salt: Uint8Array, rounds: number, le
|
||||
|
||||
return new Uint8Array(pbkdf2Sync(password, Buffer.from(salt), rounds, length, "sha256"));
|
||||
}
|
||||
|
||||
export function normalizeKeyFileBytes(bytes: Uint8Array): Uint8Array {
|
||||
return sha256(bytes);
|
||||
}
|
||||
|
||||
export function combineSecrets(password: string, keyFileBytes?: Uint8Array): Uint8Array {
|
||||
const passwordHash = sha256(password);
|
||||
if (!keyFileBytes) return passwordHash;
|
||||
return sha256(Buffer.concat([Buffer.from(passwordHash), Buffer.from(normalizeKeyFileBytes(keyFileBytes))]));
|
||||
}
|
||||
|
||||
export function deriveMasterKey(secret: Uint8Array, salt: Uint8Array, rounds: number): Uint8Array {
|
||||
if (secret.length === 0) {
|
||||
throw new Error("Missing secret for key derivation");
|
||||
}
|
||||
return new Uint8Array(pbkdf2Sync(Buffer.from(secret), Buffer.from(salt), rounds, 32, "sha256"));
|
||||
}
|
||||
|
||||
+2
-1
@@ -16,9 +16,10 @@ export function parseKdbxHeader(buffer: Uint8Array): { header: KdbxHeader; offse
|
||||
const versionMinor = reader.readUint16LE();
|
||||
const versionMajor = reader.readUint16LE();
|
||||
const version = { major: versionMajor, minor: versionMinor };
|
||||
const header: KdbxHeader = { version };
|
||||
|
||||
return {
|
||||
header: { version },
|
||||
header,
|
||||
offset: buffer.length - reader.remaining(),
|
||||
};
|
||||
}
|
||||
|
||||
+9
-1
@@ -7,7 +7,7 @@ import type {
|
||||
KeePassGroupInput,
|
||||
KeePassOpenOptions,
|
||||
} from "./types";
|
||||
import { cloneSnapshot, createEmptySnapshot, parseKdbxFile, type KdbxHeader } from "./kdbx";
|
||||
import { cloneSnapshot, combineSecrets, createEmptySnapshot, parseKdbxFile, type KdbxHeader } from "./kdbx";
|
||||
|
||||
const EMPTY = "";
|
||||
|
||||
@@ -16,6 +16,7 @@ export class KeePassDatabase {
|
||||
private dirty = false;
|
||||
|
||||
private header: KdbxHeader | null = null;
|
||||
private secret: Uint8Array | null = null;
|
||||
|
||||
constructor(
|
||||
private path: string,
|
||||
@@ -85,10 +86,17 @@ export class KeePassDatabase {
|
||||
const buffer = new Uint8Array(await readFile(this.path));
|
||||
const parsed = parseKdbxFile(buffer);
|
||||
this.header = parsed.header;
|
||||
if (this.options.keyFile) {
|
||||
const keyFile = new Uint8Array(await readFile(this.options.keyFile));
|
||||
this.secret = combineSecrets(this.options.password, keyFile);
|
||||
} else {
|
||||
this.secret = combineSecrets(this.options.password);
|
||||
}
|
||||
this.snapshot = {
|
||||
header: parsed.header,
|
||||
groups: [{ name: "Racine", path: "" }],
|
||||
entries: [],
|
||||
keyFiles: this.options.keyFile ? [this.options.keyFile] : [],
|
||||
};
|
||||
return;
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user