diff options
Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/Cargo.toml | 1 | ||||
-rw-r--r-- | ext/node/crypto/mod.rs | 58 | ||||
-rw-r--r-- | ext/node/lib.rs | 3 | ||||
-rw-r--r-- | ext/node/polyfills/internal/crypto/hash.ts | 2 | ||||
-rw-r--r-- | ext/node/polyfills/internal/crypto/sig.ts | 66 |
5 files changed, 114 insertions, 16 deletions
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index d0a5a40fb..be3231087 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -39,5 +39,6 @@ serde = "1.0.149" sha-1 = "0.10.0" sha2 = "0.10.6" sha3 = "0.10.5" +signature.workspace = true tokio.workspace = true typenum = "1.15.0" diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 352d9c31a..3529a3aa4 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -290,6 +290,64 @@ pub fn op_node_decipheriv_final( context.r#final(input, output) } +#[op] +pub fn op_node_sign( + digest: &[u8], + digest_type: &str, + key: StringOrBuffer, + key_type: &str, + key_format: &str, +) -> Result<ZeroCopyBuf, AnyError> { + match key_type { + "rsa" => { + use rsa::pkcs1v15::SigningKey; + use signature::hazmat::PrehashSigner; + let key = match key_format { + "pem" => RsaPrivateKey::from_pkcs8_pem((&key).try_into()?) + .map_err(|_| type_error("Invalid RSA key"))?, + // TODO(kt3k): Support der and jwk formats + _ => { + return Err(type_error(format!( + "Unsupported key format: {}", + key_format + ))) + } + }; + Ok( + match digest_type { + "sha224" => { + let signing_key = SigningKey::<sha2::Sha224>::new_with_prefix(key); + signing_key.sign_prehash(digest)?.to_vec() + } + "sha256" => { + let signing_key = SigningKey::<sha2::Sha256>::new_with_prefix(key); + signing_key.sign_prehash(digest)?.to_vec() + } + "sha384" => { + let signing_key = SigningKey::<sha2::Sha384>::new_with_prefix(key); + signing_key.sign_prehash(digest)?.to_vec() + } + "sha512" => { + let signing_key = SigningKey::<sha2::Sha512>::new_with_prefix(key); + signing_key.sign_prehash(digest)?.to_vec() + } + _ => { + return Err(type_error(format!( + "Unknown digest algorithm: {}", + digest_type + ))) + } + } + .into(), + ) + } + _ => Err(type_error(format!( + "Signing with {} keys is not supported yet", + key_type + ))), + } +} + fn pbkdf2_sync( password: &[u8], salt: &[u8], diff --git a/ext/node/lib.rs b/ext/node/lib.rs index b83a86a5b..0507f4a88 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -119,6 +119,7 @@ deno_core::extension!(deno_node, crypto::op_node_check_prime_bytes_async, crypto::op_node_pbkdf2, crypto::op_node_pbkdf2_async, + crypto::op_node_sign, winerror::op_node_sys_to_uv_error, v8::op_v8_cached_data_version_tag, v8::op_v8_get_heap_statistics, @@ -406,7 +407,7 @@ pub fn initialize_runtime( let source_code = format!( r#"(function loadBuiltinNodeModules(nodeGlobalThisName, usesLocalNodeModulesDir, argv0) {{ Deno[Deno.internal].node.initialize( - nodeGlobalThisName, + nodeGlobalThisName, usesLocalNodeModulesDir, argv0 ); diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts index 63c92190c..00dfa19af 100644 --- a/ext/node/polyfills/internal/crypto/hash.ts +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -73,7 +73,7 @@ export class Hash extends Transform { if (typeof algorithm === "string") { this.#context = ops.op_node_create_hash( - algorithm, + algorithm.toLowerCase(), ); if (this.#context === 0) { throw new TypeError(`Unknown hash algorithm: ${algorithm}`); diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts index d61f82b0e..e49128b1e 100644 --- a/ext/node/polyfills/internal/crypto/sig.ts +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -14,6 +14,12 @@ import type { PublicKeyInput, } from "ext:deno_node/internal/crypto/types.ts"; import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts"; +import { createHash, Hash } from "ext:deno_node/internal/crypto/hash.ts"; +import { KeyFormat, KeyType } from "ext:deno_node/internal/crypto/types.ts"; +import { isArrayBufferView } from "ext:deno_node/internal/util/types.ts"; + +const { core } = globalThis.__bootstrap; +const { ops } = core; export type DSAEncoding = "der" | "ieee-p1363"; @@ -37,30 +43,62 @@ export interface VerifyKeyObjectInput extends SigningOptions { export type KeyLike = string | Buffer | KeyObject; export class Sign extends Writable { + hash: Hash; + #digestType: string; + constructor(algorithm: string, _options?: WritableOptions) { validateString(algorithm, "algorithm"); - super(); - - notImplemented("crypto.Sign"); + super({ + write(chunk, enc, callback) { + this.update(chunk, enc); + callback(); + }, + }); + + 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); } - sign(privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput): Buffer; sign( privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, - outputFormat: BinaryToTextEncoding, - ): string; - sign( - _privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, - _outputEncoding?: BinaryToTextEncoding, + encoding?: BinaryToTextEncoding, ): Buffer | string { - notImplemented("crypto.Sign.prototype.sign"); + let keyData: Uint8Array; + let keyType: KeyType; + let keyFormat: KeyFormat; + if (typeof privateKey === "string" || isArrayBufferView(privateKey)) { + // if the key is BinaryLike, interpret it as a PEM encoded RSA key + keyData = privateKey; + keyType = "rsa"; + keyFormat = "pem"; + } else { + // TODO(kt3k): Add support for the case when privateKey is a KeyObject, + // CryptoKey, etc + notImplemented("crypto.Sign.prototype.sign with non BinaryLike input"); + } + const ret = Buffer.from(ops.op_node_sign( + this.hash.digest(), + this.#digestType, + keyData!, + keyType, + keyFormat, + )); + return encoding ? ret.toString(encoding) : ret; } - update(data: BinaryLike): this; - update(data: string, inputEncoding: Encoding): this; - update(_data: BinaryLike | string, _inputEncoding?: Encoding): this { - notImplemented("crypto.Sign.prototype.update"); + update( + data: BinaryLike | string, + encoding?: Encoding, + ): this { + this.hash.update(data, encoding); + return this; } } |