UNPKG

react-native-quick-crypto

Version:

A fast implementation of Node's `crypto` module written in C/C++ JSI

936 lines (856 loc) 26.6 kB
import { NativeQuickCrypto } from './NativeQuickCrypto/NativeQuickCrypto'; import Stream, { type TransformOptions } from 'readable-stream'; import { type BinaryLike, binaryLikeToArrayBuffer, type CipherEncoding, type Encoding, getDefaultEncoding, kEmptyObject, validateFunction, validateObject, validateString, validateUint32, validateInt32, type BinaryLikeNode, type CipherType, } from './Utils'; import { type InternalCipher, KeyVariant } from './NativeQuickCrypto/Cipher'; import type { CipherCCMOptions, CipherCCMTypes, CipherGCMTypes, CipherGCMOptions, CipherOCBOptions, CipherOCBTypes, DecipherGCM, DecipherOCB, DecipherCCM, CipherCCM, CipherOCB, CipherGCM, } from 'crypto'; // @types/node import { StringDecoder } from 'string_decoder'; import { Buffer } from '@craftzdog/react-native-buffer'; import { Buffer as SBuffer } from 'safe-buffer'; import { constants } from './constants'; import { CryptoKey, KeyEncoding, KFormatType, parsePrivateKeyEncoding, parsePublicKeyEncoding, preparePrivateKey, preparePublicOrPrivateKey, type CryptoKeyPair, type EncodingOptions, type KeyPairType, type NamedCurve, } from './keys'; import type { KeyObjectHandle } from './NativeQuickCrypto/webcrypto'; export enum ECCurve { OPENSSL_EC_EXPLICIT_CURVE, OPENSSL_EC_NAMED_CURVE, } // make sure that nextTick is there global.process.nextTick = setImmediate; const createInternalCipher = NativeQuickCrypto.createCipher; const createInternalDecipher = NativeQuickCrypto.createDecipher; const _publicEncrypt = NativeQuickCrypto.publicEncrypt; const _publicDecrypt = NativeQuickCrypto.publicDecrypt; const _privateDecrypt = NativeQuickCrypto.privateDecrypt; // eslint-disable-next-line @typescript-eslint/no-explicit-any function getUIntOption(options: Record<string, any>, key: string) { let value; if (options && (value = options[key]) != null) { // >>> Turns any type into a positive integer (also sets the sign bit to 0) if (value >>> 0 !== value) throw new Error(`options.${key}: ${value}`); return value; } return -1; } function normalizeEncoding(enc: string) { if (!enc) return 'utf8'; let retried; while (true) { switch (enc) { case 'utf8': case 'utf-8': return 'utf8'; case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return 'utf16le'; case 'latin1': case 'binary': return 'latin1'; case 'base64': case 'ascii': case 'hex': return enc; default: if (retried) return; // undefined enc = ('' + enc).toLowerCase(); retried = true; } } } function validateEncoding(data: string, encoding: string) { const normalizedEncoding = normalizeEncoding(encoding); const length = data.length; if (normalizedEncoding === 'hex' && length % 2 !== 0) { throw new Error(`Encoding ${encoding} not valid for data length ${length}`); } } function getDecoder(decoder?: StringDecoder, encoding?: BufferEncoding) { return decoder ?? new StringDecoder(encoding); } class CipherCommon extends Stream.Transform { private internal: InternalCipher; private decoder: StringDecoder | undefined; constructor( cipherType: string, cipherKey: BinaryLikeNode, isCipher: boolean, options: Record<string, TransformOptions> = {}, iv?: BinaryLike | null, ) { super(options); const cipherKeyBuffer = binaryLikeToArrayBuffer(cipherKey); // defaults to 16 bytes const authTagLength = getUIntOption(options, 'authTagLength') !== -1 ? getUIntOption(options, 'authTagLength') : 16; const args = { cipher_type: cipherType, cipher_key: cipherKeyBuffer, iv, ...options, auth_tag_len: authTagLength, }; this.internal = isCipher ? createInternalCipher(args) : createInternalDecipher(args); } update( data: BinaryLike, inputEncoding?: CipherEncoding, outputEncoding?: CipherEncoding, ): ArrayBuffer | string { const defaultEncoding = getDefaultEncoding(); inputEncoding = inputEncoding ?? defaultEncoding; outputEncoding = outputEncoding ?? defaultEncoding; if (typeof data === 'string') { validateEncoding(data, inputEncoding); } else if (!ArrayBuffer.isView(data)) { throw new Error('Invalid data argument'); } if (typeof data === 'string') { // On node this is handled on the native side // on our case we need to correctly send the arraybuffer to the jsi side inputEncoding = inputEncoding === 'buffer' ? 'utf8' : inputEncoding; data = binaryLikeToArrayBuffer(data, inputEncoding); } else { data = binaryLikeToArrayBuffer(data as BinaryLikeNode, inputEncoding); } const ret = this.internal.update(data); if (outputEncoding && outputEncoding !== 'buffer') { this.decoder = getDecoder(this.decoder, outputEncoding); // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.decoder!.write(SBuffer.from(ret) as any); } return ret; } final(): ArrayBuffer; final(outputEncoding: BufferEncoding | 'buffer'): string; final(outputEncoding?: BufferEncoding | 'buffer'): ArrayBuffer | string { const ret = this.internal.final(); if (outputEncoding && outputEncoding !== 'buffer') { this.decoder = getDecoder(this.decoder, outputEncoding); // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.decoder!.end(SBuffer.from(ret) as any); } return ret; } _transform(chunk: BinaryLike, encoding: Encoding, callback: () => void) { this.push(this.update(chunk, encoding)); callback(); } _flush(callback: () => void) { this.push(this.final()); callback(); } public setAutoPadding(autoPadding?: boolean): this { this.internal.setAutoPadding(!!autoPadding); return this; } public setAAD( buffer: Buffer, options?: { plaintextLength: number; }, ): this { this.internal.setAAD({ data: buffer.buffer, plaintextLength: options?.plaintextLength, }); return this; } public getAuthTag(): Buffer { return Buffer.from(this.internal.getAuthTag()); } public setAuthTag(tag: Buffer): this { this.internal.setAuthTag(binaryLikeToArrayBuffer(tag)); return this; } } class Cipher extends CipherCommon { constructor( cipherType: string, cipherKey: BinaryLikeNode, options: Record<string, TransformOptions> = {}, iv?: BinaryLike | null, ) { if (iv != null) { iv = binaryLikeToArrayBuffer(iv); } super(cipherType, cipherKey, true, options, iv); } } class Decipher extends CipherCommon { constructor( cipherType: string, cipherKey: BinaryLikeNode, options: Record<string, TransformOptions> = {}, iv?: BinaryLike | null, ) { if (iv != null) { iv = binaryLikeToArrayBuffer(iv); } super(cipherType, cipherKey, false, options, iv); } } /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ export function createDecipher( algorithm: CipherCCMTypes, password: BinaryLikeNode, options: CipherCCMOptions, ): DecipherCCM; /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ export function createDecipher( algorithm: CipherGCMTypes, password: BinaryLikeNode, options?: CipherGCMOptions, ): DecipherGCM; /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ export function createDecipher( algorithm: CipherType, password: BinaryLikeNode, options?: Stream.TransformOptions, ): DecipherCCM | DecipherGCM | Decipher; /** * @deprecated Use createDecipheriv instead. This function will be removed in 1.0+ */ export function createDecipher( algorithm: string, password: BinaryLikeNode, options?: CipherCCMOptions | CipherGCMOptions | Stream.TransformOptions, ): DecipherCCM | DecipherGCM | Decipher { if (options === undefined) options = {}; return new Decipher( algorithm, password, options as Record<string, TransformOptions>, ); } export function createDecipheriv( algorithm: CipherCCMTypes, key: BinaryLikeNode, iv: BinaryLike, options: CipherCCMOptions, ): DecipherCCM; export function createDecipheriv( algorithm: CipherOCBTypes, key: BinaryLikeNode, iv: BinaryLike, options: CipherOCBOptions, ): DecipherOCB; export function createDecipheriv( algorithm: CipherGCMTypes, key: BinaryLikeNode, iv: BinaryLike, options?: CipherGCMOptions, ): DecipherGCM; export function createDecipheriv( algorithm: CipherType, key: BinaryLikeNode, iv: BinaryLike | null, options?: Stream.TransformOptions, ): DecipherCCM | DecipherOCB | DecipherGCM | Decipher; export function createDecipheriv( algorithm: string, key: BinaryLikeNode, iv: BinaryLike | null, options?: | CipherCCMOptions | CipherOCBOptions | CipherGCMOptions | Stream.TransformOptions, ): DecipherCCM | DecipherOCB | DecipherGCM | Decipher { return new Decipher( algorithm, key, options as Record<string, TransformOptions>, iv, ); } /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ export function createCipher( algorithm: CipherCCMTypes, password: BinaryLikeNode, options: CipherCCMOptions, ): CipherCCM; /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ export function createCipher( algorithm: CipherGCMTypes, password: BinaryLikeNode, options?: CipherGCMOptions, ): CipherGCM; /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ export function createCipher( algorithm: CipherType, password: BinaryLikeNode, options?: Stream.TransformOptions, ): CipherCCM | CipherGCM | Cipher; /** * @deprecated Use createCipheriv instead. This function will be removed in 1.0+ */ export function createCipher( algorithm: string, password: BinaryLikeNode, options?: CipherGCMOptions | CipherCCMOptions | Stream.TransformOptions, ): CipherCCM | CipherGCM | Cipher { return new Cipher( algorithm, password, options as Record<string, TransformOptions>, ); } export function createCipheriv( algorithm: CipherCCMTypes, key: BinaryLikeNode, iv: BinaryLike, options: CipherCCMOptions, ): CipherCCM; export function createCipheriv( algorithm: CipherOCBTypes, key: BinaryLikeNode, iv: BinaryLike, options: CipherOCBOptions, ): CipherOCB; export function createCipheriv( algorithm: CipherGCMTypes, key: BinaryLikeNode, iv: BinaryLike, options?: CipherGCMOptions, ): CipherGCM; export function createCipheriv( algorithm: CipherType, key: BinaryLikeNode, iv: BinaryLike | null, options?: Stream.TransformOptions, ): CipherCCM | CipherOCB | CipherGCM | Cipher; export function createCipheriv( algorithm: string, key: BinaryLikeNode, iv: BinaryLike | null, options?: | CipherCCMOptions | CipherOCBOptions | CipherGCMOptions | Stream.TransformOptions, ): CipherCCM | CipherOCB | CipherGCM | Cipher { return new Cipher( algorithm, key, options as Record<string, TransformOptions>, iv, ); } // RSA Functions // Follows closely the model implemented in node function rsaFunctionFor( method: ( data: ArrayBuffer, format: KFormatType, type: KeyEncoding | undefined, passphrase: BinaryLike | undefined, buffer: ArrayBuffer, padding: number, oaepHash: string | undefined, oaepLabel: BinaryLike | undefined, ) => Buffer, defaultPadding: number, keyType: 'public' | 'private', ) { return (options: EncodingOptions | BinaryLike, buffer: BinaryLike) => { const { format, type, data, passphrase } = keyType === 'private' ? preparePrivateKey(options) : preparePublicOrPrivateKey(options); const padding = options && typeof options === 'object' && 'padding' in options && typeof options.padding === 'number' ? options.padding : defaultPadding; const oaepHash = options && typeof options === 'object' && 'oaepHash' in options ? options.oaepHash : undefined; const encoding = options && typeof options === 'object' && 'encoding' in options ? options.encoding : undefined; let oaepLabel = options && typeof options === 'object' && 'oaepLabel' in options ? options.oaepLabel : undefined; if (oaepHash !== undefined) validateString(oaepHash, 'key.oaepHash'); if (oaepLabel !== undefined) oaepLabel = binaryLikeToArrayBuffer(oaepLabel, encoding); buffer = binaryLikeToArrayBuffer(buffer, encoding); const rawRes = method( data, format, type, passphrase, buffer, padding, oaepHash, oaepLabel, ); return Buffer.from(rawRes); }; } export const publicEncrypt = rsaFunctionFor( _publicEncrypt, constants.RSA_PKCS1_OAEP_PADDING, 'public', ); export const publicDecrypt = rsaFunctionFor( _publicDecrypt, constants.RSA_PKCS1_PADDING, 'public', ); // const privateEncrypt = rsaFunctionFor(_privateEncrypt, constants.RSA_PKCS1_PADDING, // 'private'); export const privateDecrypt = rsaFunctionFor( _privateDecrypt, constants.RSA_PKCS1_OAEP_PADDING, 'private', ); // _ _ __ _____ _ // | | | |/ / | __ \ (_) // __ _ ___ _ __ ___ _ __ __ _| |_ ___| ' / ___ _ _| |__) |_ _ _ _ __ // / _` |/ _ \ '_ \ / _ \ '__/ _` | __/ _ \ < / _ \ | | | ___/ _` | | '__| // | (_| | __/ | | | __/ | | (_| | || __/ . \ __/ |_| | | | (_| | | | // \__, |\___|_| |_|\___|_| \__,_|\__\___|_|\_\___|\__, |_| \__,_|_|_| // __/ | __/ | // |___/ |___/ export type GenerateKeyPairOptions = { modulusLength?: number; // Key size in bits (RSA, DSA). publicExponent?: number; // Public exponent (RSA). Default: 0x10001. hashAlgorithm?: string; // Name of the message digest (RSA-PSS). mgf1HashAlgorithm?: string; // string Name of the message digest used by MGF1 (RSA-PSS). saltLength?: number; // Minimal salt length in bytes (RSA-PSS). divisorLength?: number; // Size of q in bits (DSA). namedCurve?: string; // Name of the curve to use (EC). prime?: Buffer; // The prime parameter (DH). primeLength?: number; // Prime length in bits (DH). generator?: number; // Custom generator (DH). Default: 2. groupName?: string; // Diffie-Hellman group name (DH). See crypto.getDiffieHellman(). publicKeyEncoding?: EncodingOptions; // See keyObject.export(). privateKeyEncoding?: EncodingOptions; // See keyObject.export(). paramEncoding?: string; hash?: string; mgf1Hash?: string; }; export type KeyPairKey = Buffer | KeyObjectHandle | CryptoKey | undefined; export type GenerateKeyPairReturn = [ error?: Error, privateKey?: KeyPairKey, publicKey?: KeyPairKey, ]; export type GenerateKeyPairCallback = ( error?: Error, publicKey?: KeyPairKey, privateKey?: KeyPairKey, ) => GenerateKeyPairReturn | void; export type KeyPair = { publicKey?: KeyPairKey; privateKey?: KeyPairKey; }; export type GenerateKeyPairPromiseReturn = [error?: Error, keypair?: KeyPair]; function parseKeyEncoding( keyType: string, options: GenerateKeyPairOptions = kEmptyObject, ) { const { publicKeyEncoding, privateKeyEncoding } = options; let publicFormat, publicType; if (publicKeyEncoding == null) { publicFormat = publicType = undefined; } else if (typeof publicKeyEncoding === 'object') { ({ format: publicFormat, type: publicType } = parsePublicKeyEncoding( publicKeyEncoding, keyType, 'publicKeyEncoding', )); } else { throw new Error( 'Invalid argument options.publicKeyEncoding', publicKeyEncoding, ); } let privateFormat, privateType, cipher, passphrase; if (privateKeyEncoding == null) { privateFormat = privateType = undefined; } else if (typeof privateKeyEncoding === 'object') { ({ format: privateFormat, type: privateType, cipher, passphrase, } = parsePrivateKeyEncoding( privateKeyEncoding, keyType, 'privateKeyEncoding', )); } else { throw new Error( 'Invalid argument options.privateKeyEncoding', publicKeyEncoding as ErrorOptions, ); } return [ publicFormat, publicType, privateFormat, privateType, cipher, passphrase, ]; } /** On node a very complex "job" chain is created, we are going for a far simpler approach and calling * an internal function that basically executes the same byte shuffling on the native side */ function internalGenerateKeyPair( isAsync: boolean, type: KeyPairType, options?: GenerateKeyPairOptions, callback?: GenerateKeyPairCallback, ): GenerateKeyPairReturn | void { const encoding = parseKeyEncoding(type, options); // if (options !== undefined) // validateObject(options, 'options'); switch (type) { case 'rsa-pss': // fallthrough case 'rsa': return internalRsaGenerateKeyPair( isAsync, type, options, callback, encoding, ); // case 'dsa': { // validateObject(options, 'options'); // const { modulusLength } = options!; // validateUint32(modulusLength, 'options.modulusLength'); // let { divisorLength } = options!; // if (divisorLength == null) { // divisorLength = -1; // } else validateInt32(divisorLength, 'options.divisorLength', 0); // // return new DsaKeyPairGenJob( // // mode, // // modulusLength, // // divisorLength, // // ...encoding); // } case 'ec': return internalEcGenerateKeyPair( isAsync, type, options, callback, encoding, ); // case 'ed25519': // case 'ed448': // case 'x25519': // case 'x448': { // let id; // switch (type) { // case 'ed25519': // id = EVP_PKEY_ED25519; // break; // case 'ed448': // id = EVP_PKEY_ED448; // break; // case 'x25519': // id = EVP_PKEY_X25519; // break; // case 'x448': // id = EVP_PKEY_X448; // break; // } // return new NidKeyPairGenJob(mode, id, ...encoding); // } // case 'dh': { // validateObject(options, 'options'); // const { group, primeLength, prime, generator } = options; // if (group != null) { // if (prime != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'prime'); // if (primeLength != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'primeLength'); // if (generator != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('group', 'generator'); // validateString(group, 'options.group'); // return new DhKeyPairGenJob(mode, group, ...encoding); // } // if (prime != null) { // if (primeLength != null) // throw new ERR_INCOMPATIBLE_OPTION_PAIR('prime', 'primeLength'); // validateBuffer(prime, 'options.prime'); // } else if (primeLength != null) { // validateInt32(primeLength, 'options.primeLength', 0); // } else { // throw new ERR_MISSING_OPTION( // 'At least one of the group, prime, or primeLength options' // ); // } // if (generator != null) { // validateInt32(generator, 'options.generator', 0); // } // return new DhKeyPairGenJob( // mode, // prime != null ? prime : primeLength, // generator == null ? 2 : generator, // ...encoding // ); // } default: // Fall through } const err = new Error(` Invalid Argument options: '${type}' scheme not supported for generateKey(). Currently not all encryption methods are supported in quick-crypto. Check implementation_coverage.md for status. `); return [err, undefined, undefined]; } const internalRsaGenerateKeyPair = ( isAsync: boolean, type: KeyPairType, options: GenerateKeyPairOptions | undefined, callback: GenerateKeyPairCallback | undefined, encoding: (string | ArrayBuffer | KFormatType | KeyEncoding | undefined)[], ): GenerateKeyPairReturn | void => { validateObject<GenerateKeyPairOptions>(options, 'options'); const { modulusLength } = options!; validateUint32(modulusLength as number, 'options.modulusLength'); let { publicExponent } = options!; if (publicExponent == null) { publicExponent = 0x10001; } else { validateUint32(publicExponent, 'options.publicExponent'); } if (type === 'rsa') { if (isAsync) { NativeQuickCrypto.generateKeyPair( KeyVariant.RSA_SSA_PKCS1_v1_5, // Used also for RSA-OAEP modulusLength as number, publicExponent, ...encoding, ) .then(([err, publicKey, privateKey]) => { if (publicKey instanceof Buffer) { publicKey = Buffer.from(publicKey); } if (privateKey instanceof Buffer) { privateKey = Buffer.from(privateKey); } callback!(err, publicKey, privateKey); }) .catch((err) => { callback!(err, undefined, undefined); }); } else { const [err, publicKey, privateKey] = NativeQuickCrypto.generateKeyPairSync( KeyVariant.RSA_SSA_PKCS1_v1_5, modulusLength as number, publicExponent, ...encoding, ); const pub = publicKey instanceof Buffer ? Buffer.from(publicKey) : publicKey; const priv = privateKey instanceof Buffer ? Buffer.from(privateKey) : privateKey; return [err, pub, priv]; } } const { hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength } = options!; // // We don't have a process object on RN // // const pendingDeprecation = getOptionValue('--pending-deprecation'); if (saltLength !== undefined) validateInt32(saltLength, 'options.saltLength', 0); if (hashAlgorithm !== undefined) validateString(hashAlgorithm, 'options.hashAlgorithm'); if (mgf1HashAlgorithm !== undefined) validateString(mgf1HashAlgorithm, 'options.mgf1HashAlgorithm'); if (hash !== undefined) { // pendingDeprecation && process.emitWarning( // '"options.hash" is deprecated, ' + // 'use "options.hashAlgorithm" instead.', // 'DeprecationWarning', // 'DEP0154'); validateString(hash, 'options.hash'); if (hashAlgorithm && hash !== hashAlgorithm) { throw new Error(`Invalid Argument options.hash ${hash}`); } } if (mgf1Hash !== undefined) { // pendingDeprecation && process.emitWarning( // '"options.mgf1Hash" is deprecated, ' + // 'use "options.mgf1HashAlgorithm" instead.', // 'DeprecationWarning', // 'DEP0154'); validateString(mgf1Hash, 'options.mgf1Hash'); if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { throw new Error(`Invalid Argument options.mgf1Hash ${mgf1Hash}`); } } return NativeQuickCrypto.generateKeyPairSync( KeyVariant.RSA_PSS, modulusLength as number, publicExponent, hashAlgorithm || hash, mgf1HashAlgorithm || mgf1Hash, saltLength, ...encoding, ); }; const internalEcGenerateKeyPair = ( isAsync: boolean, _type: KeyPairType, options: GenerateKeyPairOptions | undefined, callback: GenerateKeyPairCallback | undefined, encoding: (string | ArrayBuffer | KFormatType | KeyEncoding | undefined)[], ): GenerateKeyPairReturn | void => { validateObject<GenerateKeyPairOptions>(options, 'options'); const { namedCurve } = options!; validateString(namedCurve, 'options.namedCurve'); let paramEncodingFlag = ECCurve.OPENSSL_EC_NAMED_CURVE; const { paramEncoding } = options!; if (paramEncoding == null || paramEncoding === 'named') paramEncodingFlag = ECCurve.OPENSSL_EC_NAMED_CURVE; else if (paramEncoding === 'explicit') paramEncodingFlag = ECCurve.OPENSSL_EC_EXPLICIT_CURVE; else throw new Error(`Invalid Argument options.paramEncoding ${paramEncoding}`); if (isAsync) { NativeQuickCrypto.generateKeyPair( KeyVariant.EC, namedCurve as NamedCurve, paramEncodingFlag, ...encoding, ) .then(([err, publicKey, privateKey]) => { if (publicKey instanceof Buffer) { publicKey = Buffer.from(publicKey); } if (privateKey instanceof Buffer) { privateKey = Buffer.from(privateKey); } callback?.(err, publicKey, privateKey); }) .catch((err) => { callback?.(err, undefined, undefined); }); } const [err, publicKey, privateKey] = NativeQuickCrypto.generateKeyPairSync( KeyVariant.EC, namedCurve as NamedCurve, paramEncodingFlag, ...encoding, ); const pub = publicKey instanceof Buffer ? Buffer.from(publicKey) : publicKey; const priv = privateKey instanceof Buffer ? Buffer.from(privateKey) : privateKey; return [err, pub, priv]; }; export const generateKeyPair = ( type: KeyPairType, options: GenerateKeyPairOptions, callback: GenerateKeyPairCallback, ): void => { validateFunction(callback); internalGenerateKeyPair(true, type, options, callback); }; // Promisify generateKeyPair // (attempted to use util.promisify, to no avail) export const generateKeyPairPromise = ( type: KeyPairType, options: GenerateKeyPairOptions, ): Promise<GenerateKeyPairPromiseReturn> => { return new Promise((resolve, reject) => { generateKeyPair(type, options, (err, publicKey, privateKey) => { if (err) { reject([err, undefined]); } else { resolve([undefined, { publicKey, privateKey }]); } }); }); }; // generateKeyPairSync export function generateKeyPairSync(type: KeyPairType): CryptoKeyPair; export function generateKeyPairSync( type: KeyPairType, options: GenerateKeyPairOptions, ): CryptoKeyPair; export function generateKeyPairSync( type: KeyPairType, options?: GenerateKeyPairOptions, ): CryptoKeyPair { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, publicKey, privateKey] = internalGenerateKeyPair( false, type, options, undefined, )!; return { publicKey, privateKey, }; }