full site update
This commit is contained in:
19
node_modules/iron-webcrypto/dist/example.js
generated
vendored
Normal file
19
node_modules/iron-webcrypto/dist/example.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const { webcrypto } = require('node:crypto')
|
||||
const Iron = require('iron-webcrypto')
|
||||
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: [3, 4, 5],
|
||||
d: {
|
||||
e: 'f'
|
||||
}
|
||||
}
|
||||
|
||||
const password = 'a_password_having_at_least_32_chars'
|
||||
|
||||
const sealed = await Iron.seal(webcrypto, obj, password, Iron.defaults)
|
||||
console.log({ sealed })
|
||||
|
||||
const unsealed = await Iron.unseal(webcrypto, sealed, password, Iron.defaults)
|
||||
console.log({ unsealed })
|
315
node_modules/iron-webcrypto/dist/index.cjs
generated
vendored
Normal file
315
node_modules/iron-webcrypto/dist/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
'use strict';
|
||||
|
||||
// src/utils.ts
|
||||
var alphabetByEncoding = {};
|
||||
var alphabetByValue = Array.from({ length: 64 });
|
||||
for (let i = 0, start = "A".charCodeAt(0), limit = "Z".charCodeAt(0); i + start <= limit; i++) {
|
||||
const char = String.fromCharCode(i + start);
|
||||
alphabetByEncoding[char] = i;
|
||||
alphabetByValue[i] = char;
|
||||
}
|
||||
for (let i = 0, start = "a".charCodeAt(0), limit = "z".charCodeAt(0); i + start <= limit; i++) {
|
||||
const char = String.fromCharCode(i + start);
|
||||
const index = i + 26;
|
||||
alphabetByEncoding[char] = index;
|
||||
alphabetByValue[index] = char;
|
||||
}
|
||||
for (let i = 0; i < 10; i++) {
|
||||
alphabetByEncoding[i.toString(10)] = i + 52;
|
||||
const char = i.toString(10);
|
||||
const index = i + 52;
|
||||
alphabetByEncoding[char] = index;
|
||||
alphabetByValue[index] = char;
|
||||
}
|
||||
alphabetByEncoding["-"] = 62;
|
||||
alphabetByValue[62] = "-";
|
||||
alphabetByEncoding["_"] = 63;
|
||||
alphabetByValue[63] = "_";
|
||||
var bitsPerLetter = 6;
|
||||
var bitsPerByte = 8;
|
||||
var maxLetterValue = 63;
|
||||
var stringToBuffer = (value) => {
|
||||
return new TextEncoder().encode(value);
|
||||
};
|
||||
var bufferToString = (value) => {
|
||||
return new TextDecoder().decode(value);
|
||||
};
|
||||
var base64urlDecode = (_input) => {
|
||||
const input = _input + "=".repeat((4 - _input.length % 4) % 4);
|
||||
let totalByteLength = input.length / 4 * 3;
|
||||
if (input.endsWith("==")) {
|
||||
totalByteLength -= 2;
|
||||
} else if (input.endsWith("=")) {
|
||||
totalByteLength--;
|
||||
}
|
||||
const out = new ArrayBuffer(totalByteLength);
|
||||
const dataView = new DataView(out);
|
||||
for (let i = 0; i < input.length; i += 4) {
|
||||
let bits = 0;
|
||||
let bitLength = 0;
|
||||
for (let j = i, limit = i + 3; j <= limit; j++) {
|
||||
if (input[j] === "=") {
|
||||
bits >>= bitsPerLetter;
|
||||
} else {
|
||||
if (!(input[j] in alphabetByEncoding)) {
|
||||
throw new TypeError(`Invalid character ${input[j]} in base64 string.`);
|
||||
}
|
||||
bits |= alphabetByEncoding[input[j]] << (limit - j) * bitsPerLetter;
|
||||
bitLength += bitsPerLetter;
|
||||
}
|
||||
}
|
||||
const chunkOffset = i / 4 * 3;
|
||||
bits >>= bitLength % bitsPerByte;
|
||||
const byteLength = Math.floor(bitLength / bitsPerByte);
|
||||
for (let k = 0; k < byteLength; k++) {
|
||||
const offset = (byteLength - k - 1) * bitsPerByte;
|
||||
dataView.setUint8(chunkOffset + k, (bits & 255 << offset) >> offset);
|
||||
}
|
||||
}
|
||||
return new Uint8Array(out);
|
||||
};
|
||||
var base64urlEncode = (_input) => {
|
||||
const input = typeof _input === "string" ? stringToBuffer(_input) : _input;
|
||||
let str = "";
|
||||
for (let i = 0; i < input.length; i += 3) {
|
||||
let bits = 0;
|
||||
let bitLength = 0;
|
||||
for (let j = i, limit = Math.min(i + 3, input.length); j < limit; j++) {
|
||||
bits |= input[j] << (limit - j - 1) * bitsPerByte;
|
||||
bitLength += bitsPerByte;
|
||||
}
|
||||
const bitClusterCount = Math.ceil(bitLength / bitsPerLetter);
|
||||
bits <<= bitClusterCount * bitsPerLetter - bitLength;
|
||||
for (let k = 1; k <= bitClusterCount; k++) {
|
||||
const offset = (bitClusterCount - k) * bitsPerLetter;
|
||||
str += alphabetByValue[(bits & maxLetterValue << offset) >> offset];
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
// src/index.ts
|
||||
var defaults = {
|
||||
encryption: { saltBits: 256, algorithm: "aes-256-cbc", iterations: 1, minPasswordlength: 32 },
|
||||
integrity: { saltBits: 256, algorithm: "sha256", iterations: 1, minPasswordlength: 32 },
|
||||
ttl: 0,
|
||||
timestampSkewSec: 60,
|
||||
localtimeOffsetMsec: 0
|
||||
};
|
||||
var clone = (options) => ({
|
||||
...options,
|
||||
encryption: { ...options.encryption },
|
||||
integrity: { ...options.integrity }
|
||||
});
|
||||
var algorithms = {
|
||||
"aes-128-ctr": { keyBits: 128, ivBits: 128, name: "AES-CTR" },
|
||||
"aes-256-cbc": { keyBits: 256, ivBits: 128, name: "AES-CBC" },
|
||||
sha256: { keyBits: 256, name: "SHA-256" }
|
||||
};
|
||||
var macFormatVersion = "2";
|
||||
var macPrefix = "Fe26.2";
|
||||
var randomBytes = (_crypto, size) => {
|
||||
const bytes = new Uint8Array(size);
|
||||
_crypto.getRandomValues(bytes);
|
||||
return bytes;
|
||||
};
|
||||
var randomBits = (_crypto, bits) => {
|
||||
if (bits < 1)
|
||||
throw new Error("Invalid random bits count");
|
||||
const bytes = Math.ceil(bits / 8);
|
||||
return randomBytes(_crypto, bytes);
|
||||
};
|
||||
var pbkdf2 = async (_crypto, password, salt, iterations, keyLength, hash) => {
|
||||
const passwordBuffer = stringToBuffer(password);
|
||||
const importedKey = await _crypto.subtle.importKey(
|
||||
"raw",
|
||||
passwordBuffer,
|
||||
{ name: "PBKDF2" },
|
||||
false,
|
||||
["deriveBits"]
|
||||
);
|
||||
const saltBuffer = stringToBuffer(salt);
|
||||
const params = { name: "PBKDF2", hash, salt: saltBuffer, iterations };
|
||||
const derivation = await _crypto.subtle.deriveBits(params, importedKey, keyLength * 8);
|
||||
return derivation;
|
||||
};
|
||||
var generateKey = async (_crypto, password, options) => {
|
||||
var _a;
|
||||
if (!(password == null ? void 0 : password.length))
|
||||
throw new Error("Empty password");
|
||||
if (options == null || typeof options !== "object")
|
||||
throw new Error("Bad options");
|
||||
if (!(options.algorithm in algorithms))
|
||||
throw new Error(`Unknown algorithm: ${options.algorithm}`);
|
||||
const algorithm = algorithms[options.algorithm];
|
||||
const result = {};
|
||||
const hmac = (_a = options.hmac) != null ? _a : false;
|
||||
const id = hmac ? { name: "HMAC", hash: algorithm.name } : { name: algorithm.name };
|
||||
const usage = hmac ? ["sign", "verify"] : ["encrypt", "decrypt"];
|
||||
if (typeof password === "string") {
|
||||
if (password.length < options.minPasswordlength)
|
||||
throw new Error(
|
||||
`Password string too short (min ${options.minPasswordlength} characters required)`
|
||||
);
|
||||
let { salt = "" } = options;
|
||||
if (!salt) {
|
||||
const { saltBits = 0 } = options;
|
||||
if (!saltBits)
|
||||
throw new Error("Missing salt and saltBits options");
|
||||
const randomSalt = randomBits(_crypto, saltBits);
|
||||
salt = [...new Uint8Array(randomSalt)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
const derivedKey = await pbkdf2(
|
||||
_crypto,
|
||||
password,
|
||||
salt,
|
||||
options.iterations,
|
||||
algorithm.keyBits / 8,
|
||||
"SHA-1"
|
||||
);
|
||||
const importedEncryptionKey = await _crypto.subtle.importKey(
|
||||
"raw",
|
||||
derivedKey,
|
||||
id,
|
||||
false,
|
||||
usage
|
||||
);
|
||||
result.key = importedEncryptionKey;
|
||||
result.salt = salt;
|
||||
} else {
|
||||
if (password.length < algorithm.keyBits / 8)
|
||||
throw new Error("Key buffer (password) too small");
|
||||
result.key = await _crypto.subtle.importKey("raw", password, id, false, usage);
|
||||
result.salt = "";
|
||||
}
|
||||
if (options.iv)
|
||||
result.iv = options.iv;
|
||||
else if ("ivBits" in algorithm)
|
||||
result.iv = randomBits(_crypto, algorithm.ivBits);
|
||||
return result;
|
||||
};
|
||||
var getEncryptParams = (algorithm, key, data) => {
|
||||
return [
|
||||
algorithm === "aes-128-ctr" ? { name: "AES-CTR", counter: key.iv, length: 128 } : { name: "AES-CBC", iv: key.iv },
|
||||
key.key,
|
||||
typeof data === "string" ? stringToBuffer(data) : data
|
||||
];
|
||||
};
|
||||
var encrypt = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, options);
|
||||
const encrypted = await _crypto.subtle.encrypt(...getEncryptParams(options.algorithm, key, data));
|
||||
return { encrypted: new Uint8Array(encrypted), key };
|
||||
};
|
||||
var decrypt = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, options);
|
||||
const decrypted = await _crypto.subtle.decrypt(...getEncryptParams(options.algorithm, key, data));
|
||||
return bufferToString(new Uint8Array(decrypted));
|
||||
};
|
||||
var hmacWithPassword = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, { ...options, hmac: true });
|
||||
const textBuffer = stringToBuffer(data);
|
||||
const signed = await _crypto.subtle.sign({ name: "HMAC" }, key.key, textBuffer);
|
||||
const digest = base64urlEncode(new Uint8Array(signed));
|
||||
return { digest, salt: key.salt };
|
||||
};
|
||||
var normalizePassword = (password) => {
|
||||
if (typeof password === "string" || password instanceof Uint8Array)
|
||||
return { encryption: password, integrity: password };
|
||||
if ("secret" in password)
|
||||
return { id: password.id, encryption: password.secret, integrity: password.secret };
|
||||
return { id: password.id, encryption: password.encryption, integrity: password.integrity };
|
||||
};
|
||||
var seal = async (_crypto, object, password, options) => {
|
||||
if (!password)
|
||||
throw new Error("Empty password");
|
||||
const opts = clone(options);
|
||||
const now = Date.now() + (opts.localtimeOffsetMsec || 0);
|
||||
const objectString = JSON.stringify(object);
|
||||
const pass = normalizePassword(password);
|
||||
const { id = "", encryption, integrity } = pass;
|
||||
if (id && !/^\w+$/.test(id))
|
||||
throw new Error("Invalid password id");
|
||||
const { encrypted, key } = await encrypt(_crypto, encryption, opts.encryption, objectString);
|
||||
const encryptedB64 = base64urlEncode(new Uint8Array(encrypted));
|
||||
const iv = base64urlEncode(key.iv);
|
||||
const expiration = opts.ttl ? now + opts.ttl : "";
|
||||
const macBaseString = `${macPrefix}*${id}*${key.salt}*${iv}*${encryptedB64}*${expiration}`;
|
||||
const mac = await hmacWithPassword(_crypto, integrity, opts.integrity, macBaseString);
|
||||
const sealed = `${macBaseString}*${mac.salt}*${mac.digest}`;
|
||||
return sealed;
|
||||
};
|
||||
var fixedTimeComparison = (a, b) => {
|
||||
let mismatch = a.length === b.length ? 0 : 1;
|
||||
if (mismatch)
|
||||
b = a;
|
||||
for (let i = 0; i < a.length; i += 1)
|
||||
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
||||
return mismatch === 0;
|
||||
};
|
||||
var unseal = async (_crypto, sealed, password, options) => {
|
||||
if (!password)
|
||||
throw new Error("Empty password");
|
||||
const opts = clone(options);
|
||||
const now = Date.now() + (opts.localtimeOffsetMsec || 0);
|
||||
const parts = sealed.split("*");
|
||||
if (parts.length !== 8)
|
||||
throw new Error("Incorrect number of sealed components");
|
||||
const prefix = parts[0];
|
||||
let passwordId = parts[1];
|
||||
const encryptionSalt = parts[2];
|
||||
const encryptionIv = parts[3];
|
||||
const encryptedB64 = parts[4];
|
||||
const expiration = parts[5];
|
||||
const hmacSalt = parts[6];
|
||||
const hmac = parts[7];
|
||||
const macBaseString = `${prefix}*${passwordId}*${encryptionSalt}*${encryptionIv}*${encryptedB64}*${expiration}`;
|
||||
if (macPrefix !== prefix)
|
||||
throw new Error("Wrong mac prefix");
|
||||
if (expiration) {
|
||||
if (!/^\d+$/.test(expiration))
|
||||
throw new Error("Invalid expiration");
|
||||
const exp = Number.parseInt(expiration, 10);
|
||||
if (exp <= now - opts.timestampSkewSec * 1e3)
|
||||
throw new Error("Expired seal");
|
||||
}
|
||||
let pass = "";
|
||||
passwordId = passwordId || "default";
|
||||
if (typeof password === "string" || password instanceof Uint8Array)
|
||||
pass = password;
|
||||
else if (passwordId in password) {
|
||||
pass = password[passwordId];
|
||||
} else {
|
||||
throw new Error(`Cannot find password: ${passwordId}`);
|
||||
}
|
||||
pass = normalizePassword(pass);
|
||||
const macOptions = opts.integrity;
|
||||
macOptions.salt = hmacSalt;
|
||||
const mac = await hmacWithPassword(_crypto, pass.integrity, macOptions, macBaseString);
|
||||
if (!fixedTimeComparison(mac.digest, hmac))
|
||||
throw new Error("Bad hmac value");
|
||||
const encrypted = base64urlDecode(encryptedB64);
|
||||
const decryptOptions = opts.encryption;
|
||||
decryptOptions.salt = encryptionSalt;
|
||||
decryptOptions.iv = base64urlDecode(encryptionIv);
|
||||
const decrypted = await decrypt(_crypto, pass.encryption, decryptOptions, encrypted);
|
||||
if (decrypted)
|
||||
return JSON.parse(decrypted);
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.algorithms = algorithms;
|
||||
exports.base64urlDecode = base64urlDecode;
|
||||
exports.base64urlEncode = base64urlEncode;
|
||||
exports.bufferToString = bufferToString;
|
||||
exports.clone = clone;
|
||||
exports.decrypt = decrypt;
|
||||
exports.defaults = defaults;
|
||||
exports.encrypt = encrypt;
|
||||
exports.generateKey = generateKey;
|
||||
exports.hmacWithPassword = hmacWithPassword;
|
||||
exports.macFormatVersion = macFormatVersion;
|
||||
exports.macPrefix = macPrefix;
|
||||
exports.randomBits = randomBits;
|
||||
exports.seal = seal;
|
||||
exports.stringToBuffer = stringToBuffer;
|
||||
exports.unseal = unseal;
|
255
node_modules/iron-webcrypto/dist/index.d.cts
generated
vendored
Normal file
255
node_modules/iron-webcrypto/dist/index.d.cts
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
interface _Crypto {
|
||||
readonly subtle: _SubtleCrypto;
|
||||
getRandomValues: (array: Uint8Array) => Uint8Array;
|
||||
}
|
||||
interface _SubtleCrypto {
|
||||
decrypt: (algorithm: AesCbcParams | AesCtrParams | AesGcmParams | AlgorithmIdentifier | RsaOaepParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
deriveBits: (algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, length: number) => Promise<ArrayBuffer>;
|
||||
encrypt: (algorithm: AesCbcParams | AesCtrParams | AesGcmParams | AlgorithmIdentifier | RsaOaepParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
importKey: (format: Exclude<KeyFormat, 'jwk'>, keyData: ArrayBuffer | Uint8Array, algorithm: AesKeyAlgorithm | AlgorithmIdentifier | EcKeyImportParams | HmacImportParams | RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) => Promise<CryptoKey>;
|
||||
sign: (algorithm: AlgorithmIdentifier | EcdsaParams | RsaPssParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm used for encryption and decryption.
|
||||
*/
|
||||
type EncryptionAlgorithm = 'aes-128-ctr' | 'aes-256-cbc';
|
||||
/**
|
||||
* Algorithm used for integrity verification.
|
||||
*/
|
||||
type IntegrityAlgorithm = 'sha256';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type _Algorithm = EncryptionAlgorithm | IntegrityAlgorithm;
|
||||
/**
|
||||
* seal() method options.
|
||||
*/
|
||||
interface SealOptionsSub<Algorithm extends _Algorithm = _Algorithm> {
|
||||
/**
|
||||
* The length of the salt (random buffer used to ensure that two identical objects will generate a different encrypted result). Defaults to 256.
|
||||
*/
|
||||
saltBits: number;
|
||||
/**
|
||||
* The algorithm used. Defaults to 'aes-256-cbc' for encryption and 'sha256' for integrity.
|
||||
*/
|
||||
algorithm: Algorithm;
|
||||
/**
|
||||
* The number of iterations used to derive a key from the password. Defaults to 1.
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Minimum password size. Defaults to 32.
|
||||
*/
|
||||
minPasswordlength: number;
|
||||
}
|
||||
/**
|
||||
* Options for customizing the key derivation algorithm used to generate encryption and integrity verification keys as well as the algorithms and salt sizes used.
|
||||
*/
|
||||
interface SealOptions {
|
||||
/**
|
||||
* Encryption step options.
|
||||
*/
|
||||
encryption: SealOptionsSub<EncryptionAlgorithm>;
|
||||
/**
|
||||
* Integrity step options.
|
||||
*/
|
||||
integrity: SealOptionsSub<IntegrityAlgorithm>;
|
||||
/**
|
||||
* Sealed object lifetime in milliseconds where 0 means forever. Defaults to 0.
|
||||
*/
|
||||
ttl: number;
|
||||
/**
|
||||
* Number of seconds of permitted clock skew for incoming expirations. Defaults to 60 seconds.
|
||||
*/
|
||||
timestampSkewSec: number;
|
||||
/**
|
||||
* Local clock time offset, expressed in number of milliseconds (positive or negative). Defaults to 0.
|
||||
*/
|
||||
localtimeOffsetMsec: number;
|
||||
}
|
||||
/**
|
||||
* Password secret string or buffer.
|
||||
*/
|
||||
type Password = Uint8Array | string;
|
||||
/**
|
||||
* generateKey() method options.
|
||||
*/
|
||||
type GenerateKeyOptions<Algorithm extends _Algorithm = _Algorithm> = Pick<SealOptionsSub<Algorithm>, 'algorithm' | 'iterations' | 'minPasswordlength'> & {
|
||||
saltBits?: number | undefined;
|
||||
salt?: string | undefined;
|
||||
iv?: Uint8Array | undefined;
|
||||
hmac?: boolean | undefined;
|
||||
};
|
||||
/**
|
||||
* Generated internal key object.
|
||||
*/
|
||||
interface Key {
|
||||
key: CryptoKey;
|
||||
salt: string;
|
||||
iv: Uint8Array;
|
||||
}
|
||||
/**
|
||||
* Generated HMAC internal results.
|
||||
*/
|
||||
interface HMacResult {
|
||||
digest: string;
|
||||
salt: string;
|
||||
}
|
||||
declare namespace password {
|
||||
/**
|
||||
* Secret object with optional id.
|
||||
*/
|
||||
interface Secret {
|
||||
id?: string | undefined;
|
||||
secret: Password;
|
||||
}
|
||||
/**
|
||||
* Secret object with optional id and specified password for each encryption and integrity.
|
||||
*/
|
||||
interface Specific {
|
||||
id?: string | undefined;
|
||||
encryption: Password;
|
||||
integrity: Password;
|
||||
}
|
||||
/**
|
||||
* Key-value pairs hash of password id to value.
|
||||
*/
|
||||
type Hash = Record<string, Password | Secret | Specific>;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type RawPassword = Password | password.Secret | password.Specific;
|
||||
|
||||
/**
|
||||
* Convert a string to a Uint8Array.
|
||||
* @param value The string to convert
|
||||
* @returns The Uint8Array
|
||||
*/
|
||||
declare const stringToBuffer: (value: string) => Uint8Array;
|
||||
/**
|
||||
* Convert a Uint8Array to a string.
|
||||
* @param value The Uint8Array to convert
|
||||
* @returns The string
|
||||
*/
|
||||
declare const bufferToString: (value: Uint8Array) => string;
|
||||
/**
|
||||
* Decode a base64url string to a Uint8Array.
|
||||
* @param _input The base64url string to decode (automatically padded as necessary)
|
||||
* @returns The Uint8Array
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4648#section-5
|
||||
*/
|
||||
declare const base64urlDecode: (_input: string) => Uint8Array;
|
||||
/**
|
||||
* Encode a Uint8Array to a base64url string.
|
||||
* @param _input The Uint8Array to encode
|
||||
* @returns The base64url string (without padding)
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4648#section-5
|
||||
*/
|
||||
declare const base64urlEncode: (_input: Uint8Array | string) => string;
|
||||
|
||||
/**
|
||||
* The default encryption and integrity settings.
|
||||
*/
|
||||
declare const defaults: SealOptions;
|
||||
/**
|
||||
* Clones the options object.
|
||||
* @param options The options object to clone
|
||||
* @returns A new options object
|
||||
*/
|
||||
declare const clone: (options: SealOptions) => SealOptions;
|
||||
/**
|
||||
* Configuration of each supported algorithm.
|
||||
*/
|
||||
declare const algorithms: {
|
||||
readonly 'aes-128-ctr': {
|
||||
readonly keyBits: 128;
|
||||
readonly ivBits: 128;
|
||||
readonly name: "AES-CTR";
|
||||
};
|
||||
readonly 'aes-256-cbc': {
|
||||
readonly keyBits: 256;
|
||||
readonly ivBits: 128;
|
||||
readonly name: "AES-CBC";
|
||||
};
|
||||
readonly sha256: {
|
||||
readonly keyBits: 256;
|
||||
readonly name: "SHA-256";
|
||||
};
|
||||
};
|
||||
/**
|
||||
* MAC normalization format version.
|
||||
*/
|
||||
declare const macFormatVersion = "2";
|
||||
/**
|
||||
* MAC normalization prefix.
|
||||
*/
|
||||
declare const macPrefix = "Fe26.2";
|
||||
/**
|
||||
* Generate cryptographically strong pseudorandom bits.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param bits Number of bits to generate
|
||||
* @returns Buffer
|
||||
*/
|
||||
declare const randomBits: (_crypto: _Crypto, bits: number) => Uint8Array;
|
||||
/**
|
||||
* Generates a key from the password.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns An object with keys: key, salt, iv
|
||||
*/
|
||||
declare const generateKey: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions) => Promise<Key>;
|
||||
/**
|
||||
* Encrypts data.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data String to encrypt
|
||||
* @returns An object with keys: encrypted, key
|
||||
*/
|
||||
declare const encrypt: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<EncryptionAlgorithm>, data: string) => Promise<{
|
||||
encrypted: Uint8Array;
|
||||
key: Key;
|
||||
}>;
|
||||
/**
|
||||
* Decrypts data.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data Buffer to decrypt
|
||||
* @returns Decrypted string
|
||||
*/
|
||||
declare const decrypt: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<EncryptionAlgorithm>, data: Uint8Array | string) => Promise<string>;
|
||||
/**
|
||||
* Calculates a HMAC digest.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data String to calculate the HMAC over
|
||||
* @returns An object with keys: digest, salt
|
||||
*/
|
||||
declare const hmacWithPassword: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<IntegrityAlgorithm>, data: string) => Promise<HMacResult>;
|
||||
/**
|
||||
* Serializes, encrypts, and signs objects into an iron protocol string.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param object Data being sealed
|
||||
* @param password A string, buffer or object
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns Iron sealed string
|
||||
*/
|
||||
declare const seal: (_crypto: _Crypto, object: unknown, password: RawPassword, options: SealOptions) => Promise<string>;
|
||||
/**
|
||||
* Verifies, decrypts, and reconstruct an iron protocol string into an object.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param sealed The iron protocol string generated with seal()
|
||||
* @param password A string, buffer, or object
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns The verified decrypted object (can be null)
|
||||
*/
|
||||
declare const unseal: (_crypto: _Crypto, sealed: string, password: Password | password.Hash, options: SealOptions) => Promise<unknown>;
|
||||
|
||||
export { type EncryptionAlgorithm, type GenerateKeyOptions, type HMacResult, type IntegrityAlgorithm, type Key, type Password, type RawPassword, type SealOptions, type SealOptionsSub, type _Algorithm, algorithms, base64urlDecode, base64urlEncode, bufferToString, clone, decrypt, defaults, encrypt, generateKey, hmacWithPassword, macFormatVersion, macPrefix, password, randomBits, seal, stringToBuffer, unseal };
|
255
node_modules/iron-webcrypto/dist/index.d.ts
generated
vendored
Normal file
255
node_modules/iron-webcrypto/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
interface _Crypto {
|
||||
readonly subtle: _SubtleCrypto;
|
||||
getRandomValues: (array: Uint8Array) => Uint8Array;
|
||||
}
|
||||
interface _SubtleCrypto {
|
||||
decrypt: (algorithm: AesCbcParams | AesCtrParams | AesGcmParams | AlgorithmIdentifier | RsaOaepParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
deriveBits: (algorithm: AlgorithmIdentifier | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params, baseKey: CryptoKey, length: number) => Promise<ArrayBuffer>;
|
||||
encrypt: (algorithm: AesCbcParams | AesCtrParams | AesGcmParams | AlgorithmIdentifier | RsaOaepParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
importKey: (format: Exclude<KeyFormat, 'jwk'>, keyData: ArrayBuffer | Uint8Array, algorithm: AesKeyAlgorithm | AlgorithmIdentifier | EcKeyImportParams | HmacImportParams | RsaHashedImportParams, extractable: boolean, keyUsages: KeyUsage[]) => Promise<CryptoKey>;
|
||||
sign: (algorithm: AlgorithmIdentifier | EcdsaParams | RsaPssParams, key: CryptoKey, data: Uint8Array) => Promise<ArrayBuffer>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm used for encryption and decryption.
|
||||
*/
|
||||
type EncryptionAlgorithm = 'aes-128-ctr' | 'aes-256-cbc';
|
||||
/**
|
||||
* Algorithm used for integrity verification.
|
||||
*/
|
||||
type IntegrityAlgorithm = 'sha256';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type _Algorithm = EncryptionAlgorithm | IntegrityAlgorithm;
|
||||
/**
|
||||
* seal() method options.
|
||||
*/
|
||||
interface SealOptionsSub<Algorithm extends _Algorithm = _Algorithm> {
|
||||
/**
|
||||
* The length of the salt (random buffer used to ensure that two identical objects will generate a different encrypted result). Defaults to 256.
|
||||
*/
|
||||
saltBits: number;
|
||||
/**
|
||||
* The algorithm used. Defaults to 'aes-256-cbc' for encryption and 'sha256' for integrity.
|
||||
*/
|
||||
algorithm: Algorithm;
|
||||
/**
|
||||
* The number of iterations used to derive a key from the password. Defaults to 1.
|
||||
*/
|
||||
iterations: number;
|
||||
/**
|
||||
* Minimum password size. Defaults to 32.
|
||||
*/
|
||||
minPasswordlength: number;
|
||||
}
|
||||
/**
|
||||
* Options for customizing the key derivation algorithm used to generate encryption and integrity verification keys as well as the algorithms and salt sizes used.
|
||||
*/
|
||||
interface SealOptions {
|
||||
/**
|
||||
* Encryption step options.
|
||||
*/
|
||||
encryption: SealOptionsSub<EncryptionAlgorithm>;
|
||||
/**
|
||||
* Integrity step options.
|
||||
*/
|
||||
integrity: SealOptionsSub<IntegrityAlgorithm>;
|
||||
/**
|
||||
* Sealed object lifetime in milliseconds where 0 means forever. Defaults to 0.
|
||||
*/
|
||||
ttl: number;
|
||||
/**
|
||||
* Number of seconds of permitted clock skew for incoming expirations. Defaults to 60 seconds.
|
||||
*/
|
||||
timestampSkewSec: number;
|
||||
/**
|
||||
* Local clock time offset, expressed in number of milliseconds (positive or negative). Defaults to 0.
|
||||
*/
|
||||
localtimeOffsetMsec: number;
|
||||
}
|
||||
/**
|
||||
* Password secret string or buffer.
|
||||
*/
|
||||
type Password = Uint8Array | string;
|
||||
/**
|
||||
* generateKey() method options.
|
||||
*/
|
||||
type GenerateKeyOptions<Algorithm extends _Algorithm = _Algorithm> = Pick<SealOptionsSub<Algorithm>, 'algorithm' | 'iterations' | 'minPasswordlength'> & {
|
||||
saltBits?: number | undefined;
|
||||
salt?: string | undefined;
|
||||
iv?: Uint8Array | undefined;
|
||||
hmac?: boolean | undefined;
|
||||
};
|
||||
/**
|
||||
* Generated internal key object.
|
||||
*/
|
||||
interface Key {
|
||||
key: CryptoKey;
|
||||
salt: string;
|
||||
iv: Uint8Array;
|
||||
}
|
||||
/**
|
||||
* Generated HMAC internal results.
|
||||
*/
|
||||
interface HMacResult {
|
||||
digest: string;
|
||||
salt: string;
|
||||
}
|
||||
declare namespace password {
|
||||
/**
|
||||
* Secret object with optional id.
|
||||
*/
|
||||
interface Secret {
|
||||
id?: string | undefined;
|
||||
secret: Password;
|
||||
}
|
||||
/**
|
||||
* Secret object with optional id and specified password for each encryption and integrity.
|
||||
*/
|
||||
interface Specific {
|
||||
id?: string | undefined;
|
||||
encryption: Password;
|
||||
integrity: Password;
|
||||
}
|
||||
/**
|
||||
* Key-value pairs hash of password id to value.
|
||||
*/
|
||||
type Hash = Record<string, Password | Secret | Specific>;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type RawPassword = Password | password.Secret | password.Specific;
|
||||
|
||||
/**
|
||||
* Convert a string to a Uint8Array.
|
||||
* @param value The string to convert
|
||||
* @returns The Uint8Array
|
||||
*/
|
||||
declare const stringToBuffer: (value: string) => Uint8Array;
|
||||
/**
|
||||
* Convert a Uint8Array to a string.
|
||||
* @param value The Uint8Array to convert
|
||||
* @returns The string
|
||||
*/
|
||||
declare const bufferToString: (value: Uint8Array) => string;
|
||||
/**
|
||||
* Decode a base64url string to a Uint8Array.
|
||||
* @param _input The base64url string to decode (automatically padded as necessary)
|
||||
* @returns The Uint8Array
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4648#section-5
|
||||
*/
|
||||
declare const base64urlDecode: (_input: string) => Uint8Array;
|
||||
/**
|
||||
* Encode a Uint8Array to a base64url string.
|
||||
* @param _input The Uint8Array to encode
|
||||
* @returns The base64url string (without padding)
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4648#section-5
|
||||
*/
|
||||
declare const base64urlEncode: (_input: Uint8Array | string) => string;
|
||||
|
||||
/**
|
||||
* The default encryption and integrity settings.
|
||||
*/
|
||||
declare const defaults: SealOptions;
|
||||
/**
|
||||
* Clones the options object.
|
||||
* @param options The options object to clone
|
||||
* @returns A new options object
|
||||
*/
|
||||
declare const clone: (options: SealOptions) => SealOptions;
|
||||
/**
|
||||
* Configuration of each supported algorithm.
|
||||
*/
|
||||
declare const algorithms: {
|
||||
readonly 'aes-128-ctr': {
|
||||
readonly keyBits: 128;
|
||||
readonly ivBits: 128;
|
||||
readonly name: "AES-CTR";
|
||||
};
|
||||
readonly 'aes-256-cbc': {
|
||||
readonly keyBits: 256;
|
||||
readonly ivBits: 128;
|
||||
readonly name: "AES-CBC";
|
||||
};
|
||||
readonly sha256: {
|
||||
readonly keyBits: 256;
|
||||
readonly name: "SHA-256";
|
||||
};
|
||||
};
|
||||
/**
|
||||
* MAC normalization format version.
|
||||
*/
|
||||
declare const macFormatVersion = "2";
|
||||
/**
|
||||
* MAC normalization prefix.
|
||||
*/
|
||||
declare const macPrefix = "Fe26.2";
|
||||
/**
|
||||
* Generate cryptographically strong pseudorandom bits.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param bits Number of bits to generate
|
||||
* @returns Buffer
|
||||
*/
|
||||
declare const randomBits: (_crypto: _Crypto, bits: number) => Uint8Array;
|
||||
/**
|
||||
* Generates a key from the password.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns An object with keys: key, salt, iv
|
||||
*/
|
||||
declare const generateKey: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions) => Promise<Key>;
|
||||
/**
|
||||
* Encrypts data.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data String to encrypt
|
||||
* @returns An object with keys: encrypted, key
|
||||
*/
|
||||
declare const encrypt: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<EncryptionAlgorithm>, data: string) => Promise<{
|
||||
encrypted: Uint8Array;
|
||||
key: Key;
|
||||
}>;
|
||||
/**
|
||||
* Decrypts data.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer key
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data Buffer to decrypt
|
||||
* @returns Decrypted string
|
||||
*/
|
||||
declare const decrypt: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<EncryptionAlgorithm>, data: Uint8Array | string) => Promise<string>;
|
||||
/**
|
||||
* Calculates a HMAC digest.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param password A password string or buffer
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @param data String to calculate the HMAC over
|
||||
* @returns An object with keys: digest, salt
|
||||
*/
|
||||
declare const hmacWithPassword: (_crypto: _Crypto, password: Password, options: GenerateKeyOptions<IntegrityAlgorithm>, data: string) => Promise<HMacResult>;
|
||||
/**
|
||||
* Serializes, encrypts, and signs objects into an iron protocol string.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param object Data being sealed
|
||||
* @param password A string, buffer or object
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns Iron sealed string
|
||||
*/
|
||||
declare const seal: (_crypto: _Crypto, object: unknown, password: RawPassword, options: SealOptions) => Promise<string>;
|
||||
/**
|
||||
* Verifies, decrypts, and reconstruct an iron protocol string into an object.
|
||||
* @param _crypto Custom WebCrypto implementation
|
||||
* @param sealed The iron protocol string generated with seal()
|
||||
* @param password A string, buffer, or object
|
||||
* @param options Object used to customize the key derivation algorithm
|
||||
* @returns The verified decrypted object (can be null)
|
||||
*/
|
||||
declare const unseal: (_crypto: _Crypto, sealed: string, password: Password | password.Hash, options: SealOptions) => Promise<unknown>;
|
||||
|
||||
export { type EncryptionAlgorithm, type GenerateKeyOptions, type HMacResult, type IntegrityAlgorithm, type Key, type Password, type RawPassword, type SealOptions, type SealOptionsSub, type _Algorithm, algorithms, base64urlDecode, base64urlEncode, bufferToString, clone, decrypt, defaults, encrypt, generateKey, hmacWithPassword, macFormatVersion, macPrefix, password, randomBits, seal, stringToBuffer, unseal };
|
298
node_modules/iron-webcrypto/dist/index.js
generated
vendored
Normal file
298
node_modules/iron-webcrypto/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
// src/utils.ts
|
||||
var alphabetByEncoding = {};
|
||||
var alphabetByValue = Array.from({ length: 64 });
|
||||
for (let i = 0, start = "A".charCodeAt(0), limit = "Z".charCodeAt(0); i + start <= limit; i++) {
|
||||
const char = String.fromCharCode(i + start);
|
||||
alphabetByEncoding[char] = i;
|
||||
alphabetByValue[i] = char;
|
||||
}
|
||||
for (let i = 0, start = "a".charCodeAt(0), limit = "z".charCodeAt(0); i + start <= limit; i++) {
|
||||
const char = String.fromCharCode(i + start);
|
||||
const index = i + 26;
|
||||
alphabetByEncoding[char] = index;
|
||||
alphabetByValue[index] = char;
|
||||
}
|
||||
for (let i = 0; i < 10; i++) {
|
||||
alphabetByEncoding[i.toString(10)] = i + 52;
|
||||
const char = i.toString(10);
|
||||
const index = i + 52;
|
||||
alphabetByEncoding[char] = index;
|
||||
alphabetByValue[index] = char;
|
||||
}
|
||||
alphabetByEncoding["-"] = 62;
|
||||
alphabetByValue[62] = "-";
|
||||
alphabetByEncoding["_"] = 63;
|
||||
alphabetByValue[63] = "_";
|
||||
var bitsPerLetter = 6;
|
||||
var bitsPerByte = 8;
|
||||
var maxLetterValue = 63;
|
||||
var stringToBuffer = (value) => {
|
||||
return new TextEncoder().encode(value);
|
||||
};
|
||||
var bufferToString = (value) => {
|
||||
return new TextDecoder().decode(value);
|
||||
};
|
||||
var base64urlDecode = (_input) => {
|
||||
const input = _input + "=".repeat((4 - _input.length % 4) % 4);
|
||||
let totalByteLength = input.length / 4 * 3;
|
||||
if (input.endsWith("==")) {
|
||||
totalByteLength -= 2;
|
||||
} else if (input.endsWith("=")) {
|
||||
totalByteLength--;
|
||||
}
|
||||
const out = new ArrayBuffer(totalByteLength);
|
||||
const dataView = new DataView(out);
|
||||
for (let i = 0; i < input.length; i += 4) {
|
||||
let bits = 0;
|
||||
let bitLength = 0;
|
||||
for (let j = i, limit = i + 3; j <= limit; j++) {
|
||||
if (input[j] === "=") {
|
||||
bits >>= bitsPerLetter;
|
||||
} else {
|
||||
if (!(input[j] in alphabetByEncoding)) {
|
||||
throw new TypeError(`Invalid character ${input[j]} in base64 string.`);
|
||||
}
|
||||
bits |= alphabetByEncoding[input[j]] << (limit - j) * bitsPerLetter;
|
||||
bitLength += bitsPerLetter;
|
||||
}
|
||||
}
|
||||
const chunkOffset = i / 4 * 3;
|
||||
bits >>= bitLength % bitsPerByte;
|
||||
const byteLength = Math.floor(bitLength / bitsPerByte);
|
||||
for (let k = 0; k < byteLength; k++) {
|
||||
const offset = (byteLength - k - 1) * bitsPerByte;
|
||||
dataView.setUint8(chunkOffset + k, (bits & 255 << offset) >> offset);
|
||||
}
|
||||
}
|
||||
return new Uint8Array(out);
|
||||
};
|
||||
var base64urlEncode = (_input) => {
|
||||
const input = typeof _input === "string" ? stringToBuffer(_input) : _input;
|
||||
let str = "";
|
||||
for (let i = 0; i < input.length; i += 3) {
|
||||
let bits = 0;
|
||||
let bitLength = 0;
|
||||
for (let j = i, limit = Math.min(i + 3, input.length); j < limit; j++) {
|
||||
bits |= input[j] << (limit - j - 1) * bitsPerByte;
|
||||
bitLength += bitsPerByte;
|
||||
}
|
||||
const bitClusterCount = Math.ceil(bitLength / bitsPerLetter);
|
||||
bits <<= bitClusterCount * bitsPerLetter - bitLength;
|
||||
for (let k = 1; k <= bitClusterCount; k++) {
|
||||
const offset = (bitClusterCount - k) * bitsPerLetter;
|
||||
str += alphabetByValue[(bits & maxLetterValue << offset) >> offset];
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
// src/index.ts
|
||||
var defaults = {
|
||||
encryption: { saltBits: 256, algorithm: "aes-256-cbc", iterations: 1, minPasswordlength: 32 },
|
||||
integrity: { saltBits: 256, algorithm: "sha256", iterations: 1, minPasswordlength: 32 },
|
||||
ttl: 0,
|
||||
timestampSkewSec: 60,
|
||||
localtimeOffsetMsec: 0
|
||||
};
|
||||
var clone = (options) => ({
|
||||
...options,
|
||||
encryption: { ...options.encryption },
|
||||
integrity: { ...options.integrity }
|
||||
});
|
||||
var algorithms = {
|
||||
"aes-128-ctr": { keyBits: 128, ivBits: 128, name: "AES-CTR" },
|
||||
"aes-256-cbc": { keyBits: 256, ivBits: 128, name: "AES-CBC" },
|
||||
sha256: { keyBits: 256, name: "SHA-256" }
|
||||
};
|
||||
var macFormatVersion = "2";
|
||||
var macPrefix = "Fe26.2";
|
||||
var randomBytes = (_crypto, size) => {
|
||||
const bytes = new Uint8Array(size);
|
||||
_crypto.getRandomValues(bytes);
|
||||
return bytes;
|
||||
};
|
||||
var randomBits = (_crypto, bits) => {
|
||||
if (bits < 1)
|
||||
throw new Error("Invalid random bits count");
|
||||
const bytes = Math.ceil(bits / 8);
|
||||
return randomBytes(_crypto, bytes);
|
||||
};
|
||||
var pbkdf2 = async (_crypto, password, salt, iterations, keyLength, hash) => {
|
||||
const passwordBuffer = stringToBuffer(password);
|
||||
const importedKey = await _crypto.subtle.importKey(
|
||||
"raw",
|
||||
passwordBuffer,
|
||||
{ name: "PBKDF2" },
|
||||
false,
|
||||
["deriveBits"]
|
||||
);
|
||||
const saltBuffer = stringToBuffer(salt);
|
||||
const params = { name: "PBKDF2", hash, salt: saltBuffer, iterations };
|
||||
const derivation = await _crypto.subtle.deriveBits(params, importedKey, keyLength * 8);
|
||||
return derivation;
|
||||
};
|
||||
var generateKey = async (_crypto, password, options) => {
|
||||
var _a;
|
||||
if (!(password == null ? void 0 : password.length))
|
||||
throw new Error("Empty password");
|
||||
if (options == null || typeof options !== "object")
|
||||
throw new Error("Bad options");
|
||||
if (!(options.algorithm in algorithms))
|
||||
throw new Error(`Unknown algorithm: ${options.algorithm}`);
|
||||
const algorithm = algorithms[options.algorithm];
|
||||
const result = {};
|
||||
const hmac = (_a = options.hmac) != null ? _a : false;
|
||||
const id = hmac ? { name: "HMAC", hash: algorithm.name } : { name: algorithm.name };
|
||||
const usage = hmac ? ["sign", "verify"] : ["encrypt", "decrypt"];
|
||||
if (typeof password === "string") {
|
||||
if (password.length < options.minPasswordlength)
|
||||
throw new Error(
|
||||
`Password string too short (min ${options.minPasswordlength} characters required)`
|
||||
);
|
||||
let { salt = "" } = options;
|
||||
if (!salt) {
|
||||
const { saltBits = 0 } = options;
|
||||
if (!saltBits)
|
||||
throw new Error("Missing salt and saltBits options");
|
||||
const randomSalt = randomBits(_crypto, saltBits);
|
||||
salt = [...new Uint8Array(randomSalt)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
const derivedKey = await pbkdf2(
|
||||
_crypto,
|
||||
password,
|
||||
salt,
|
||||
options.iterations,
|
||||
algorithm.keyBits / 8,
|
||||
"SHA-1"
|
||||
);
|
||||
const importedEncryptionKey = await _crypto.subtle.importKey(
|
||||
"raw",
|
||||
derivedKey,
|
||||
id,
|
||||
false,
|
||||
usage
|
||||
);
|
||||
result.key = importedEncryptionKey;
|
||||
result.salt = salt;
|
||||
} else {
|
||||
if (password.length < algorithm.keyBits / 8)
|
||||
throw new Error("Key buffer (password) too small");
|
||||
result.key = await _crypto.subtle.importKey("raw", password, id, false, usage);
|
||||
result.salt = "";
|
||||
}
|
||||
if (options.iv)
|
||||
result.iv = options.iv;
|
||||
else if ("ivBits" in algorithm)
|
||||
result.iv = randomBits(_crypto, algorithm.ivBits);
|
||||
return result;
|
||||
};
|
||||
var getEncryptParams = (algorithm, key, data) => {
|
||||
return [
|
||||
algorithm === "aes-128-ctr" ? { name: "AES-CTR", counter: key.iv, length: 128 } : { name: "AES-CBC", iv: key.iv },
|
||||
key.key,
|
||||
typeof data === "string" ? stringToBuffer(data) : data
|
||||
];
|
||||
};
|
||||
var encrypt = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, options);
|
||||
const encrypted = await _crypto.subtle.encrypt(...getEncryptParams(options.algorithm, key, data));
|
||||
return { encrypted: new Uint8Array(encrypted), key };
|
||||
};
|
||||
var decrypt = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, options);
|
||||
const decrypted = await _crypto.subtle.decrypt(...getEncryptParams(options.algorithm, key, data));
|
||||
return bufferToString(new Uint8Array(decrypted));
|
||||
};
|
||||
var hmacWithPassword = async (_crypto, password, options, data) => {
|
||||
const key = await generateKey(_crypto, password, { ...options, hmac: true });
|
||||
const textBuffer = stringToBuffer(data);
|
||||
const signed = await _crypto.subtle.sign({ name: "HMAC" }, key.key, textBuffer);
|
||||
const digest = base64urlEncode(new Uint8Array(signed));
|
||||
return { digest, salt: key.salt };
|
||||
};
|
||||
var normalizePassword = (password) => {
|
||||
if (typeof password === "string" || password instanceof Uint8Array)
|
||||
return { encryption: password, integrity: password };
|
||||
if ("secret" in password)
|
||||
return { id: password.id, encryption: password.secret, integrity: password.secret };
|
||||
return { id: password.id, encryption: password.encryption, integrity: password.integrity };
|
||||
};
|
||||
var seal = async (_crypto, object, password, options) => {
|
||||
if (!password)
|
||||
throw new Error("Empty password");
|
||||
const opts = clone(options);
|
||||
const now = Date.now() + (opts.localtimeOffsetMsec || 0);
|
||||
const objectString = JSON.stringify(object);
|
||||
const pass = normalizePassword(password);
|
||||
const { id = "", encryption, integrity } = pass;
|
||||
if (id && !/^\w+$/.test(id))
|
||||
throw new Error("Invalid password id");
|
||||
const { encrypted, key } = await encrypt(_crypto, encryption, opts.encryption, objectString);
|
||||
const encryptedB64 = base64urlEncode(new Uint8Array(encrypted));
|
||||
const iv = base64urlEncode(key.iv);
|
||||
const expiration = opts.ttl ? now + opts.ttl : "";
|
||||
const macBaseString = `${macPrefix}*${id}*${key.salt}*${iv}*${encryptedB64}*${expiration}`;
|
||||
const mac = await hmacWithPassword(_crypto, integrity, opts.integrity, macBaseString);
|
||||
const sealed = `${macBaseString}*${mac.salt}*${mac.digest}`;
|
||||
return sealed;
|
||||
};
|
||||
var fixedTimeComparison = (a, b) => {
|
||||
let mismatch = a.length === b.length ? 0 : 1;
|
||||
if (mismatch)
|
||||
b = a;
|
||||
for (let i = 0; i < a.length; i += 1)
|
||||
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
||||
return mismatch === 0;
|
||||
};
|
||||
var unseal = async (_crypto, sealed, password, options) => {
|
||||
if (!password)
|
||||
throw new Error("Empty password");
|
||||
const opts = clone(options);
|
||||
const now = Date.now() + (opts.localtimeOffsetMsec || 0);
|
||||
const parts = sealed.split("*");
|
||||
if (parts.length !== 8)
|
||||
throw new Error("Incorrect number of sealed components");
|
||||
const prefix = parts[0];
|
||||
let passwordId = parts[1];
|
||||
const encryptionSalt = parts[2];
|
||||
const encryptionIv = parts[3];
|
||||
const encryptedB64 = parts[4];
|
||||
const expiration = parts[5];
|
||||
const hmacSalt = parts[6];
|
||||
const hmac = parts[7];
|
||||
const macBaseString = `${prefix}*${passwordId}*${encryptionSalt}*${encryptionIv}*${encryptedB64}*${expiration}`;
|
||||
if (macPrefix !== prefix)
|
||||
throw new Error("Wrong mac prefix");
|
||||
if (expiration) {
|
||||
if (!/^\d+$/.test(expiration))
|
||||
throw new Error("Invalid expiration");
|
||||
const exp = Number.parseInt(expiration, 10);
|
||||
if (exp <= now - opts.timestampSkewSec * 1e3)
|
||||
throw new Error("Expired seal");
|
||||
}
|
||||
let pass = "";
|
||||
passwordId = passwordId || "default";
|
||||
if (typeof password === "string" || password instanceof Uint8Array)
|
||||
pass = password;
|
||||
else if (passwordId in password) {
|
||||
pass = password[passwordId];
|
||||
} else {
|
||||
throw new Error(`Cannot find password: ${passwordId}`);
|
||||
}
|
||||
pass = normalizePassword(pass);
|
||||
const macOptions = opts.integrity;
|
||||
macOptions.salt = hmacSalt;
|
||||
const mac = await hmacWithPassword(_crypto, pass.integrity, macOptions, macBaseString);
|
||||
if (!fixedTimeComparison(mac.digest, hmac))
|
||||
throw new Error("Bad hmac value");
|
||||
const encrypted = base64urlDecode(encryptedB64);
|
||||
const decryptOptions = opts.encryption;
|
||||
decryptOptions.salt = encryptionSalt;
|
||||
decryptOptions.iv = base64urlDecode(encryptionIv);
|
||||
const decrypted = await decrypt(_crypto, pass.encryption, decryptOptions, encrypted);
|
||||
if (decrypted)
|
||||
return JSON.parse(decrypted);
|
||||
return null;
|
||||
};
|
||||
|
||||
export { algorithms, base64urlDecode, base64urlEncode, bufferToString, clone, decrypt, defaults, encrypt, generateKey, hmacWithPassword, macFormatVersion, macPrefix, randomBits, seal, stringToBuffer, unseal };
|
Reference in New Issue
Block a user