fix(ext/node): crypto.getCipherInfo() (#22916)

Stub implementation of getCipherInfo(). Good enough for most cases.

Note: We do not support all OpenSSL ciphers (likely never will)

Fixes https://github.com/denoland/deno/issues/21805
This commit is contained in:
Divy Srivastava 2024-03-14 06:30:29 -07:00 committed by GitHub
parent cad79af785
commit cf3c6f9b08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 107 additions and 42 deletions

View file

@ -86,7 +86,6 @@ import {
import {
Cipheriv,
Decipheriv,
getCipherInfo,
privateDecrypt,
privateEncrypt,
publicDecrypt,
@ -149,6 +148,7 @@ import type {
X509CheckOptions,
} from "ext:deno_node/internal/crypto/x509.ts";
import {
getCipherInfo,
getCiphers,
getCurves,
secureHeapUsed,

View file

@ -22,11 +22,6 @@ import {
op_node_public_encrypt,
} from "ext:core/ops";
import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
import {
validateInt32,
validateObject,
} from "ext:deno_node/internal/validators.mjs";
import { Buffer } from "node:buffer";
import { notImplemented } from "ext:deno_node/_utils.ts";
import type { TransformOptions } from "ext:deno_node/_stream.d.ts";
@ -405,41 +400,6 @@ export class Decipheriv extends Transform implements Cipher {
}
}
export function getCipherInfo(
nameOrNid: string | number,
options?: { keyLength?: number; ivLength?: number },
) {
if (typeof nameOrNid !== "string" && typeof nameOrNid !== "number") {
throw new ERR_INVALID_ARG_TYPE(
"nameOrNid",
["string", "number"],
nameOrNid,
);
}
if (typeof nameOrNid === "number") {
validateInt32(nameOrNid, "nameOrNid");
}
let keyLength, ivLength;
if (options !== undefined) {
validateObject(options, "options");
({ keyLength, ivLength } = options);
if (keyLength !== undefined) {
validateInt32(keyLength, "options.keyLength");
}
if (ivLength !== undefined) {
validateInt32(ivLength, "options.ivLength");
}
}
notImplemented("crypto.getCipherInfo");
}
export function privateEncrypt(
privateKey: ArrayBufferView | string | KeyObject,
buffer: ArrayBufferView | string | KeyObject,
@ -503,5 +463,4 @@ export default {
Cipheriv,
Decipheriv,
prepareKey,
getCipherInfo,
};

View file

@ -83,6 +83,89 @@ export function getCiphers(): string[] {
return supportedCiphers;
}
export function getCipherInfo(
nameOrNid: string | number,
options?: { keyLength?: number; ivLength?: number },
) {
if (typeof nameOrNid !== "string" && typeof nameOrNid !== "number") {
throw new ERR_INVALID_ARG_TYPE(
"nameOrNid",
["string", "number"],
nameOrNid,
);
}
if (typeof nameOrNid === "number") {
validateInt32(nameOrNid, "nameOrNid");
}
let keyLength, ivLength;
if (options !== undefined) {
validateObject(options, "options");
({ keyLength, ivLength } = options);
if (keyLength !== undefined) {
validateInt32(keyLength, "options.keyLength");
}
if (ivLength !== undefined) {
validateInt32(ivLength, "options.ivLength");
}
}
// This API is heavily based on OpenSSL's EVP_get_cipherbyname(3) and
// EVP_get_cipherbynid(3) functions.
//
// TODO(@littledivy): write proper cipher info utility in Rust
// in future refactors
const cipher = supportedCiphers.find((c) => c === nameOrNid);
if (cipher === undefined) {
return undefined;
}
const match = cipher.match(/^(aes)-(\d+)-(\w+)$/);
if (match) {
const [, name, keyLength, mode] = match;
return {
name: `${name}-${keyLength}-${mode}`,
keyLength: parseInt(keyLength) / 8,
mode,
ivLength: 16,
};
}
if (cipher === "aes128") {
return {
name: "aes-128-cbc",
keyLength: 16,
mode: "cbc",
ivLength: 16,
};
}
if (cipher === "aes192") {
return {
name: "aes-192-cbc",
keyLength: 24,
mode: "cbc",
ivLength: 16,
};
}
if (cipher === "aes256") {
return {
name: "aes-256-cbc",
keyLength: 32,
mode: "cbc",
ivLength: 16,
};
}
return undefined;
}
let defaultEncoding = "buffer";
export function setDefaultEncoding(val: string) {
@ -150,6 +233,7 @@ export default {
getDefaultEncoding,
setDefaultEncoding,
getCiphers,
getCipherInfo,
getCurves,
secureHeapUsed,
setEngine,

View file

@ -256,3 +256,25 @@ Deno.test({
);
},
});
Deno.test({
name: "getCiphers",
fn() {
assertEquals(crypto.getCiphers().includes("aes-128-cbc"), true);
},
});
Deno.test({
name: "getCipherInfo",
fn() {
const info = crypto.getCipherInfo("aes-128-cbc")!;
assertEquals(info.name, "aes-128-cbc");
assertEquals(info.keyLength, 16);
assertEquals(info.ivLength, 16);
const info2 = crypto.getCipherInfo("aes128")!;
assertEquals(info2.name, "aes-128-cbc");
assertEquals(info2.keyLength, 16);
assertEquals(info2.ivLength, 16);
},
});