@hazae41/kdbx
Version:
Rust-like KeePass (KDBX 4) file format for TypeScript
598 lines (597 loc) • 24.3 kB
JavaScript
// deno-lint-ignore-file no-namespace
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
export * from "./cipher/mod.js";
export * from "./compression/mod.js";
import { Bytes } from "../../../../libs/bytes/mod.js";
import { BytesAsUuid, StringAsUuid } from "../../../../libs/uuid/mod.js";
import { Dictionary, Entries, Value } from "../../dictionary/mod.js";
import { PreHmacKey } from "../../hmac/mod.js";
import { DerivedKey, MasterKeys, PreHmacMasterKey, PreMasterKey } from "../../mod.js";
import { Vector } from "../../vector/mod.js";
import { argon2 } from "@hazae41/argon2";
import { Readable, Unknown, Writable } from "@hazae41/binary";
import { Cipher } from "./cipher/mod.js";
import { Compression } from "./compression/mod.js";
export class Version {
major;
minor;
constructor(major, minor) {
this.major = major;
this.minor = minor;
}
sizeOrThrow() {
return 2 + 2;
}
writeOrThrow(cursor) {
cursor.writeUint16OrThrow(this.minor, true);
cursor.writeUint16OrThrow(this.major, true);
}
cloneOrThrow() {
return this;
}
}
export class MagicAndVersionAndHeadersWithBytesWithHashAndHmacWithKeys {
data;
keys;
constructor(data, keys) {
this.data = data;
this.keys = keys;
}
sizeOrThrow() {
return this.data.sizeOrThrow();
}
writeOrThrow(cursor) {
this.data.writeOrThrow(cursor);
}
async rotateOrThrow(composite) {
const data = this.data.data.rotateOrThrow();
const keys = await data.deriveOrThrow(composite);
const hash = await MagicAndVersionAndHeadersWithBytesWithHashAndHmac.computeOrThrow(data, keys);
return new MagicAndVersionAndHeadersWithBytesWithHashAndHmacWithKeys(hash, keys);
}
}
export class MagicAndVersionAndHeadersWithBytesWithHashAndHmac {
data;
hash;
hmac;
constructor(data, hash, hmac) {
this.data = data;
this.hash = hash;
this.hmac = hmac;
}
static async computeOrThrow(data, keys) {
const index = 0xffffffffffffffffn;
const major = keys.authifier.bytes;
const key = await new PreHmacKey(index, major).digestOrThrow();
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", data.bytes.bytes));
const hmac = new Uint8Array(await key.signOrThrow(data.bytes.bytes));
return new MagicAndVersionAndHeadersWithBytesWithHashAndHmac(data, new Unknown(hash), new Unknown(hmac));
}
async verifyOrThrow(keys) {
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", this.data.bytes.bytes));
if (!Bytes.equals(hash, this.hash.bytes))
throw new Error();
const index = 0xffffffffffffffffn;
const major = keys.authifier.bytes;
const key = await new PreHmacKey(index, major).digestOrThrow();
await key.verifyOrThrow(this.data.bytes.bytes, this.hmac.bytes);
}
sizeOrThrow() {
return this.data.sizeOrThrow() + 32 + 32;
}
writeOrThrow(cursor) {
this.data.writeOrThrow(cursor);
cursor.writeOrThrow(this.hash.bytes);
cursor.writeOrThrow(this.hmac.bytes);
}
cloneOrThrow() {
const data = this.data.cloneOrThrow();
const hash = this.hash.cloneOrThrow();
const hmac = this.hmac.cloneOrThrow();
return new MagicAndVersionAndHeadersWithBytesWithHashAndHmac(data, hash, hmac);
}
async deriveOrThrow(composite) {
return await this.data.deriveOrThrow(composite);
}
}
(function (MagicAndVersionAndHeadersWithBytesWithHashAndHmac) {
function readOrThrow(cursor) {
const data = MagicAndVersionAndHeadersWithBytes.readOrThrow(cursor);
const hash = new Unknown(cursor.readOrThrow(32));
const hmac = new Unknown(cursor.readOrThrow(32));
return new MagicAndVersionAndHeadersWithBytesWithHashAndHmac(data, hash, hmac);
}
MagicAndVersionAndHeadersWithBytesWithHashAndHmac.readOrThrow = readOrThrow;
})(MagicAndVersionAndHeadersWithBytesWithHashAndHmac || (MagicAndVersionAndHeadersWithBytesWithHashAndHmac = {}));
export class MagicAndVersionAndHeadersWithBytes {
value;
bytes;
constructor(value, bytes) {
this.value = value;
this.bytes = bytes;
}
static computeOrThrow(value) {
const bytes = new Unknown(Writable.writeToBytesOrThrow(value));
return new MagicAndVersionAndHeadersWithBytes(value, bytes);
}
rotateOrThrow() {
return MagicAndVersionAndHeadersWithBytes.computeOrThrow(this.value.rotateOrThrow());
}
sizeOrThrow() {
return this.bytes.bytes.length;
}
writeOrThrow(cursor) {
cursor.writeOrThrow(this.bytes.bytes);
}
cloneOrThrow() {
const value = this.value.cloneOrThrow();
const bytes = this.bytes.cloneOrThrow();
return new MagicAndVersionAndHeadersWithBytes(value, bytes);
}
async deriveOrThrow(composite) {
return await this.value.deriveOrThrow(composite);
}
}
(function (MagicAndVersionAndHeadersWithBytes) {
function readOrThrow(cursor) {
const start = cursor.offset;
const value = MagicAndVersionAndHeaders.readOrThrow(cursor);
const bytes = new Unknown(cursor.bytes.subarray(start, cursor.offset));
return new MagicAndVersionAndHeadersWithBytes(value, bytes);
}
MagicAndVersionAndHeadersWithBytes.readOrThrow = readOrThrow;
})(MagicAndVersionAndHeadersWithBytes || (MagicAndVersionAndHeadersWithBytes = {}));
export class MagicAndVersionAndHeaders {
version;
headers;
constructor(version, headers) {
this.version = version;
this.headers = headers;
}
rotateOrThrow() {
const { version } = this;
const headers = this.headers.rotateOrThrow();
return new MagicAndVersionAndHeaders(version, headers);
}
sizeOrThrow() {
return 4 + 4 + this.version.sizeOrThrow() + this.headers.sizeOrThrow();
}
writeOrThrow(cursor) {
cursor.writeUint32OrThrow(0x9AA2D903, true);
cursor.writeUint32OrThrow(0xB54BFB67, true);
this.version.writeOrThrow(cursor);
this.headers.writeOrThrow(cursor);
}
cloneOrThrow() {
const version = this.version.cloneOrThrow();
const headers = this.headers.cloneOrThrow();
return new MagicAndVersionAndHeaders(version, headers);
}
async deriveOrThrow(composite) {
return await this.headers.deriveOrThrow(composite);
}
}
(function (MagicAndVersionAndHeaders) {
function readOrThrow(cursor) {
const alpha = cursor.readUint32OrThrow(true);
if (alpha !== 0x9AA2D903)
throw new Error();
const beta = cursor.readUint32OrThrow(true);
if (beta !== 0xB54BFB67)
throw new Error();
const minor = cursor.readUint16OrThrow(true);
const major = cursor.readUint16OrThrow(true);
const version = new Version(major, minor);
if (major !== 4)
throw new Error();
const headers = Headers.readOrThrow(cursor);
return new MagicAndVersionAndHeaders(version, headers);
}
MagicAndVersionAndHeaders.readOrThrow = readOrThrow;
})(MagicAndVersionAndHeaders || (MagicAndVersionAndHeaders = {}));
export class Headers {
value;
constructor(value) {
this.value = value;
}
get cipher() {
return this.value.value[2][0];
}
get compression() {
return this.value.value[3][0];
}
get seed() {
return this.value.value[4][0];
}
get iv() {
return this.value.value[7][0];
}
get kdf() {
return this.value.value[11][0];
}
get custom() {
return this.value.value[12]?.[0];
}
rotateOrThrow() {
const { cipher, compression, custom } = this;
const seed = new Unknown(crypto.getRandomValues(new Uint8Array(32)));
const iv = new Unknown(crypto.getRandomValues(new Uint8Array(cipher.IV.length)));
const kdf = this.kdf.rotateOrThrow();
return Headers.initOrThrow({ cipher, compression, seed, iv, kdf, custom });
}
sizeOrThrow() {
return this.value.sizeOrThrow();
}
writeOrThrow(cursor) {
this.value.writeOrThrow(cursor);
}
cloneOrThrow() {
return Readable.readFromBytesOrThrow(Headers, Writable.writeToBytesOrThrow(this));
}
async deriveOrThrow(composite) {
const { seed } = this;
const derived = this.kdf.deriveOrThrow(composite);
const encrypter = await new PreMasterKey(seed, derived).digestOrThrow();
const authifier = await new PreHmacMasterKey(seed, derived).digestOrThrow();
return new MasterKeys(encrypter, authifier);
}
}
(function (Headers) {
function initOrThrow(init) {
const { cipher, compression, seed, iv, kdf, custom } = init;
if (iv.bytes.length !== cipher.IV.length)
throw new Error();
const indexed = {
2: [cipher],
3: [compression],
4: [new Unknown(seed.bytes)],
7: [new Unknown(iv.bytes)],
11: [kdf],
12: custom != null ? [custom] : undefined
};
return new Headers(Vector.initOrThrow(indexed));
}
Headers.initOrThrow = initOrThrow;
function readOrThrow(cursor) {
const vector = Vector.readOrThrow(cursor);
if (vector.value[2].length !== 1)
throw new Error();
if (vector.value[3].length !== 1)
throw new Error();
if (vector.value[4].length !== 1)
throw new Error();
if (vector.value[4][0].bytes.length !== 32)
throw new Error();
if (vector.value[7].length !== 1)
throw new Error();
if (vector.value[11].length !== 1)
throw new Error();
if (vector.value[12] != null && vector.value[12].length !== 1)
throw new Error();
const indexed = {
2: [vector.value[2][0].readIntoOrThrow(Cipher)],
3: [vector.value[3][0].readIntoOrThrow(Compression)],
4: [vector.value[4][0]],
7: [vector.value[7][0]],
11: [vector.value[11][0].readIntoOrThrow(KdfParameters)],
12: vector.value[12] != null ? [vector.value[12][0].readIntoOrThrow(Dictionary)] : undefined
};
return new Headers(new Vector(vector.bytes, indexed));
}
Headers.readOrThrow = readOrThrow;
})(Headers || (Headers = {}));
export class Seed {
bytes;
constructor(bytes) {
this.bytes = bytes;
}
static readOrThrow(cursor) {
return new Seed(new Unknown(cursor.readOrThrow(32)));
}
}
export var KdfParameters;
(function (KdfParameters) {
class AesKdf {
value;
constructor(value) {
this.value = value;
}
get seed() {
return this.value.entries.value["S"].value;
}
get rounds() {
return this.value.entries.value["R"].value;
}
rotateOrThrow() {
const { version } = this.value;
const $UUID = this.value.entries.value["$UUID"];
const R = this.value.entries.value["R"];
const S = new Value.Bytes(new Unknown(crypto.getRandomValues(new Uint8Array(32))));
const value = Dictionary.initOrThrow(version, { $UUID, R, S });
return new AesKdf(value);
}
deriveOrThrow(key) {
throw new Error();
}
sizeOrThrow() {
return this.value.sizeOrThrow();
}
writeOrThrow(cursor) {
this.value.writeOrThrow(cursor);
}
cloneOrThrow() {
return new AesKdf(this.value.cloneOrThrow());
}
}
KdfParameters.AesKdf = AesKdf;
(function (AesKdf) {
AesKdf.$UUID = "c9d9f39a-628a-4460-bf74-0d08c18a4fea";
function parseOrThrow(dictionary) {
const { version, entries } = dictionary;
if (entries.value["$UUID"] instanceof Value.Bytes === false)
throw new Error();
const $UUID = entries.value["$UUID"];
if (entries.value.R instanceof Value.UInt32 === false)
throw new Error();
const R = entries.value.R;
if (entries.value.S instanceof Value.Bytes === false)
throw new Error();
const S = entries.value.S;
return new KdfParameters.AesKdf(new Dictionary(version, new Entries(entries.bytes, { $UUID, R, S })));
}
AesKdf.parseOrThrow = parseOrThrow;
})(AesKdf = KdfParameters.AesKdf || (KdfParameters.AesKdf = {}));
class Argon2d {
value;
constructor(value) {
this.value = value;
}
get salt() {
return this.value.entries.value["S"].value;
}
get parallelism() {
return this.value.entries.value["P"].value;
}
get memory() {
return this.value.entries.value["M"].value;
}
get iterations() {
return this.value.entries.value["I"].value;
}
get version() {
return this.value.entries.value["V"].value;
}
rotateOrThrow() {
const { version } = this.value;
const $UUID = this.value.entries.value["$UUID"];
const S = new Value.Bytes(new Unknown(crypto.getRandomValues(new Uint8Array(32))));
const P = this.value.entries.value.P;
const M = this.value.entries.value.M;
const I = this.value.entries.value.I;
const V = this.value.entries.value.V;
const value = Dictionary.initOrThrow(version, { $UUID, S, P, M, I, V });
return new Argon2d(value);
}
deriveOrThrow(key) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const { version, iterations, parallelism, memory, salt } = this;
const { Memory, Argon2Deriver } = argon2.get().getOrThrow();
const mkey = __addDisposableResource(env_1, Memory.fromOrThrow(key.value.bytes), false);
const msalt = __addDisposableResource(env_1, Memory.fromOrThrow(salt.bytes), false);
const deriver = __addDisposableResource(env_1, Argon2Deriver.createOrThrow("argon2d", version, Number(memory) / 1024, Number(iterations), parallelism), false);
const derived = __addDisposableResource(env_1, deriver.deriveOrThrow(mkey, msalt), false);
return new DerivedKey(new Unknown(new Uint8Array(derived.bytes)));
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
}
sizeOrThrow() {
return this.value.sizeOrThrow();
}
writeOrThrow(cursor) {
this.value.writeOrThrow(cursor);
}
cloneOrThrow() {
return new Argon2d(this.value.cloneOrThrow());
}
}
KdfParameters.Argon2d = Argon2d;
(function (Argon2d) {
Argon2d.$UUID = "ef636ddf-8c29-444b-91f7-a9a403e30a0c";
function createOrThrow() {
const version = new Dictionary.Version(0, 1);
const $UUID = new Value.Bytes(new Unknown(BytesAsUuid.from(Argon2d.$UUID)));
const S = new Value.Bytes(new Unknown(crypto.getRandomValues(new Uint8Array(32))));
const P = new Value.UInt32(2);
const M = new Value.UInt64(16777216n);
const I = new Value.UInt64(12n);
const V = new Value.UInt32(0x13);
const value = Dictionary.initOrThrow(version, { $UUID, S, P, M, I, V });
return new Argon2d(value);
}
Argon2d.createOrThrow = createOrThrow;
function parseOrThrow(dictionary) {
const { version, entries } = dictionary;
if (dictionary.entries.value["$UUID"] instanceof Value.Bytes === false)
throw new Error();
const $UUID = dictionary.entries.value["$UUID"];
if (dictionary.entries.value.S instanceof Value.Bytes === false)
throw new Error();
const S = dictionary.entries.value.S;
if (dictionary.entries.value.P instanceof Value.UInt32 === false)
throw new Error();
const P = dictionary.entries.value.P;
if (dictionary.entries.value.M instanceof Value.UInt64 === false)
throw new Error();
const M = dictionary.entries.value.M;
if (dictionary.entries.value.I instanceof Value.UInt64 === false)
throw new Error();
const I = dictionary.entries.value.I;
if (dictionary.entries.value.V instanceof Value.UInt32 === false)
throw new Error();
const V = dictionary.entries.value.V;
return new KdfParameters.Argon2d(new Dictionary(version, new Entries(entries.bytes, { $UUID, S, P, M, I, V })));
}
Argon2d.parseOrThrow = parseOrThrow;
})(Argon2d = KdfParameters.Argon2d || (KdfParameters.Argon2d = {}));
class Argon2id {
value;
constructor(value) {
this.value = value;
}
get salt() {
return this.value.entries.value["S"].value;
}
get parallelism() {
return this.value.entries.value["P"].value;
}
get memory() {
return this.value.entries.value["M"].value;
}
get iterations() {
return this.value.entries.value["I"].value;
}
get version() {
return this.value.entries.value["V"].value;
}
rotateOrThrow() {
const { version } = this.value;
const $UUID = this.value.entries.value["$UUID"];
const S = new Value.Bytes(new Unknown(crypto.getRandomValues(new Uint8Array(32))));
const P = this.value.entries.value.P;
const M = this.value.entries.value.M;
const I = this.value.entries.value.I;
const V = this.value.entries.value.V;
const value = Dictionary.initOrThrow(version, { $UUID, S, P, M, I, V });
return new Argon2d(value);
}
deriveOrThrow(key) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
const { version, iterations, parallelism, memory, salt } = this;
const { Memory, Argon2Deriver } = argon2.get().getOrThrow();
const mkey = __addDisposableResource(env_2, Memory.fromOrThrow(key.value.bytes), false);
const msalt = __addDisposableResource(env_2, Memory.fromOrThrow(salt.bytes), false);
const deriver = __addDisposableResource(env_2, Argon2Deriver.createOrThrow("argon2id", version, Number(memory) / 1024, Number(iterations), parallelism), false);
const derived = __addDisposableResource(env_2, deriver.deriveOrThrow(mkey, msalt), false);
return new DerivedKey(new Unknown(new Uint8Array(derived.bytes)));
}
catch (e_2) {
env_2.error = e_2;
env_2.hasError = true;
}
finally {
__disposeResources(env_2);
}
}
sizeOrThrow() {
return this.value.sizeOrThrow();
}
writeOrThrow(cursor) {
this.value.writeOrThrow(cursor);
}
cloneOrThrow() {
return new Argon2id(this.value.cloneOrThrow());
}
}
KdfParameters.Argon2id = Argon2id;
(function (Argon2id) {
Argon2id.$UUID = "9e298b19-56db-4773-b23d-fc3ec6f0a1e6";
function parseOrThrow(dictionary) {
const { version, entries } = dictionary;
if (entries.value["$UUID"] instanceof Value.Bytes === false)
throw new Error();
const $UUID = entries.value["$UUID"];
if (entries.value.S instanceof Value.Bytes === false)
throw new Error();
const S = entries.value.S;
if (entries.value.P instanceof Value.UInt32 === false)
throw new Error();
const P = entries.value.P;
if (entries.value.M instanceof Value.UInt64 === false)
throw new Error();
const M = entries.value.M;
if (entries.value.I instanceof Value.UInt64 === false)
throw new Error();
const I = entries.value.I;
if (entries.value.V instanceof Value.UInt32 === false)
throw new Error();
const V = entries.value.V;
return new KdfParameters.Argon2id(new Dictionary(version, new Entries(entries.bytes, { $UUID, S, P, M, I, V })));
}
Argon2id.parseOrThrow = parseOrThrow;
})(Argon2id = KdfParameters.Argon2id || (KdfParameters.Argon2id = {}));
function readOrThrow(cursor) {
const dictionary = Dictionary.readOrThrow(cursor);
if (dictionary.entries.value["$UUID"] instanceof Value.Bytes === false)
throw new Error();
const $UUID = StringAsUuid.from(dictionary.entries.value["$UUID"].value.bytes);
if (![KdfParameters.AesKdf.$UUID, KdfParameters.Argon2d.$UUID, KdfParameters.Argon2id.$UUID].includes($UUID))
throw new Error();
if ($UUID === KdfParameters.AesKdf.$UUID)
return KdfParameters.AesKdf.parseOrThrow(dictionary);
if ($UUID === KdfParameters.Argon2d.$UUID)
return KdfParameters.Argon2d.parseOrThrow(dictionary);
if ($UUID === KdfParameters.Argon2id.$UUID)
return KdfParameters.Argon2id.parseOrThrow(dictionary);
throw new Error();
}
KdfParameters.readOrThrow = readOrThrow;
})(KdfParameters || (KdfParameters = {}));