react-native-quick-crypto
Version:
A fast implementation of Node's `crypto` module written in C/C++ JSI
172 lines (163 loc) • 5.12 kB
JavaScript
;
import { Buffer } from '@craftzdog/react-native-buffer';
import { NitroModules } from 'react-native-nitro-modules';
import { KeyObject } from './classes';
import { isCryptoKey } from './utils';
import { binaryLikeToArrayBuffer as toAB, isStringOrBuffer, KFormatType, KeyEncoding } from '../utils';
function prepareKey(key, isPublic) {
// Already a KeyObject
if (key instanceof KeyObject) {
if (isPublic) {
if (key.type === 'secret') {
throw new Error('Cannot use secret key for signature verification');
}
} else {
if (key.type !== 'private') {
throw new Error('Key must be a private key for signing');
}
}
return {
keyObject: key
};
}
// CryptoKey - extract KeyObject
if (isCryptoKey(key)) {
const cryptoKey = key;
return prepareKey(cryptoKey.keyObject, isPublic);
}
// Raw string or buffer - create KeyObject
if (isStringOrBuffer(key)) {
const isPem = typeof key === 'string' && key.includes('-----BEGIN');
const format = isPem ? KFormatType.PEM : undefined;
const type = isPublic ? 'public' : 'private';
const keyData = toAB(key);
const keyObject = KeyObject.createKeyObject(type, keyData, format);
return {
keyObject
};
}
// KeyInputObject with options
if (typeof key === 'object' && 'key' in key) {
const keyObj = key;
const {
key: data,
format,
type,
padding,
saltLength,
dsaEncoding
} = keyObj;
// Nested KeyObject
if (data instanceof KeyObject) {
return {
keyObject: data,
options: {
padding,
saltLength,
dsaEncoding
}
};
}
// Nested CryptoKey
if (isCryptoKey(data)) {
return {
keyObject: data.keyObject,
options: {
padding,
saltLength,
dsaEncoding
}
};
}
if (!isStringOrBuffer(data)) {
throw new Error('Invalid key data type');
}
// Determine format
const isPem = format === 'pem' || typeof data === 'string' && data.includes('-----BEGIN');
const kFormat = isPem ? KFormatType.PEM : format === 'der' ? KFormatType.DER : undefined;
// Determine encoding type
let kType;
if (type === 'pkcs8') kType = KeyEncoding.PKCS8;else if (type === 'pkcs1') kType = KeyEncoding.PKCS1;else if (type === 'sec1') kType = KeyEncoding.SEC1;else if (type === 'spki') kType = KeyEncoding.SPKI;
const keyType = isPublic ? 'public' : 'private';
// Always convert to ArrayBuffer to avoid Nitro bridge string truncation bug
const originalLength = typeof data === 'string' ? data.length : data.byteLength;
const keyData = toAB(data);
console.log(`[prepareKey KeyInputObject] ${keyType} key, original length: ${originalLength}, ArrayBuffer size: ${keyData.byteLength}`);
const keyObject = KeyObject.createKeyObject(keyType, keyData, kFormat, kType);
return {
keyObject,
options: {
padding,
saltLength,
dsaEncoding
}
};
}
throw new Error('Invalid key input');
}
function dsaEncodingToNumber(dsaEncoding) {
if (dsaEncoding === 'der') return 0;
if (dsaEncoding === 'ieee-p1363') return 1;
return undefined;
}
export class Sign {
constructor(algorithm) {
this.handle = NitroModules.createHybridObject('SignHandle');
this.handle.init(algorithm);
}
update(data) {
const dataBuffer = toAB(data);
this.handle.update(dataBuffer);
return this;
}
sign(privateKey, outputEncoding) {
if (privateKey === null || privateKey === undefined) {
throw new Error('Private key is required');
}
const {
keyObject,
options
} = prepareKey(privateKey, false);
const signature = this.handle.sign(keyObject.handle, options?.padding, options?.saltLength, dsaEncodingToNumber(options?.dsaEncoding));
const buf = Buffer.from(signature);
if (outputEncoding) {
return buf.toString(outputEncoding);
}
return buf;
}
}
export class Verify {
constructor(algorithm) {
this.handle = NitroModules.createHybridObject('VerifyHandle');
this.handle.init(algorithm);
}
update(data) {
const dataBuffer = toAB(data);
this.handle.update(dataBuffer);
return this;
}
verify(publicKey, signature, signatureEncoding) {
if (publicKey === null || publicKey === undefined) {
throw new Error('Public key is required');
}
const {
keyObject,
options
} = prepareKey(publicKey, true);
// Convert signature to ArrayBuffer
let sigBuffer;
if (signatureEncoding && typeof signature === 'string') {
sigBuffer = toAB(Buffer.from(signature, signatureEncoding));
} else {
sigBuffer = toAB(signature);
}
return this.handle.verify(keyObject.handle, sigBuffer, options?.padding, options?.saltLength, dsaEncodingToNumber(options?.dsaEncoding));
}
}
export function createSign(algorithm) {
return new Sign(algorithm);
}
export function createVerify(algorithm) {
return new Verify(algorithm);
}
//# sourceMappingURL=signVerify.js.map