summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal/crypto
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2024-07-05 10:10:22 +0200
committerGitHub <noreply@github.com>2024-07-05 10:10:22 +0200
commit08e5606c3400d3a993c0ce6748901c56fc3db35b (patch)
tree032d3a09c6d22763ceb703e7908ca159d3d7a809 /ext/node/polyfills/internal/crypto
parentb290fd01f3f5d32f9d010fc719ced0240759c049 (diff)
fix(ext/node): rewrite digest handling (#24392)
Previously we had many different code paths all handling digests in different places, all with wildly different digest support. This commit rewrites this to use a single digest handling mechanism for all digest operations. It adds various aliases for digest algorithms, like node does. For example `sha1WithRSAEncryption` is an alias for `sha1`. It also adds support for `md5-sha1` digests in various places.
Diffstat (limited to 'ext/node/polyfills/internal/crypto')
-rw-r--r--ext/node/polyfills/internal/crypto/hkdf.ts7
-rw-r--r--ext/node/polyfills/internal/crypto/pbkdf2.ts76
-rw-r--r--ext/node/polyfills/internal/crypto/sig.ts9
-rw-r--r--ext/node/polyfills/internal/crypto/types.ts2
4 files changed, 69 insertions, 25 deletions
diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts
index 0a8dcbb2e..cca40a3c6 100644
--- a/ext/node/polyfills/internal/crypto/hkdf.ts
+++ b/ext/node/polyfills/internal/crypto/hkdf.ts
@@ -23,6 +23,7 @@ import {
} from "ext:deno_node/internal/crypto/util.ts";
import {
createSecretKey,
+ getKeyMaterial,
isKeyObject,
KeyObject,
} from "ext:deno_node/internal/crypto/keys.ts";
@@ -35,7 +36,7 @@ import {
const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
validateString(hash, "digest");
- key = new Uint8Array(prepareKey(key));
+ key = getKeyMaterial(prepareKey(key));
validateByteSource(salt, "salt");
validateByteSource(info, "info");
@@ -108,6 +109,8 @@ export function hkdf(
validateFunction(callback, "callback");
+ hash = hash.toLowerCase();
+
op_node_hkdf_async(hash, key, salt, info, length)
.then((okm) => callback(null, okm.buffer))
.catch((err) => callback(new ERR_CRYPTO_INVALID_DIGEST(err), undefined));
@@ -128,6 +131,8 @@ export function hkdfSync(
length,
));
+ hash = hash.toLowerCase();
+
const okm = new Uint8Array(length);
try {
op_node_hkdf(hash, key, salt, info, okm);
diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts
index 5cf102fa9..4e58cb68b 100644
--- a/ext/node/polyfills/internal/crypto/pbkdf2.ts
+++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts
@@ -7,8 +7,19 @@ 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"
@@ -29,6 +40,30 @@ export type Algorithms =
| "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)
@@ -39,18 +74,21 @@ export function pbkdf2Sync(
salt: HASH_DATA,
iterations: number,
keylen: number,
- digest: Algorithms = "sha1",
+ digest: string,
): Buffer {
- if (typeof iterations !== "number" || iterations < 0) {
- throw new TypeError("Bad iterations");
- }
- if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) {
- throw new TypeError("Bad key length");
- }
+ ({ 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 Error("Invalid digest");
+ throw new ERR_CRYPTO_INVALID_DIGEST(digest);
}
return Buffer.from(DK);
@@ -66,16 +104,26 @@ export function pbkdf2(
salt: HASH_DATA,
iterations: number,
keylen: number,
- digest: Algorithms = "sha1",
+ digest: string,
callback: (err: Error | null, derivedKey?: Buffer) => void,
) {
- if (typeof iterations !== "number" || iterations < 0) {
- throw new TypeError("Bad iterations");
- }
- if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) {
- throw new TypeError("Bad key length");
+ 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,
diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts
index 5a9611780..473670d2a 100644
--- a/ext/node/polyfills/internal/crypto/sig.ts
+++ b/ext/node/polyfills/internal/crypto/sig.ts
@@ -67,10 +67,6 @@ export class SignImpl extends Writable {
algorithm = algorithm.toLowerCase();
- if (algorithm.startsWith("rsa-")) {
- // Allows RSA-[digest_algorithm] as a valid algorithm
- algorithm = algorithm.slice(4);
- }
this.#digestType = algorithm;
this.hash = createHash(this.#digestType);
}
@@ -121,11 +117,6 @@ export class VerifyImpl extends Writable {
algorithm = algorithm.toLowerCase();
- if (algorithm.startsWith("rsa-")) {
- // Allows RSA-[digest_algorithm] as a valid algorithm
- algorithm = algorithm.slice(4);
- }
-
this.#digestType = algorithm;
this.hash = createHash(this.#digestType);
}
diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts
index 2b3ce34fe..45c0ea286 100644
--- a/ext/node/polyfills/internal/crypto/types.ts
+++ b/ext/node/polyfills/internal/crypto/types.ts
@@ -3,7 +3,7 @@
import { Buffer } from "../../buffer.ts";
-export type HASH_DATA = string | ArrayBufferView | Buffer;
+export type HASH_DATA = string | ArrayBufferView | Buffer | ArrayBuffer;
export type BinaryToTextEncoding = "base64" | "base64url" | "hex" | "binary";