diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-02-14 17:38:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 17:38:45 +0100 |
commit | d47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch) | |
tree | 6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/internal/crypto | |
parent | 1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff) |
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is
transpiled and snapshotted during the build process.
During the first pass a minimal amount of work was done to create the
snapshot, a lot of code in "ext/node" depends on presence of "Deno"
global. This code will be gradually fixed in the follow up PRs to migrate
it to import relevant APIs from "internal:" modules.
Currently the code from snapshot is not used in any way, and all
Node/npm compatibility still uses code from
"https://deno.land/std/node" (or from the location specified by
"DENO_NODE_COMPAT_URL"). This will also be handled in a follow
up PRs.
---------
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/internal/crypto')
20 files changed, 3321 insertions, 0 deletions
diff --git a/ext/node/polyfills/internal/crypto/_hex.ts b/ext/node/polyfills/internal/crypto/_hex.ts new file mode 100644 index 000000000..5cc44aaa8 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_hex.ts @@ -0,0 +1,19 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-fmt-ignore +const hexTable = new Uint8Array([ + 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 97, 98, + 99, 100, 101, 102 +]); + +/** Encodes `src` into `src.length * 2` bytes. */ +export function encode(src: Uint8Array): Uint8Array { + const dst = new Uint8Array(src.length * 2); + for (let i = 0; i < dst.length; i++) { + const v = src[i]; + dst[i * 2] = hexTable[v >> 4]; + dst[i * 2 + 1] = hexTable[v & 0x0f]; + } + return dst; +} diff --git a/ext/node/polyfills/internal/crypto/_keys.ts b/ext/node/polyfills/internal/crypto/_keys.ts new file mode 100644 index 000000000..794582bf1 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_keys.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { kKeyObject } from "internal:deno_node/polyfills/internal/crypto/constants.ts"; + +export const kKeyType = Symbol("kKeyType"); + +export function isKeyObject(obj: unknown): boolean { + return ( + obj != null && (obj as Record<symbol, unknown>)[kKeyType] !== undefined + ); +} + +export function isCryptoKey(obj: unknown): boolean { + return ( + obj != null && (obj as Record<symbol, unknown>)[kKeyObject] !== undefined + ); +} diff --git a/ext/node/polyfills/internal/crypto/_randomBytes.ts b/ext/node/polyfills/internal/crypto/_randomBytes.ts new file mode 100644 index 000000000..41678fcf1 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomBytes.ts @@ -0,0 +1,70 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export const MAX_RANDOM_VALUES = 65536; +export const MAX_SIZE = 4294967295; + +function generateRandomBytes(size: number) { + if (size > MAX_SIZE) { + throw new RangeError( + `The value of "size" is out of range. It must be >= 0 && <= ${MAX_SIZE}. Received ${size}`, + ); + } + + const bytes = Buffer.allocUnsafe(size); + + //Work around for getRandomValues max generation + if (size > MAX_RANDOM_VALUES) { + for (let generated = 0; generated < size; generated += MAX_RANDOM_VALUES) { + globalThis.crypto.getRandomValues( + bytes.slice(generated, generated + MAX_RANDOM_VALUES), + ); + } + } else { + globalThis.crypto.getRandomValues(bytes); + } + + return bytes; +} + +/** + * @param size Buffer length, must be equal or greater than zero + */ +export default function randomBytes(size: number): Buffer; +export default function randomBytes( + size: number, + cb?: (err: Error | null, buf?: Buffer) => void, +): void; +export default function randomBytes( + size: number, + cb?: (err: Error | null, buf?: Buffer) => void, +): Buffer | void { + if (typeof cb === "function") { + let err: Error | null = null, bytes: Buffer; + try { + bytes = generateRandomBytes(size); + } catch (e) { + //NodeJS nonsense + //If the size is out of range it will throw sync, otherwise throw async + if ( + e instanceof RangeError && + e.message.includes('The value of "size" is out of range') + ) { + throw e; + } else if (e instanceof Error) { + err = e; + } else { + err = new Error("[non-error thrown]"); + } + } + setTimeout(() => { + if (err) { + cb(err); + } else { + cb(null, bytes); + } + }, 0); + } else { + return generateRandomBytes(size); + } +} diff --git a/ext/node/polyfills/internal/crypto/_randomFill.ts b/ext/node/polyfills/internal/crypto/_randomFill.ts new file mode 100644 index 000000000..045072696 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomFill.ts @@ -0,0 +1,84 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import randomBytes, { + MAX_SIZE as kMaxUint32, +} from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +const kBufferMaxLength = 0x7fffffff; + +function assertOffset(offset: number, length: number) { + if (offset > kMaxUint32 || offset < 0) { + throw new TypeError("offset must be a uint32"); + } + + if (offset > kBufferMaxLength || offset > length) { + throw new RangeError("offset out of range"); + } +} + +function assertSize(size: number, offset: number, length: number) { + if (size > kMaxUint32 || size < 0) { + throw new TypeError("size must be a uint32"); + } + + if (size + offset > length || size > kBufferMaxLength) { + throw new RangeError("buffer too small"); + } +} + +export default function randomFill( + buf: Buffer, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset: number, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset: number, + size: number, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset?: number | ((err: Error | null, buf: Buffer) => void), + size?: number | ((err: Error | null, buf: Buffer) => void), + cb?: (err: Error | null, buf: Buffer) => void, +) { + if (typeof offset === "function") { + cb = offset; + offset = 0; + size = buf.length; + } else if (typeof size === "function") { + cb = size; + size = buf.length - Number(offset as number); + } + + assertOffset(offset as number, buf.length); + assertSize(size as number, offset as number, buf.length); + + randomBytes(size as number, (err, bytes) => { + if (err) return cb!(err, buf); + bytes?.copy(buf, offset as number); + cb!(null, buf); + }); +} + +export function randomFillSync(buf: Buffer, offset = 0, size?: number) { + assertOffset(offset, buf.length); + + if (size === undefined) size = buf.length - offset; + + assertSize(size, offset, buf.length); + + const bytes = randomBytes(size); + + bytes.copy(buf, offset); + + return buf; +} diff --git a/ext/node/polyfills/internal/crypto/_randomInt.ts b/ext/node/polyfills/internal/crypto/_randomInt.ts new file mode 100644 index 000000000..637251541 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomInt.ts @@ -0,0 +1,60 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +export default function randomInt(max: number): number; +export default function randomInt(min: number, max: number): number; +export default function randomInt( + max: number, + cb: (err: Error | null, n?: number) => void, +): void; +export default function randomInt( + min: number, + max: number, + cb: (err: Error | null, n?: number) => void, +): void; + +export default function randomInt( + max: number, + min?: ((err: Error | null, n?: number) => void) | number, + cb?: (err: Error | null, n?: number) => void, +): number | void { + if (typeof max === "number" && typeof min === "number") { + [max, min] = [min, max]; + } + if (min === undefined) min = 0; + else if (typeof min === "function") { + cb = min; + min = 0; + } + + if ( + !Number.isSafeInteger(min) || + typeof max === "number" && !Number.isSafeInteger(max) + ) { + throw new Error("max or min is not a Safe Number"); + } + + if (max - min > Math.pow(2, 48)) { + throw new RangeError("max - min should be less than 2^48!"); + } + + if (min >= max) { + throw new Error("Min is bigger than Max!"); + } + + const randomBuffer = new Uint32Array(1); + + globalThis.crypto.getRandomValues(randomBuffer); + + const randomNumber = randomBuffer[0] / (0xffffffff + 1); + + min = Math.ceil(min); + max = Math.floor(max); + + const result = Math.floor(randomNumber * (max - min)) + min; + + if (cb) { + cb(null, result); + return; + } + + return result; +} diff --git a/ext/node/polyfills/internal/crypto/certificate.ts b/ext/node/polyfills/internal/crypto/certificate.ts new file mode 100644 index 000000000..f6fb333a9 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/certificate.ts @@ -0,0 +1,23 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export class Certificate { + static Certificate = Certificate; + static exportChallenge(_spkac: BinaryLike, _encoding?: string): Buffer { + notImplemented("crypto.Certificate.exportChallenge"); + } + + static exportPublicKey(_spkac: BinaryLike, _encoding?: string): Buffer { + notImplemented("crypto.Certificate.exportPublicKey"); + } + + static verifySpkac(_spkac: BinaryLike, _encoding?: string): boolean { + notImplemented("crypto.Certificate.verifySpkac"); + } +} + +export default Certificate; diff --git a/ext/node/polyfills/internal/crypto/cipher.ts b/ext/node/polyfills/internal/crypto/cipher.ts new file mode 100644 index 000000000..2778b40fa --- /dev/null +++ b/ext/node/polyfills/internal/crypto/cipher.ts @@ -0,0 +1,292 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateInt32, + validateObject, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import type { TransformOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import { Transform } from "internal:deno_node/polyfills/_stream.mjs"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BufferEncoding } from "internal:deno_node/polyfills/_global.d.ts"; +import type { + BinaryLike, + Encoding, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js"; + +export { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js"; + +export type CipherCCMTypes = + | "aes-128-ccm" + | "aes-192-ccm" + | "aes-256-ccm" + | "chacha20-poly1305"; +export type CipherGCMTypes = "aes-128-gcm" | "aes-192-gcm" | "aes-256-gcm"; +export type CipherOCBTypes = "aes-128-ocb" | "aes-192-ocb" | "aes-256-ocb"; + +export type CipherKey = BinaryLike | KeyObject; + +export interface CipherCCMOptions extends TransformOptions { + authTagLength: number; +} + +export interface CipherGCMOptions extends TransformOptions { + authTagLength?: number | undefined; +} + +export interface CipherOCBOptions extends TransformOptions { + authTagLength: number; +} + +export interface Cipher extends ReturnType<typeof Transform> { + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + + setAutoPadding(autoPadding?: boolean): this; +} + +export type Decipher = Cipher; + +export interface CipherCCM extends Cipher { + setAAD( + buffer: ArrayBufferView, + options: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface CipherGCM extends Cipher { + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface CipherOCB extends Cipher { + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface DecipherCCM extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options: { + plaintextLength: number; + }, + ): this; +} + +export interface DecipherGCM extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; +} + +export interface DecipherOCB extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; +} + +export class Cipheriv extends Transform implements Cipher { + constructor( + _cipher: string, + _key: CipherKey, + _iv: BinaryLike | null, + _options?: TransformOptions, + ) { + super(); + + notImplemented("crypto.Cipheriv"); + } + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + final(_outputEncoding?: string): Buffer | string { + notImplemented("crypto.Cipheriv.prototype.final"); + } + + getAuthTag(): Buffer { + notImplemented("crypto.Cipheriv.prototype.getAuthTag"); + } + + setAAD( + _buffer: ArrayBufferView, + _options?: { + plaintextLength: number; + }, + ): this { + notImplemented("crypto.Cipheriv.prototype.setAAD"); + } + + setAutoPadding(_autoPadding?: boolean): this { + notImplemented("crypto.Cipheriv.prototype.setAutoPadding"); + } + + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + update( + _data: string | BinaryLike | ArrayBufferView, + _inputEncoding?: Encoding, + _outputEncoding?: Encoding, + ): Buffer | string { + notImplemented("crypto.Cipheriv.prototype.update"); + } +} + +export class Decipheriv extends Transform implements Cipher { + constructor( + _cipher: string, + _key: CipherKey, + _iv: BinaryLike | null, + _options?: TransformOptions, + ) { + super(); + + notImplemented("crypto.Decipheriv"); + } + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + final(_outputEncoding?: string): Buffer | string { + notImplemented("crypto.Decipheriv.prototype.final"); + } + + setAAD( + _buffer: ArrayBufferView, + _options?: { + plaintextLength: number; + }, + ): this { + notImplemented("crypto.Decipheriv.prototype.setAAD"); + } + + setAuthTag(_buffer: BinaryLike, _encoding?: string): this { + notImplemented("crypto.Decipheriv.prototype.setAuthTag"); + } + + setAutoPadding(_autoPadding?: boolean): this { + notImplemented("crypto.Decipheriv.prototype.setAutoPadding"); + } + + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + update( + _data: string | BinaryLike | ArrayBufferView, + _inputEncoding?: Encoding, + _outputEncoding?: Encoding, + ): Buffer | string { + notImplemented("crypto.Decipheriv.prototype.update"); + } +} + +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 default { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, + Cipheriv, + Decipheriv, + getCipherInfo, +}; diff --git a/ext/node/polyfills/internal/crypto/constants.ts b/ext/node/polyfills/internal/crypto/constants.ts new file mode 100644 index 000000000..d62415360 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/constants.ts @@ -0,0 +1,5 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +export const kHandle = Symbol("kHandle"); +export const kKeyObject = Symbol("kKeyObject"); diff --git a/ext/node/polyfills/internal/crypto/diffiehellman.ts b/ext/node/polyfills/internal/crypto/diffiehellman.ts new file mode 100644 index 000000000..eb903ccb3 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/diffiehellman.ts @@ -0,0 +1,306 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateInt32, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + getDefaultEncoding, + toBuf, +} from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import type { + BinaryLike, + BinaryToTextEncoding, + ECDHKeyFormat, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BufferEncoding } from "internal:deno_node/polyfills/_global.d.ts"; + +const DH_GENERATOR = 2; + +export class DiffieHellman { + verifyError!: number; + + constructor( + sizeOrKey: unknown, + keyEncoding?: unknown, + generator?: unknown, + genEncoding?: unknown, + ) { + if ( + typeof sizeOrKey !== "number" && + typeof sizeOrKey !== "string" && + !isArrayBufferView(sizeOrKey) && + !isAnyArrayBuffer(sizeOrKey) + ) { + throw new ERR_INVALID_ARG_TYPE( + "sizeOrKey", + ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], + sizeOrKey, + ); + } + + if (typeof sizeOrKey === "number") { + validateInt32(sizeOrKey, "sizeOrKey"); + } + + if ( + keyEncoding && + !Buffer.isEncoding(keyEncoding as BinaryToTextEncoding) && + keyEncoding !== "buffer" + ) { + genEncoding = generator; + generator = keyEncoding; + keyEncoding = false; + } + + const encoding = getDefaultEncoding(); + keyEncoding = keyEncoding || encoding; + genEncoding = genEncoding || encoding; + + if (typeof sizeOrKey !== "number") { + sizeOrKey = toBuf(sizeOrKey as string, keyEncoding as string); + } + + if (!generator) { + generator = DH_GENERATOR; + } else if (typeof generator === "number") { + validateInt32(generator, "generator"); + } else if (typeof generator === "string") { + generator = toBuf(generator, genEncoding as string); + } else if (!isArrayBufferView(generator) && !isAnyArrayBuffer(generator)) { + throw new ERR_INVALID_ARG_TYPE( + "generator", + ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], + generator, + ); + } + + notImplemented("crypto.DiffieHellman"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding): string; + generateKeys(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.generateKeys"); + } + + getGenerator(): Buffer; + getGenerator(encoding: BinaryToTextEncoding): string; + getGenerator(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getGenerator"); + } + + getPrime(): Buffer; + getPrime(encoding: BinaryToTextEncoding): string; + getPrime(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrime"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding): string; + getPublicKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPublicKey"); + } + + setPrivateKey(privateKey: ArrayBufferView): void; + setPrivateKey(privateKey: string, encoding: BufferEncoding): void; + setPrivateKey( + _privateKey: ArrayBufferView | string, + _encoding?: BufferEncoding, + ) { + notImplemented("crypto.DiffieHellman.prototype.setPrivateKey"); + } + + setPublicKey(publicKey: ArrayBufferView): void; + setPublicKey(publicKey: string, encoding: BufferEncoding): void; + setPublicKey( + _publicKey: ArrayBufferView | string, + _encoding?: BufferEncoding, + ) { + notImplemented("crypto.DiffieHellman.prototype.setPublicKey"); + } +} + +export class DiffieHellmanGroup { + verifyError!: number; + + constructor(_name: string) { + notImplemented("crypto.DiffieHellmanGroup"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding): string; + generateKeys(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.generateKeys"); + } + + getGenerator(): Buffer; + getGenerator(encoding: BinaryToTextEncoding): string; + getGenerator(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getGenerator"); + } + + getPrime(): Buffer; + getPrime(encoding: BinaryToTextEncoding): string; + getPrime(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrime"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding): string; + getPublicKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPublicKey"); + } +} + +export class ECDH { + constructor(curve: string) { + validateString(curve, "curve"); + + notImplemented("crypto.ECDH"); + } + + static convertKey( + _key: BinaryLike, + _curve: string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: "latin1" | "hex" | "base64" | "base64url", + _format?: "uncompressed" | "compressed" | "hybrid", + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.convertKey"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; + generateKeys( + _encoding?: BinaryToTextEncoding, + _format?: ECDHKeyFormat, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.generateKeys"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.ECDH.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; + getPublicKey( + _encoding?: BinaryToTextEncoding, + _format?: ECDHKeyFormat, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.getPublicKey"); + } + + setPrivateKey(privateKey: ArrayBufferView): void; + setPrivateKey(privateKey: string, encoding: BinaryToTextEncoding): void; + setPrivateKey( + _privateKey: ArrayBufferView | string, + _encoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.setPrivateKey"); + } +} + +export function diffieHellman(_options: { + privateKey: KeyObject; + publicKey: KeyObject; +}): Buffer { + notImplemented("crypto.diffieHellman"); +} + +export default { + DiffieHellman, + DiffieHellmanGroup, + ECDH, + diffieHellman, +}; diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts new file mode 100644 index 000000000..7995e5f8c --- /dev/null +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -0,0 +1,230 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { Transform } from "internal:deno_node/polyfills/stream.ts"; +import { encode as encodeToHex } from "internal:deno_node/polyfills/internal/crypto/_hex.ts"; +import { + forgivingBase64Encode as encodeToBase64, + forgivingBase64UrlEncode as encodeToBase64Url, +} from "internal:deno_web/00_infra.js"; +import type { TransformOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import type { + BinaryToTextEncoding, + Encoding, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { + KeyObject, + prepareSecretKey, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +const { ops } = globalThis.__bootstrap.core; + +const coerceToBytes = (data: string | BufferSource): Uint8Array => { + if (data instanceof Uint8Array) { + return data; + } else if (typeof data === "string") { + // This assumes UTF-8, which may not be correct. + return new TextEncoder().encode(data); + } else if (ArrayBuffer.isView(data)) { + return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + } else if (data instanceof ArrayBuffer) { + return new Uint8Array(data); + } else { + throw new TypeError("expected data to be string | BufferSource"); + } +}; + +/** + * The Hash class is a utility for creating hash digests of data. It can be used in one of two ways: + * + * - As a stream that is both readable and writable, where data is written to produce a computed hash digest on the readable side, or + * - Using the hash.update() and hash.digest() methods to produce the computed hash. + * + * The crypto.createHash() method is used to create Hash instances. Hash objects are not to be created directly using the new keyword. + */ +export class Hash extends Transform { + #context: number; + + constructor( + algorithm: string | number, + _opts?: TransformOptions, + ) { + super({ + transform(chunk: string, _encoding: string, callback: () => void) { + ops.op_node_hash_update(context, coerceToBytes(chunk)); + callback(); + }, + flush(callback: () => void) { + this.push(context.digest(undefined)); + callback(); + }, + }); + + if (typeof algorithm === "string") { + this.#context = ops.op_node_create_hash( + algorithm, + ); + } else { + this.#context = algorithm; + } + + const context = this.#context; + } + + copy(): Hash { + return new Hash(ops.op_node_clone_hash(this.#context)); + } + + /** + * Updates the hash content with the given data. + */ + update(data: string | ArrayBuffer, _encoding?: string): this { + let bytes; + if (typeof data === "string") { + data = new TextEncoder().encode(data); + bytes = coerceToBytes(data); + } else { + bytes = coerceToBytes(data); + } + + ops.op_node_hash_update(this.#context, bytes); + + return this; + } + + /** + * Calculates the digest of all of the data. + * + * If encoding is provided a string will be returned; otherwise a Buffer is returned. + * + * Supported encodings are currently 'hex', 'binary', 'base64', 'base64url'. + */ + digest(encoding?: string): Buffer | string { + const digest = this.#context.digest(undefined); + if (encoding === undefined) { + return Buffer.from(digest); + } + + switch (encoding) { + case "hex": + return new TextDecoder().decode(encodeToHex(new Uint8Array(digest))); + case "binary": + return String.fromCharCode(...digest); + case "base64": + return encodeToBase64(digest); + case "base64url": + return encodeToBase64Url(digest); + case "buffer": + return Buffer.from(digest); + default: + return Buffer.from(digest).toString(encoding); + } + } +} + +export function Hmac( + hmac: string, + key: string | ArrayBuffer | KeyObject, + options?: TransformOptions, +): Hmac { + return new HmacImpl(hmac, key, options); +} + +type Hmac = HmacImpl; + +class HmacImpl extends Transform { + #ipad: Uint8Array; + #opad: Uint8Array; + #ZEROES = Buffer.alloc(128); + #algorithm: string; + #hash: Hash; + + constructor( + hmac: string, + key: string | ArrayBuffer | KeyObject, + options?: TransformOptions, + ) { + super({ + transform(chunk: string, encoding: string, callback: () => void) { + // deno-lint-ignore no-explicit-any + self.update(coerceToBytes(chunk), encoding as any); + callback(); + }, + flush(callback: () => void) { + this.push(self.digest()); + callback(); + }, + }); + // deno-lint-ignore no-this-alias + const self = this; + if (key instanceof KeyObject) { + notImplemented("Hmac: KeyObject key is not implemented"); + } + + validateString(hmac, "hmac"); + const u8Key = prepareSecretKey(key, options?.encoding) as Buffer; + + const alg = hmac.toLowerCase(); + this.#hash = new Hash(alg, options); + this.#algorithm = alg; + const blockSize = (alg === "sha512" || alg === "sha384") ? 128 : 64; + const keySize = u8Key.length; + + let bufKey: Buffer; + + if (keySize > blockSize) { + bufKey = this.#hash.update(u8Key).digest() as Buffer; + } else { + bufKey = Buffer.concat([u8Key, this.#ZEROES], blockSize); + } + + this.#ipad = Buffer.allocUnsafe(blockSize); + this.#opad = Buffer.allocUnsafe(blockSize); + + for (let i = 0; i < blockSize; i++) { + this.#ipad[i] = bufKey[i] ^ 0x36; + this.#opad[i] = bufKey[i] ^ 0x5C; + } + + this.#hash = new Hash(alg); + this.#hash.update(this.#ipad); + } + + digest(): Buffer; + digest(encoding: BinaryToTextEncoding): string; + digest(encoding?: BinaryToTextEncoding): Buffer | string { + const result = this.#hash.digest(); + + return new Hash(this.#algorithm).update(this.#opad).update(result).digest( + encoding, + ); + } + + update(data: string | ArrayBuffer, inputEncoding?: Encoding): this { + this.#hash.update(data, inputEncoding); + return this; + } +} + +Hmac.prototype = HmacImpl.prototype; + +/** + * Creates and returns a Hash object that can be used to generate hash digests + * using the given `algorithm`. Optional `options` argument controls stream behavior. + */ +export function createHash(algorithm: string, opts?: TransformOptions) { + return new Hash(algorithm, opts); +} + +export default { + Hash, + Hmac, + createHash, +}; diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts new file mode 100644 index 000000000..aebdd9152 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/hkdf.ts @@ -0,0 +1,130 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + validateFunction, + validateInteger, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE, + hideStackFrames, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + toBuf, + validateByteSource, +} from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import { + createSecretKey, + isKeyObject, + KeyObject, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { kMaxLength } from "internal:deno_node/polyfills/internal/buffer.mjs"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +const validateParameters = hideStackFrames((hash, key, salt, info, length) => { + key = prepareKey(key); + salt = toBuf(salt); + info = toBuf(info); + + validateString(hash, "digest"); + validateByteSource(salt, "salt"); + validateByteSource(info, "info"); + + validateInteger(length, "length", 0, kMaxLength); + + if (info.byteLength > 1024) { + throw new ERR_OUT_OF_RANGE( + "info", + "must not contain more than 1024 bytes", + info.byteLength, + ); + } + + return { + hash, + key, + salt, + info, + length, + }; +}); + +function prepareKey(key: BinaryLike | KeyObject) { + if (isKeyObject(key)) { + return key; + } + + if (isAnyArrayBuffer(key)) { + return createSecretKey(new Uint8Array(key as unknown as ArrayBufferLike)); + } + + key = toBuf(key as string); + + if (!isArrayBufferView(key)) { + throw new ERR_INVALID_ARG_TYPE( + "ikm", + [ + "string", + "SecretKeyObject", + "ArrayBuffer", + "TypedArray", + "DataView", + "Buffer", + ], + key, + ); + } + + return createSecretKey(key); +} + +export function hkdf( + hash: string, + key: BinaryLike | KeyObject, + salt: BinaryLike, + info: BinaryLike, + length: number, + callback: (err: Error | null, derivedKey: ArrayBuffer) => void, +) { + ({ hash, key, salt, info, length } = validateParameters( + hash, + key, + salt, + info, + length, + )); + + validateFunction(callback, "callback"); + + notImplemented("crypto.hkdf"); +} + +export function hkdfSync( + hash: string, + key: BinaryLike | KeyObject, + salt: BinaryLike, + info: BinaryLike, + length: number, +) { + ({ hash, key, salt, info, length } = validateParameters( + hash, + key, + salt, + info, + length, + )); + + notImplemented("crypto.hkdfSync"); +} + +export default { + hkdf, + hkdfSync, +}; diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts new file mode 100644 index 000000000..1a947b95b --- /dev/null +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -0,0 +1,682 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + KeyFormat, + KeyType, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export function generateKey( + _type: "hmac" | "aes", + _options: { + length: number; + }, + _callback: (err: Error | null, key: KeyObject) => void, +) { + notImplemented("crypto.generateKey"); +} + +export interface BasePrivateKeyEncodingOptions<T extends KeyFormat> { + format: T; + cipher?: string | undefined; + passphrase?: string | undefined; +} + +export interface RSAKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + publicKeyEncoding: { + type: "pkcs1" | "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs1" | "pkcs8"; + }; +} + +export interface RSAPSSKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + /** + * Name of the message digest + */ + hashAlgorithm?: string; + /** + * Name of the message digest used by MGF1 + */ + mgf1HashAlgorithm?: string; + /** + * Minimal salt length in bytes + */ + saltLength?: string; + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface DSAKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Size of q in bits + */ + divisorLength: number; + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface ECKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Name of the curve to use. + */ + namedCurve: string; + publicKeyEncoding: { + type: "pkcs1" | "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "sec1" | "pkcs8"; + }; +} + +export interface ED25519KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface ED448KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface X25519KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface X448KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions<PrivF> & { + type: "pkcs8"; + }; +} + +export interface RSAKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; +} + +export interface RSAPSSKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + /** + * Name of the message digest + */ + hashAlgorithm?: string; + /** + * Name of the message digest used by MGF1 + */ + mgf1HashAlgorithm?: string; + /** + * Minimal salt length in bytes + */ + saltLength?: string; +} + +export interface DSAKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Size of q in bits + */ + divisorLength: number; +} + +// deno-lint-ignore no-empty-interface +export interface ED25519KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface ED448KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface X25519KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface X448KeyPairKeyObjectOptions {} + +export interface ECKeyPairKeyObjectOptions { + /** + * Name of the curve to use + */ + namedCurve: string; +} + +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + _type: KeyType, + _options: unknown, + _callback: ( + err: Error | null, + // deno-lint-ignore no-explicit-any + publicKey: any, + // deno-lint-ignore no-explicit-any + privateKey: any, + ) => void, +) { + notImplemented("crypto.generateKeyPair"); +} + +export interface KeyPairKeyObjectResult { + publicKey: KeyObject; + privateKey: KeyObject; +} + +export interface KeyPairSyncResult< + T1 extends string | Buffer, + T2 extends string | Buffer, +> { + publicKey: T1; + privateKey: T2; +} + +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "ed25519", + options?: ED25519KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "ed448", + options?: ED448KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "x25519", + options?: X25519KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult<string, string>; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"pem", "der">, +): KeyPairSyncResult<string, Buffer>; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"der", "pem">, +): KeyPairSyncResult<Buffer, string>; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"der", "der">, +): KeyPairSyncResult<Buffer, Buffer>; +export function generateKeyPairSync( + type: "x448", + options?: X448KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + _type: KeyType, + _options: unknown, +): + | KeyPairKeyObjectResult + | KeyPairSyncResult<string | Buffer, string | Buffer> { + notImplemented("crypto.generateKeyPairSync"); +} + +export function generateKeySync( + _type: "hmac" | "aes", + _options: { + length: number; + }, +): KeyObject { + notImplemented("crypto.generateKeySync"); +} + +export default { + generateKey, + generateKeySync, + generateKeyPair, + generateKeyPairSync, +}; diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts new file mode 100644 index 000000000..7c9e7bad9 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -0,0 +1,294 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + kHandle, + kKeyObject, +} from "internal:deno_node/polyfills/internal/crypto/constants.ts"; +import { + ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import type { + KeyFormat, + KeyType, + PrivateKeyInput, + PublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { hideStackFrames } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + isCryptoKey as isCryptoKey_, + isKeyObject as isKeyObject_, + kKeyType, +} from "internal:deno_node/polyfills/internal/crypto/_keys.ts"; + +const getArrayBufferOrView = hideStackFrames( + ( + buffer, + name, + encoding, + ): + | ArrayBuffer + | SharedArrayBuffer + | Buffer + | DataView + | BigInt64Array + | BigUint64Array + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array => { + if (isAnyArrayBuffer(buffer)) { + return buffer; + } + if (typeof buffer === "string") { + if (encoding === "buffer") { + encoding = "utf8"; + } + return Buffer.from(buffer, encoding); + } + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + name, + [ + "string", + "ArrayBuffer", + "Buffer", + "TypedArray", + "DataView", + ], + buffer, + ); + } + return buffer; + }, +); + +export interface AsymmetricKeyDetails { + /** + * Key size in bits (RSA, DSA). + */ + modulusLength?: number | undefined; + /** + * Public exponent (RSA). + */ + publicExponent?: bigint | undefined; + /** + * Name of the message digest (RSA-PSS). + */ + hashAlgorithm?: string | undefined; + /** + * Name of the message digest used by MGF1 (RSA-PSS). + */ + mgf1HashAlgorithm?: string | undefined; + /** + * Minimal salt length in bytes (RSA-PSS). + */ + saltLength?: number | undefined; + /** + * Size of q in bits (DSA). + */ + divisorLength?: number | undefined; + /** + * Name of the curve (EC). + */ + namedCurve?: string | undefined; +} + +export type KeyObjectType = "secret" | "public" | "private"; + +export interface KeyExportOptions<T extends KeyFormat> { + type: "pkcs1" | "spki" | "pkcs8" | "sec1"; + format: T; + cipher?: string | undefined; + passphrase?: string | Buffer | undefined; +} + +export interface JwkKeyExportOptions { + format: "jwk"; +} + +export function isKeyObject(obj: unknown): obj is KeyObject { + return isKeyObject_(obj); +} + +export function isCryptoKey( + obj: unknown, +): obj is { type: string; [kKeyObject]: KeyObject } { + return isCryptoKey_(obj); +} + +export class KeyObject { + [kKeyType]: KeyObjectType; + [kHandle]: unknown; + + constructor(type: KeyObjectType, handle: unknown) { + if (type !== "secret" && type !== "public" && type !== "private") { + throw new ERR_INVALID_ARG_VALUE("type", type); + } + + if (typeof handle !== "object") { + throw new ERR_INVALID_ARG_TYPE("handle", "object", handle); + } + + this[kKeyType] = type; + + Object.defineProperty(this, kHandle, { + value: handle, + enumerable: false, + configurable: false, + writable: false, + }); + } + + get type(): KeyObjectType { + return this[kKeyType]; + } + + get asymmetricKeyDetails(): AsymmetricKeyDetails | undefined { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyDetails"); + + return undefined; + } + + get asymmetricKeyType(): KeyType | undefined { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyType"); + + return undefined; + } + + get symmetricKeySize(): number | undefined { + notImplemented("crypto.KeyObject.prototype.symmetricKeySize"); + + return undefined; + } + + static from(key: CryptoKey): KeyObject { + if (!isCryptoKey(key)) { + throw new ERR_INVALID_ARG_TYPE("key", "CryptoKey", key); + } + + notImplemented("crypto.KeyObject.prototype.from"); + } + + equals(otherKeyObject: KeyObject): boolean { + if (!isKeyObject(otherKeyObject)) { + throw new ERR_INVALID_ARG_TYPE( + "otherKeyObject", + "KeyObject", + otherKeyObject, + ); + } + + notImplemented("crypto.KeyObject.prototype.equals"); + } + + export(options: KeyExportOptions<"pem">): string | Buffer; + export(options?: KeyExportOptions<"der">): Buffer; + export(options?: JwkKeyExportOptions): JsonWebKey; + export(_options?: unknown): string | Buffer | JsonWebKey { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyType"); + } +} + +export interface JsonWebKeyInput { + key: JsonWebKey; + format: "jwk"; +} + +export function createPrivateKey( + _key: PrivateKeyInput | string | Buffer | JsonWebKeyInput, +): KeyObject { + notImplemented("crypto.createPrivateKey"); +} + +export function createPublicKey( + _key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput, +): KeyObject { + notImplemented("crypto.createPublicKey"); +} + +function getKeyTypes(allowKeyObject: boolean, bufferOnly = false) { + const types = [ + "ArrayBuffer", + "Buffer", + "TypedArray", + "DataView", + "string", // Only if bufferOnly == false + "KeyObject", // Only if allowKeyObject == true && bufferOnly == false + "CryptoKey", // Only if allowKeyObject == true && bufferOnly == false + ]; + if (bufferOnly) { + return types.slice(0, 4); + } else if (!allowKeyObject) { + return types.slice(0, 5); + } + return types; +} + +export function prepareSecretKey( + key: string | ArrayBuffer | KeyObject, + encoding: string | undefined, + bufferOnly = false, +) { + if (!bufferOnly) { + if (isKeyObject(key)) { + if (key.type !== "secret") { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, "secret"); + } + return key[kHandle]; + } else if (isCryptoKey(key)) { + if (key.type !== "secret") { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, "secret"); + } + return key[kKeyObject][kHandle]; + } + } + if ( + typeof key !== "string" && + !isArrayBufferView(key) && + !isAnyArrayBuffer(key) + ) { + throw new ERR_INVALID_ARG_TYPE( + "key", + getKeyTypes(!bufferOnly, bufferOnly), + key, + ); + } + + return getArrayBufferOrView(key, "key", encoding); +} + +export function createSecretKey(key: ArrayBufferView): KeyObject; +export function createSecretKey( + key: string, + encoding: string, +): KeyObject; +export function createSecretKey( + _key: string | ArrayBufferView, + _encoding?: string, +): KeyObject { + notImplemented("crypto.createSecretKey"); +} + +export default { + createPrivateKey, + createPublicKey, + createSecretKey, + isKeyObject, + isCryptoKey, + KeyObject, + prepareSecretKey, +}; diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts new file mode 100644 index 000000000..a3d821ae7 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts @@ -0,0 +1,183 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import { HASH_DATA } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export const MAX_ALLOC = Math.pow(2, 30) - 1; + +export type NormalizedAlgorithms = + | "md5" + | "ripemd160" + | "sha1" + | "sha224" + | "sha256" + | "sha384" + | "sha512"; + +export type Algorithms = + | "md5" + | "ripemd160" + | "rmd160" + | "sha1" + | "sha224" + | "sha256" + | "sha384" + | "sha512"; + +const createHasher = (algorithm: string) => (value: Uint8Array) => + Buffer.from(createHash(algorithm).update(value).digest() as Buffer); + +function getZeroes(zeros: number) { + return Buffer.alloc(zeros); +} + +const sizes = { + md5: 16, + sha1: 20, + sha224: 28, + sha256: 32, + sha384: 48, + sha512: 64, + rmd160: 20, + ripemd160: 20, +}; + +function toBuffer(bufferable: HASH_DATA) { + if (bufferable instanceof Uint8Array || typeof bufferable === "string") { + return Buffer.from(bufferable as Uint8Array); + } else { + return Buffer.from(bufferable.buffer); + } +} + +export class Hmac { + hash: (value: Uint8Array) => Buffer; + ipad1: Buffer; + opad: Buffer; + alg: string; + blocksize: number; + size: number; + ipad2: Buffer; + + constructor(alg: Algorithms, key: Buffer, saltLen: number) { + this.hash = createHasher(alg); + + const blocksize = alg === "sha512" || alg === "sha384" ? 128 : 64; + + if (key.length > blocksize) { + key = this.hash(key); + } else if (key.length < blocksize) { + key = Buffer.concat([key, getZeroes(blocksize - key.length)], blocksize); + } + + const ipad = Buffer.allocUnsafe(blocksize + sizes[alg]); + const opad = Buffer.allocUnsafe(blocksize + sizes[alg]); + for (let i = 0; i < blocksize; i++) { + ipad[i] = key[i] ^ 0x36; + opad[i] = key[i] ^ 0x5c; + } + + const ipad1 = Buffer.allocUnsafe(blocksize + saltLen + 4); + ipad.copy(ipad1, 0, 0, blocksize); + + this.ipad1 = ipad1; + this.ipad2 = ipad; + this.opad = opad; + this.alg = alg; + this.blocksize = blocksize; + this.size = sizes[alg]; + } + + run(data: Buffer, ipad: Buffer) { + data.copy(ipad, this.blocksize); + const h = this.hash(ipad); + h.copy(this.opad, this.blocksize); + return this.hash(this.opad); + } +} + +/** + * @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: Algorithms = "sha1", +): 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"); + } + + const bufferedPassword = toBuffer(password); + const bufferedSalt = toBuffer(salt); + + const hmac = new Hmac(digest, bufferedPassword, bufferedSalt.length); + + const DK = Buffer.allocUnsafe(keylen); + const block1 = Buffer.allocUnsafe(bufferedSalt.length + 4); + bufferedSalt.copy(block1, 0, 0, bufferedSalt.length); + + let destPos = 0; + const hLen = sizes[digest]; + const l = Math.ceil(keylen / hLen); + + for (let i = 1; i <= l; i++) { + block1.writeUInt32BE(i, bufferedSalt.length); + + const T = hmac.run(block1, hmac.ipad1); + let U = T; + + for (let j = 1; j < iterations; j++) { + U = hmac.run(U, hmac.ipad2); + for (let k = 0; k < hLen; k++) T[k] ^= U[k]; + } + + T.copy(DK, destPos); + destPos += hLen; + } + + return 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: Algorithms = "sha1", + callback: (err: Error | null, derivedKey?: Buffer) => void, +) { + setTimeout(() => { + let err = null, + res; + try { + res = pbkdf2Sync(password, salt, iterations, keylen, digest); + } catch (e) { + err = e; + } + if (err) { + callback(err instanceof Error ? err : new Error("[non-error thrown]")); + } else { + callback(null, res); + } + }, 0); +} + +export default { + Hmac, + MAX_ALLOC, + pbkdf2, + pbkdf2Sync, +}; diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts new file mode 100644 index 000000000..158ea40da --- /dev/null +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -0,0 +1,140 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import randomBytes from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +import randomFill, { + randomFillSync, +} from "internal:deno_node/polyfills/internal/crypto/_randomFill.ts"; +import randomInt from "internal:deno_node/polyfills/internal/crypto/_randomInt.ts"; + +export { default as randomBytes } from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +export { + default as randomFill, + randomFillSync, +} from "internal:deno_node/polyfills/internal/crypto/_randomFill.ts"; +export { default as randomInt } from "internal:deno_node/polyfills/internal/crypto/_randomInt.ts"; + +export type LargeNumberLike = + | ArrayBufferView + | SharedArrayBuffer + | ArrayBuffer + | bigint; + +export interface CheckPrimeOptions { + /** + * The number of Miller-Rabin probabilistic primality iterations to perform. + * When the value is 0 (zero), a number of checks is used that yields a false positive rate of at most 2-64 for random input. + * Care must be used when selecting a number of checks. + * Refer to the OpenSSL documentation for the BN_is_prime_ex function nchecks options for more details. + * + * @default 0 + */ + checks?: number | undefined; +} + +export function checkPrime( + candidate: LargeNumberLike, + callback: (err: Error | null, result: boolean) => void, +): void; +export function checkPrime( + candidate: LargeNumberLike, + options: CheckPrimeOptions, + callback: (err: Error | null, result: boolean) => void, +): void; +export function checkPrime( + _candidate: LargeNumberLike, + _options?: CheckPrimeOptions | ((err: Error | null, result: boolean) => void), + _callback?: (err: Error | null, result: boolean) => void, +) { + notImplemented("crypto.checkPrime"); +} + +export function checkPrimeSync( + _candidate: LargeNumberLike, + _options?: CheckPrimeOptions, +): boolean { + notImplemented("crypto.checkPrimeSync"); +} + +export interface GeneratePrimeOptions { + add?: LargeNumberLike | undefined; + rem?: LargeNumberLike | undefined; + /** + * @default false + */ + safe?: boolean | undefined; + bigint?: boolean | undefined; +} + +export interface GeneratePrimeOptionsBigInt extends GeneratePrimeOptions { + bigint: true; +} + +export interface GeneratePrimeOptionsArrayBuffer extends GeneratePrimeOptions { + bigint?: false | undefined; +} + +export function generatePrime( + size: number, + callback: (err: Error | null, prime: ArrayBuffer) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptionsBigInt, + callback: (err: Error | null, prime: bigint) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptionsArrayBuffer, + callback: (err: Error | null, prime: ArrayBuffer) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptions, + callback: (err: Error | null, prime: ArrayBuffer | bigint) => void, +): void; +export function generatePrime( + _size: number, + _options?: unknown, + _callback?: unknown, +) { + notImplemented("crypto.generatePrime"); +} + +export function generatePrimeSync(size: number): ArrayBuffer; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptionsBigInt, +): bigint; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptionsArrayBuffer, +): ArrayBuffer; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptions, +): ArrayBuffer | bigint; +export function generatePrimeSync( + _size: number, + _options?: + | GeneratePrimeOptionsBigInt + | GeneratePrimeOptionsArrayBuffer + | GeneratePrimeOptions, +): ArrayBuffer | bigint { + notImplemented("crypto.generatePrimeSync"); +} + +export const randomUUID = () => globalThis.crypto.randomUUID(); + +export default { + checkPrime, + checkPrimeSync, + generatePrime, + generatePrimeSync, + randomUUID, + randomInt, + randomBytes, + randomFill, + randomFillSync, +}; diff --git a/ext/node/polyfills/internal/crypto/scrypt.ts b/ext/node/polyfills/internal/crypto/scrypt.ts new file mode 100644 index 000000000..1bdafb63d --- /dev/null +++ b/ext/node/polyfills/internal/crypto/scrypt.ts @@ -0,0 +1,278 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/* +MIT License + +Copyright (c) 2018 cryptocoinjs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { pbkdf2Sync as pbkdf2 } from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; +import { HASH_DATA } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +type Opts = Partial<{ + N: number; + cost: number; + p: number; + parallelization: number; + r: number; + blockSize: number; + maxmem: number; +}>; + +const fixOpts = (opts?: Opts) => { + const out = { N: 16384, p: 1, r: 8, maxmem: 32 << 20 }; + if (!opts) return out; + + if (opts.N) out.N = opts.N; + else if (opts.cost) out.N = opts.cost; + + if (opts.p) out.p = opts.p; + else if (opts.parallelization) out.p = opts.parallelization; + + if (opts.r) out.r = opts.r; + else if (opts.blockSize) out.r = opts.blockSize; + + if (opts.maxmem) out.maxmem = opts.maxmem; + + return out; +}; + +function blockxor(S: Buffer, Si: number, D: Buffer, Di: number, len: number) { + let i = -1; + while (++i < len) D[Di + i] ^= S[Si + i]; +} +function arraycopy( + src: Buffer, + srcPos: number, + dest: Buffer, + destPos: number, + length: number, +) { + src.copy(dest, destPos, srcPos, srcPos + length); +} + +const R = (a: number, b: number) => (a << b) | (a >>> (32 - b)); + +class ScryptRom { + B: Buffer; + r: number; + N: number; + p: number; + XY: Buffer; + V: Buffer; + B32: Int32Array; + x: Int32Array; + _X: Buffer; + constructor(b: Buffer, r: number, N: number, p: number) { + this.B = b; + this.r = r; + this.N = N; + this.p = p; + this.XY = Buffer.allocUnsafe(256 * r); + this.V = Buffer.allocUnsafe(128 * r * N); + this.B32 = new Int32Array(16); // salsa20_8 + this.x = new Int32Array(16); // salsa20_8 + this._X = Buffer.allocUnsafe(64); // blockmix_salsa8 + } + + run() { + const p = this.p | 0; + const r = this.r | 0; + for (let i = 0; i < p; i++) this.scryptROMix(i, r); + + return this.B; + } + + scryptROMix(i: number, r: number) { + const blockStart = i * 128 * r; + const offset = (2 * r - 1) * 64; + const blockLen = 128 * r; + const B = this.B; + const N = this.N | 0; + const V = this.V; + const XY = this.XY; + B.copy(XY, 0, blockStart, blockStart + blockLen); + for (let i1 = 0; i1 < N; i1++) { + XY.copy(V, i1 * blockLen, 0, blockLen); + this.blockmix_salsa8(blockLen); + } + + let j: number; + for (let i2 = 0; i2 < N; i2++) { + j = XY.readUInt32LE(offset) & (N - 1); + blockxor(V, j * blockLen, XY, 0, blockLen); + this.blockmix_salsa8(blockLen); + } + XY.copy(B, blockStart, 0, blockLen); + } + + blockmix_salsa8(blockLen: number) { + const BY = this.XY; + const r = this.r; + const _X = this._X; + arraycopy(BY, (2 * r - 1) * 64, _X, 0, 64); + let i; + for (i = 0; i < 2 * r; i++) { + blockxor(BY, i * 64, _X, 0, 64); + this.salsa20_8(); + arraycopy(_X, 0, BY, blockLen + i * 64, 64); + } + for (i = 0; i < r; i++) { + arraycopy(BY, blockLen + i * 2 * 64, BY, i * 64, 64); + arraycopy(BY, blockLen + (i * 2 + 1) * 64, BY, (i + r) * 64, 64); + } + } + + salsa20_8() { + const B32 = this.B32; + const B = this._X; + const x = this.x; + + let i; + for (i = 0; i < 16; i++) { + B32[i] = (B[i * 4 + 0] & 0xff) << 0; + B32[i] |= (B[i * 4 + 1] & 0xff) << 8; + B32[i] |= (B[i * 4 + 2] & 0xff) << 16; + B32[i] |= (B[i * 4 + 3] & 0xff) << 24; + } + + for (i = 0; i < 16; i++) x[i] = B32[i]; + + for (i = 0; i < 4; i++) { + x[4] ^= R(x[0] + x[12], 7); + x[8] ^= R(x[4] + x[0], 9); + x[12] ^= R(x[8] + x[4], 13); + x[0] ^= R(x[12] + x[8], 18); + x[9] ^= R(x[5] + x[1], 7); + x[13] ^= R(x[9] + x[5], 9); + x[1] ^= R(x[13] + x[9], 13); + x[5] ^= R(x[1] + x[13], 18); + x[14] ^= R(x[10] + x[6], 7); + x[2] ^= R(x[14] + x[10], 9); + x[6] ^= R(x[2] + x[14], 13); + x[10] ^= R(x[6] + x[2], 18); + x[3] ^= R(x[15] + x[11], 7); + x[7] ^= R(x[3] + x[15], 9); + x[11] ^= R(x[7] + x[3], 13); + x[15] ^= R(x[11] + x[7], 18); + x[1] ^= R(x[0] + x[3], 7); + x[2] ^= R(x[1] + x[0], 9); + x[3] ^= R(x[2] + x[1], 13); + x[0] ^= R(x[3] + x[2], 18); + x[6] ^= R(x[5] + x[4], 7); + x[7] ^= R(x[6] + x[5], 9); + x[4] ^= R(x[7] + x[6], 13); + x[5] ^= R(x[4] + x[7], 18); + x[11] ^= R(x[10] + x[9], 7); + x[8] ^= R(x[11] + x[10], 9); + x[9] ^= R(x[8] + x[11], 13); + x[10] ^= R(x[9] + x[8], 18); + x[12] ^= R(x[15] + x[14], 7); + x[13] ^= R(x[12] + x[15], 9); + x[14] ^= R(x[13] + x[12], 13); + x[15] ^= R(x[14] + x[13], 18); + } + for (i = 0; i < 16; i++) B32[i] += x[i]; + + let bi; + + for (i = 0; i < 16; i++) { + bi = i * 4; + B[bi + 0] = (B32[i] >> 0) & 0xff; + B[bi + 1] = (B32[i] >> 8) & 0xff; + B[bi + 2] = (B32[i] >> 16) & 0xff; + B[bi + 3] = (B32[i] >> 24) & 0xff; + } + } + + clean() { + this.XY.fill(0); + this.V.fill(0); + this._X.fill(0); + this.B.fill(0); + for (let i = 0; i < 16; i++) { + this.B32[i] = 0; + this.x[i] = 0; + } + } +} + +export function scryptSync( + password: HASH_DATA, + salt: HASH_DATA, + keylen: number, + _opts?: Opts, +): Buffer { + const { N, r, p, maxmem } = fixOpts(_opts); + + const blen = p * 128 * r; + + if (32 * r * (N + 2) * 4 + blen > maxmem) { + throw new Error("excedes max memory"); + } + + const b = pbkdf2(password, salt, 1, blen, "sha256"); + + const scryptRom = new ScryptRom(b, r, N, p); + const out = scryptRom.run(); + + const fin = pbkdf2(password, out, 1, keylen, "sha256"); + scryptRom.clean(); + return fin; +} + +type Callback = (err: unknown, result?: Buffer) => void; + +export function scrypt( + password: HASH_DATA, + salt: HASH_DATA, + keylen: number, + _opts: Opts | null | Callback, + cb?: Callback, +) { + if (!cb) { + cb = _opts as Callback; + _opts = null; + } + const { N, r, p, maxmem } = fixOpts(_opts as Opts); + + const blen = p * 128 * r; + if (32 * r * (N + 2) * 4 + blen > maxmem) { + throw new Error("excedes max memory"); + } + + try { + const b = pbkdf2(password, salt, 1, blen, "sha256"); + + const scryptRom = new ScryptRom(b, r, N, p); + const out = scryptRom.run(); + const result = pbkdf2(password, out, 1, keylen, "sha256"); + scryptRom.clean(); + cb(null, result); + } catch (err: unknown) { + return cb(err); + } +} + +export default { + scrypt, + scryptSync, +}; diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts new file mode 100644 index 000000000..6c163c8e5 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -0,0 +1,148 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import type { WritableOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import Writable from "internal:deno_node/polyfills/internal/streams/writable.mjs"; +import type { + BinaryLike, + BinaryToTextEncoding, + Encoding, + PrivateKeyInput, + PublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; + +export type DSAEncoding = "der" | "ieee-p1363"; + +export interface SigningOptions { + padding?: number | undefined; + saltLength?: number | undefined; + dsaEncoding?: DSAEncoding | undefined; +} + +export interface SignPrivateKeyInput extends PrivateKeyInput, SigningOptions {} + +export interface SignKeyObjectInput extends SigningOptions { + key: KeyObject; +} +export interface VerifyPublicKeyInput extends PublicKeyInput, SigningOptions {} + +export interface VerifyKeyObjectInput extends SigningOptions { + key: KeyObject; +} + +export type KeyLike = string | Buffer | KeyObject; + +export class Sign extends Writable { + constructor(algorithm: string, _options?: WritableOptions) { + validateString(algorithm, "algorithm"); + + super(); + + notImplemented("crypto.Sign"); + } + + sign(privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput): Buffer; + sign( + privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + outputFormat: BinaryToTextEncoding, + ): string; + sign( + _privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.Sign.prototype.sign"); + } + + update(data: BinaryLike): this; + update(data: string, inputEncoding: Encoding): this; + update(_data: BinaryLike | string, _inputEncoding?: Encoding): this { + notImplemented("crypto.Sign.prototype.update"); + } +} + +export class Verify extends Writable { + constructor(algorithm: string, _options?: WritableOptions) { + validateString(algorithm, "algorithm"); + + super(); + + notImplemented("crypto.Verify"); + } + + update(data: BinaryLike): this; + update(data: string, inputEncoding: Encoding): this; + update(_data: BinaryLike, _inputEncoding?: string): this { + notImplemented("crypto.Sign.prototype.update"); + } + + verify( + object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, + ): boolean; + verify( + object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: string, + signatureEncoding?: BinaryToTextEncoding, + ): boolean; + verify( + _object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + _signature: ArrayBufferView | string, + _signatureEncoding?: BinaryToTextEncoding, + ): boolean { + notImplemented("crypto.Sign.prototype.sign"); + } +} + +export function signOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, +): Buffer; +export function signOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + callback: (error: Error | null, data: Buffer) => void, +): void; +export function signOneShot( + _algorithm: string | null | undefined, + _data: ArrayBufferView, + _key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + _callback?: (error: Error | null, data: Buffer) => void, +): Buffer | void { + notImplemented("crypto.sign"); +} + +export function verifyOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, +): boolean; +export function verifyOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, + callback: (error: Error | null, result: boolean) => void, +): void; +export function verifyOneShot( + _algorithm: string | null | undefined, + _data: ArrayBufferView, + _key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + _signature: ArrayBufferView, + _callback?: (error: Error | null, result: boolean) => void, +): boolean | void { + notImplemented("crypto.verify"); +} + +export default { + signOneShot, + verifyOneShot, + Sign, + Verify, +}; diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts new file mode 100644 index 000000000..3bb9ec160 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/types.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export type HASH_DATA = string | ArrayBufferView | Buffer; + +export type BinaryToTextEncoding = "base64" | "base64url" | "hex" | "binary"; + +export type CharacterEncoding = "utf8" | "utf-8" | "utf16le" | "latin1"; + +export type LegacyCharacterEncoding = "ascii" | "binary" | "ucs2" | "ucs-2"; + +export type Encoding = + | BinaryToTextEncoding + | CharacterEncoding + | LegacyCharacterEncoding; + +export type ECDHKeyFormat = "compressed" | "uncompressed" | "hybrid"; + +export type BinaryLike = string | ArrayBufferView; + +export type KeyFormat = "pem" | "der"; + +export type KeyType = + | "rsa" + | "rsa-pss" + | "dsa" + | "ec" + | "ed25519" + | "ed448" + | "x25519" + | "x448"; + +export interface PrivateKeyInput { + key: string | Buffer; + format?: KeyFormat | undefined; + type?: "pkcs1" | "pkcs8" | "sec1" | undefined; + passphrase?: string | Buffer | undefined; +} + +export interface PublicKeyInput { + key: string | Buffer; + format?: KeyFormat | undefined; + type?: "pkcs1" | "spki" | undefined; +} diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts new file mode 100644 index 000000000..f9fce8b2d --- /dev/null +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -0,0 +1,129 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { getCiphers } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + ERR_INVALID_ARG_TYPE, + hideStackFrames, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { crypto as constants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { + kHandle, + kKeyObject, +} from "internal:deno_node/polyfills/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", +]; + +let defaultEncoding = "buffer"; + +export function setDefaultEncoding(val: string) { + defaultEncoding = val; +} + +export function getDefaultEncoding(): string { + return defaultEncoding; +} + +// This is here because many functions accepted binary strings without +// any explicit encoding in older versions of node, and we don't want +// to break them unnecessarily. +export function toBuf(val: string | Buffer, encoding?: string): Buffer { + if (typeof val === "string") { + if (encoding === "buffer") { + encoding = "utf8"; + } + + return Buffer.from(val, encoding); + } + + return val; +} + +export const validateByteSource = hideStackFrames((val, name) => { + val = toBuf(val); + + if (isAnyArrayBuffer(val) || isArrayBufferView(val)) { + return; + } + + throw new ERR_INVALID_ARG_TYPE( + name, + ["string", "ArrayBuffer", "TypedArray", "DataView", "Buffer"], + val, + ); +}); + +/** + * Returns an array of the names of the supported hash algorithms, such as 'sha1'. + */ +export function getHashes(): readonly string[] { + return digestAlgorithms; +} + +export function getCurves(): readonly string[] { + notImplemented("crypto.getCurves"); +} + +export interface SecureHeapUsage { + total: number; + min: number; + used: number; + utilization: number; +} + +export function secureHeapUsed(): SecureHeapUsage { + notImplemented("crypto.secureHeapUsed"); +} + +export function setEngine(_engine: string, _flags: typeof constants) { + notImplemented("crypto.setEngine"); +} + +export { getCiphers, kHandle, kKeyObject }; + +export default { + getDefaultEncoding, + getHashes, + setDefaultEncoding, + getCiphers, + getCurves, + secureHeapUsed, + setEngine, + validateByteSource, + toBuf, + kHandle, + kKeyObject, +}; diff --git a/ext/node/polyfills/internal/crypto/x509.ts b/ext/node/polyfills/internal/crypto/x509.ts new file mode 100644 index 000000000..0722d7865 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/x509.ts @@ -0,0 +1,186 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +// deno-lint-ignore no-explicit-any +export type PeerCertificate = any; + +export interface X509CheckOptions { + /** + * @default 'always' + */ + subject: "always" | "never"; + /** + * @default true + */ + wildcards: boolean; + /** + * @default true + */ + partialWildcards: boolean; + /** + * @default false + */ + multiLabelWildcards: boolean; + /** + * @default false + */ + singleLabelSubdomains: boolean; +} + +export class X509Certificate { + constructor(buffer: BinaryLike) { + if (typeof buffer === "string") { + buffer = Buffer.from(buffer); + } + + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + "buffer", + ["string", "Buffer", "TypedArray", "DataView"], + buffer, + ); + } + + notImplemented("crypto.X509Certificate"); + } + + get ca(): boolean { + notImplemented("crypto.X509Certificate.prototype.ca"); + + return false; + } + + checkEmail( + _email: string, + _options?: Pick<X509CheckOptions, "subject">, + ): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkEmail"); + } + + checkHost(_name: string, _options?: X509CheckOptions): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkHost"); + } + + checkIP(_ip: string): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkIP"); + } + + checkIssued(_otherCert: X509Certificate): boolean { + notImplemented("crypto.X509Certificate.prototype.checkIssued"); + } + + checkPrivateKey(_privateKey: KeyObject): boolean { + notImplemented("crypto.X509Certificate.prototype.checkPrivateKey"); + } + + get fingerprint(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint"); + + return ""; + } + + get fingerprint256(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint256"); + + return ""; + } + + get fingerprint512(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint512"); + + return ""; + } + + get infoAccess(): string | undefined { + notImplemented("crypto.X509Certificate.prototype.infoAccess"); + + return ""; + } + + get issuer(): string { + notImplemented("crypto.X509Certificate.prototype.issuer"); + + return ""; + } + + get issuerCertificate(): X509Certificate | undefined { + notImplemented("crypto.X509Certificate.prototype.issuerCertificate"); + + return {} as X509Certificate; + } + + get keyUsage(): string[] { + notImplemented("crypto.X509Certificate.prototype.keyUsage"); + + return []; + } + + get publicKey(): KeyObject { + notImplemented("crypto.X509Certificate.prototype.publicKey"); + + return {} as KeyObject; + } + + get raw(): Buffer { + notImplemented("crypto.X509Certificate.prototype.raw"); + + return {} as Buffer; + } + + get serialNumber(): string { + notImplemented("crypto.X509Certificate.prototype.serialNumber"); + + return ""; + } + + get subject(): string { + notImplemented("crypto.X509Certificate.prototype.subject"); + + return ""; + } + + get subjectAltName(): string | undefined { + notImplemented("crypto.X509Certificate.prototype.subjectAltName"); + + return ""; + } + + toJSON(): string { + return this.toString(); + } + + toLegacyObject(): PeerCertificate { + notImplemented("crypto.X509Certificate.prototype.toLegacyObject"); + } + + toString(): string { + notImplemented("crypto.X509Certificate.prototype.toString"); + } + + get validFrom(): string { + notImplemented("crypto.X509Certificate.prototype.validFrom"); + + return ""; + } + + get validTo(): string { + notImplemented("crypto.X509Certificate.prototype.validTo"); + + return ""; + } + + verify(_publicKey: KeyObject): boolean { + notImplemented("crypto.X509Certificate.prototype.verify"); + } +} + +export default { + X509Certificate, +}; |