// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { op_node_pbkdf2, op_node_pbkdf2_async } from "ext:core/ops"; import { Buffer } from "node:buffer"; import { HASH_DATA } from "ext:deno_node/internal/crypto/types.ts"; import { validateFunction, validateString, validateUint32, } from "ext:deno_node/internal/validators.mjs"; import { getArrayBufferOrView } from "ext:deno_node/internal/crypto/keys.ts"; import { ERR_CRYPTO_INVALID_DIGEST, ERR_OUT_OF_RANGE, } from "ext:deno_node/internal/errors.ts"; export const MAX_ALLOC = Math.pow(2, 30) - 1; export const MAX_I32 = 2 ** 31 - 1; export type NormalizedAlgorithms = | "md5" | "ripemd160" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"; export type Algorithms = | "md5" | "ripemd160" | "rmd160" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"; function check( password: HASH_DATA, salt: HASH_DATA, iterations: number, keylen: number, digest: string, ) { validateString(digest, "digest"); password = getArrayBufferOrView(password, "password", "buffer"); salt = getArrayBufferOrView(salt, "salt", "buffer"); validateUint32(iterations, "iterations", true); validateUint32(keylen, "keylen"); if (iterations > MAX_I32) { throw new ERR_OUT_OF_RANGE("iterations", `<= ${MAX_I32}`, iterations); } if (keylen > MAX_I32) { throw new ERR_OUT_OF_RANGE("keylen", `<= ${MAX_I32}`, keylen); } return { password, salt, iterations, keylen, digest }; } /** * @param iterations Needs to be higher or equal than zero * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) * @param digest Algorithm to be used for encryption */ export function pbkdf2Sync( password: HASH_DATA, salt: HASH_DATA, iterations: number, keylen: number, digest: string, ): Buffer { ({ password, salt, iterations, keylen, digest } = check( password, salt, iterations, keylen, digest, )); digest = digest.toLowerCase() as NormalizedAlgorithms; const DK = new Uint8Array(keylen); if (!op_node_pbkdf2(password, salt, iterations, digest, DK)) { throw new ERR_CRYPTO_INVALID_DIGEST(digest); } return Buffer.from(DK); } /** * @param iterations Needs to be higher or equal than zero * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) * @param digest Algorithm to be used for encryption */ export function pbkdf2( password: HASH_DATA, salt: HASH_DATA, iterations: number, keylen: number, digest: string, callback: (err: Error | null, derivedKey?: Buffer) => void, ) { if (typeof digest === "function") { callback = digest; digest = undefined as unknown as string; } ({ password, salt, iterations, keylen, digest } = check( password, salt, iterations, keylen, digest, )); validateFunction(callback, "callback"); digest = digest.toLowerCase() as NormalizedAlgorithms; op_node_pbkdf2_async( password, salt, iterations, digest, keylen, ).then( (DK) => callback(null, Buffer.from(DK)), ) .catch((err) => callback(err)); } export default { MAX_ALLOC, pbkdf2, pbkdf2Sync, };