From 814edcdd570b3fbef2001f7e6434320743a8f834 Mon Sep 17 00:00:00 2001 From: Felipe Baltor Date: Mon, 26 Jun 2023 23:04:49 -0300 Subject: [PATCH] test(ext/node): port crypto_test.ts from deno_std (#19561) --- .../unit_node/crypto/crypto_hash_test.ts | 110 +++++++++++++++++- ext/node/lib.rs | 1 + ext/node/ops/crypto/digest.rs | 13 +++ ext/node/ops/crypto/mod.rs | 5 + ext/node/polyfills/crypto.ts | 8 +- ext/node/polyfills/internal/crypto/hash.ts | 8 ++ ext/node/polyfills/internal/crypto/util.ts | 37 ------ 7 files changed, 139 insertions(+), 43 deletions(-) diff --git a/cli/tests/unit_node/crypto/crypto_hash_test.ts b/cli/tests/unit_node/crypto/crypto_hash_test.ts index 6795777703..e140765ecd 100644 --- a/cli/tests/unit_node/crypto/crypto_hash_test.ts +++ b/cli/tests/unit_node/crypto/crypto_hash_test.ts @@ -1,10 +1,15 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -import { createHash, createHmac } from "node:crypto"; -import { assertEquals } from "../../../../test_util/std/testing/asserts.ts"; +import { createHash, createHmac, getHashes, randomUUID } from "node:crypto"; +import { Buffer } from "node:buffer"; +import { Readable } from "node:stream"; +import { + assert, + assertEquals, +} from "../../../../test_util/std/testing/asserts.ts"; // https://github.com/denoland/deno/issues/18140 Deno.test({ - name: "createHmac digest", + name: "[node/crypto] createHmac digest", fn() { assertEquals( createHmac("sha256", "secret").update("hello").digest("hex"), @@ -14,7 +19,7 @@ Deno.test({ }); Deno.test({ - name: "createHash digest", + name: "[node/crypto] createHash digest", fn() { assertEquals( createHash("sha256").update("hello").digest("hex"), @@ -22,3 +27,100 @@ Deno.test({ ); }, }); + +Deno.test("[node/crypto.Hash] basic usage - buffer output", () => { + const d = createHash("sha1").update("abc").update("def").digest(); + assertEquals( + d, + Buffer.from([ + 0x1f, + 0x8a, + 0xc1, + 0xf, + 0x23, + 0xc5, + 0xb5, + 0xbc, + 0x11, + 0x67, + 0xbd, + 0xa8, + 0x4b, + 0x83, + 0x3e, + 0x5c, + 0x5, + 0x7a, + 0x77, + 0xd2, + ]), + ); +}); + +Deno.test("[node/crypto.Hash] basic usage - hex output", () => { + const d = createHash("sha1").update("abc").update("def").digest("hex"); + assertEquals(d, "1f8ac10f23c5b5bc1167bda84b833e5c057a77d2"); +}); + +Deno.test("[node/crypto.Hash] basic usage - base64 output", () => { + const d = createHash("sha1").update("abc").update("def").digest("base64"); + assertEquals(d, "H4rBDyPFtbwRZ72oS4M+XAV6d9I="); +}); + +Deno.test("[node/crypto.Hash] basic usage - base64url output", () => { + const d = createHash("sha1").update("abc").update("def").digest("base64url"); + assertEquals(d, "H4rBDyPFtbwRZ72oS4M-XAV6d9I"); +}); + +Deno.test("[node/crypto.Hash] streaming usage", async () => { + const source = Readable.from(["abc", "def"]); + const hash = createHash("sha1"); + const dest = source.pipe(hash); + const result = await new Promise((resolve, _) => { + let buffer = Buffer.from([]); + dest.on("data", (data) => { + buffer = Buffer.concat([buffer, data]); + }); + dest.on("end", () => { + resolve(buffer); + }); + }); + assertEquals( + result, + Buffer.from([ + 0x1f, + 0x8a, + 0xc1, + 0xf, + 0x23, + 0xc5, + 0xb5, + 0xbc, + 0x11, + 0x67, + 0xbd, + 0xa8, + 0x4b, + 0x83, + 0x3e, + 0x5c, + 0x5, + 0x7a, + 0x77, + 0xd2, + ]), + ); +}); + +Deno.test("[node/crypto.getHashes]", () => { + for (const algorithm of getHashes()) { + const d = createHash(algorithm).update("abc").digest(); + assert(d instanceof Buffer); + assert(d.length > 0); + } +}); + +Deno.test("[node/crypto.getRandomUUID] works the same way as Web Crypto API", () => { + assertEquals(randomUUID().length, crypto.randomUUID().length); + assertEquals(typeof randomUUID(), typeof crypto.randomUUID()); +}); diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 99c138b8f3..bb8d744c85 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -139,6 +139,7 @@ deno_core::extension!(deno_node, ops::crypto::op_node_cipheriv_final, ops::crypto::op_node_create_cipheriv, ops::crypto::op_node_create_hash, + ops::crypto::op_node_get_hashes, ops::crypto::op_node_decipheriv_decrypt, ops::crypto::op_node_decipheriv_final, ops::crypto::op_node_hash_update, diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index 4fab58a432..685fc32d01 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -99,6 +99,19 @@ impl Hash { Sha512(context) => context.finalize(), } } + + pub fn get_hashes() -> Vec<&'static str> { + vec![ + "md4", + "md5", + "ripemd160", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + ] + } } impl Clone for Hash { diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index a83263fff6..1155a4d15a 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -88,6 +88,11 @@ pub fn op_node_create_hash(state: &mut OpState, algorithm: &str) -> u32 { }) } +#[op(fast)] +pub fn op_node_get_hashes() -> Vec<&'static str> { + digest::Hash::get_hashes() +} + #[op(fast)] pub fn op_node_hash_update(state: &mut OpState, rid: u32, data: &[u8]) -> bool { let context = match state.resource_table.get::(rid) { diff --git a/ext/node/polyfills/crypto.ts b/ext/node/polyfills/crypto.ts index bc18410c7f..1c166240c5 100644 --- a/ext/node/polyfills/crypto.ts +++ b/ext/node/polyfills/crypto.ts @@ -134,7 +134,12 @@ import type { VerifyKeyObjectInput, VerifyPublicKeyInput, } from "ext:deno_node/internal/crypto/sig.ts"; -import { createHash, Hash, Hmac } from "ext:deno_node/internal/crypto/hash.ts"; +import { + createHash, + getHashes, + Hash, + Hmac, +} from "ext:deno_node/internal/crypto/hash.ts"; import { X509Certificate } from "ext:deno_node/internal/crypto/x509.ts"; import type { PeerCertificate, @@ -143,7 +148,6 @@ import type { import { getCiphers, getCurves, - getHashes, secureHeapUsed, setEngine, } from "ext:deno_node/internal/crypto/util.ts"; diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts index 34e3c1230e..51c9ec955d 100644 --- a/ext/node/polyfills/internal/crypto/hash.ts +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -229,6 +229,14 @@ export function createHash(algorithm: string, opts?: TransformOptions) { return new Hash(algorithm, opts); } +/** + * Get the list of implemented hash algorithms. + * @returns Array of hash algorithm names. + */ +export function getHashes() { + return ops.op_node_get_hashes(); +} + export default { Hash, Hmac, diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts index 2e269b7fad..18495c3497 100644 --- a/ext/node/polyfills/internal/crypto/util.ts +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -17,35 +17,6 @@ import { kKeyObject, } from "ext:deno_node/internal/crypto/constants.ts"; -// TODO(kt3k): Generate this list from `digestAlgorithms` -// of std/crypto/_wasm/mod.ts -const digestAlgorithms = [ - "blake2b256", - "blake2b384", - "blake2b", - "blake2s", - "blake3", - "keccak-224", - "keccak-256", - "keccak-384", - "keccak-512", - "sha384", - "sha3-224", - "sha3-256", - "sha3-384", - "sha3-512", - "shake128", - "shake256", - "tiger", - "rmd160", - "sha224", - "sha256", - "sha512", - "md4", - "md5", - "sha1", -]; - export type EllipticCurve = { name: string; ephemeral: boolean; @@ -148,13 +119,6 @@ export const validateByteSource = hideStackFrames((val, name) => { ); }); -/** - * Returns an array of the names of the supported hash algorithms, such as 'sha1'. - */ -export function getHashes(): readonly string[] { - return digestAlgorithms; -} - const curveNames = ellipticCurves.map((x) => x.name); export function getCurves(): readonly string[] { return curveNames; @@ -181,7 +145,6 @@ export { kAesKeyLengths, kHandle, kKeyObject }; export default { getDefaultEncoding, - getHashes, setDefaultEncoding, getCiphers, getCurves,