UNPKG

react-native-quick-crypto

Version:

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

271 lines (262 loc) 8.4 kB
"use strict"; import { Buffer } from '@craftzdog/react-native-buffer'; import { NitroModules } from 'react-native-nitro-modules'; import { KeyType, getSortedUsages } from '../utils'; import { parsePrivateKeyEncoding, parsePublicKeyEncoding } from './utils'; export class CryptoKey { get [Symbol.toStringTag]() { return 'CryptoKey'; } constructor(keyObject, keyAlgorithm, keyUsages, keyExtractable) { this.keyObject = keyObject; this.keyAlgorithm = keyAlgorithm; // Frozen so external code can't mutate `key.usages` (per WebCrypto spec). this.keyUsages = Object.freeze(getSortedUsages(keyUsages)); this.keyExtractable = keyExtractable; } // eslint-disable-next-line @typescript-eslint/no-unused-vars inspect(_depth, _options) { throw new Error('CryptoKey.inspect is not implemented'); // if (depth < 0) return this; // const opts = { // ...options, // depth: options.depth == null ? null : options.depth - 1, // }; // return `CryptoKey ${inspect( // { // type: this.type, // extractable: this.extractable, // algorithm: this.algorithm, // usages: this.usages, // }, // opts // )}`; } get type() { // if (!(this instanceof CryptoKey)) throw new Error('Invalid CryptoKey'); return this.keyObject.type; } get extractable() { return this.keyExtractable; } get algorithm() { return this.keyAlgorithm; } get usages() { return this.keyUsages; } } export class KeyObject { get [Symbol.toStringTag]() { return 'KeyObject'; } // eslint-disable-next-line @typescript-eslint/no-unused-vars export(_options) { // This is a placeholder and should be overridden by subclasses. throw new Error('export() must be implemented by subclasses'); } equals(otherKeyObject) { if (!(otherKeyObject instanceof KeyObject)) { throw new TypeError('otherKeyObject must be a KeyObject'); } return this.handle.keyEquals(otherKeyObject.handle); } constructor(type, handleOrKey) { if (type !== 'secret' && type !== 'public' && type !== 'private') throw new Error(`invalid KeyObject type: ${type}`); if (handleOrKey instanceof ArrayBuffer) { this.handle = NitroModules.createHybridObject('KeyObjectHandle'); let keyType; switch (type) { case 'public': keyType = KeyType.PUBLIC; break; case 'private': keyType = KeyType.PRIVATE; break; case 'secret': keyType = KeyType.SECRET; break; default: // Should not happen throw new Error('invalid key type'); } this.handle.init(keyType, handleOrKey); } else { this.handle = handleOrKey; } this.type = type; } static from(key) { if (!(key instanceof CryptoKey)) { throw new TypeError(`The "key" argument must be an instance of CryptoKey. Received ${typeof key}`); } return key.keyObject; } toCryptoKey(algorithm, extractable, keyUsages) { return new CryptoKey(this, algorithm, keyUsages, extractable); } static createKeyObject(type, key, format, encoding, passphrase) { if (type !== 'secret' && type !== 'public' && type !== 'private') throw new Error(`invalid KeyObject type: ${type}`); const handle = NitroModules.createHybridObject('KeyObjectHandle'); let keyType; switch (type) { case 'public': keyType = KeyType.PUBLIC; break; case 'private': keyType = KeyType.PRIVATE; break; case 'secret': keyType = KeyType.SECRET; break; default: throw new Error('invalid key type'); } handle.init(keyType, key, format, encoding, passphrase); // For asymmetric keys, return the appropriate subclass if (type === 'public' || type === 'private') { try { handle.getAsymmetricKeyType(); // If we get here, it's an asymmetric key - return the appropriate subclass if (type === 'public') { return new PublicKeyObject(handle); } else { return new PrivateKeyObject(handle); } } catch { // Not an asymmetric key, fall through to regular KeyObject } } // For secret keys, return SecretKeyObject if (type === 'secret') { return new SecretKeyObject(handle); } // Return regular KeyObject for symmetric keys or if asymmetric detection failed return new KeyObject(type, handle); } getAsymmetricKeyType() { return undefined; } getAsymmetricKeyDetails() { return undefined; } } export class SecretKeyObject extends KeyObject { constructor(handle) { super('secret', handle); } get symmetricKeySize() { return this.handle.getSymmetricKeySize(); } export(options) { if (options?.format === 'pem' || options?.format === 'jwk') { throw new Error(`SecretKey export for ${options.format} is not supported`); } const key = this.handle.exportKey(); return Buffer.from(key); } } // const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); // const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails'); // function normalizeKeyDetails(details = {}) { // if (details.publicExponent !== undefined) { // return { // ...details, // publicExponent: bigIntArrayToUnsignedBigInt( // new Uint8Array(details.publicExponent) // ), // }; // } // return details; // } export class AsymmetricKeyObject extends KeyObject { constructor(type, handle) { super(type, handle); } get asymmetricKeyType() { if (!this._asymmetricKeyType) { this._asymmetricKeyType = this.handle.getAsymmetricKeyType(); } return this._asymmetricKeyType; } get asymmetricKeyDetails() { if (!this._asymmetricKeyDetails) { this._asymmetricKeyDetails = this.handle.keyDetail(); } return this._asymmetricKeyDetails; } get namedCurve() { return this.asymmetricKeyDetails?.namedCurve; } } export class PublicKeyObject extends AsymmetricKeyObject { constructor(handle) { super('public', handle); } export(options) { if (options?.format === 'jwk') { return this.handle.exportJwk({}, false); } if (options?.format === 'raw-public') { if (this.asymmetricKeyType === 'ec') { const pointType = options.type ?? 'uncompressed'; if (pointType !== 'compressed' && pointType !== 'uncompressed') { throw new Error(`Invalid options.type for raw-public EC export: ${pointType}`); } return Buffer.from(this.handle.exportECPublicRaw(pointType === 'compressed')); } return Buffer.from(this.handle.exportRawPublic()); } const { format, type } = parsePublicKeyEncoding(options, this.asymmetricKeyType); if (typeof format !== 'number') { throw new Error(`Unexpected format ${format} after raw-format dispatch`); } const key = this.handle.exportKey(format, typeof type === 'string' ? undefined : type); const buffer = Buffer.from(key); if (options?.format === 'pem') { return buffer.toString('utf-8'); } return buffer; } } export class PrivateKeyObject extends AsymmetricKeyObject { constructor(handle) { super('private', handle); } export(options) { if (options?.format === 'jwk') { if (options.passphrase !== undefined) { throw new Error('jwk does not support encryption'); } return this.handle.exportJwk({}, false); } if (options?.format === 'raw-private') { if (this.asymmetricKeyType === 'ec') { return Buffer.from(this.handle.exportECPrivateRaw()); } return Buffer.from(this.handle.exportRawPrivate()); } if (options?.format === 'raw-seed') { return Buffer.from(this.handle.exportRawSeed()); } const { format, type, cipher, passphrase } = parsePrivateKeyEncoding(options, this.asymmetricKeyType); if (typeof format !== 'number') { throw new Error(`Unexpected format ${format} after raw-format dispatch`); } const key = this.handle.exportKey(format, typeof type === 'string' ? undefined : type, cipher, passphrase); const buffer = Buffer.from(key); if (options?.format === 'pem') { return buffer.toString('utf-8'); } return buffer; } } //# sourceMappingURL=classes.js.map