react-native-quick-crypto
Version:
A fast implementation of Node's `crypto` module written in C/C++ JSI
147 lines (126 loc) • 4.37 kB
text/typescript
import { NitroModules } from 'react-native-nitro-modules';
import { Buffer } from '@craftzdog/react-native-buffer';
import { KeyObject, PublicKeyObject, PrivateKeyObject } from './keys/classes';
import type { DsaKeyPair } from './specs/dsaKeyPair.nitro';
import type { GenerateKeyPairOptions, KeyPairGenConfig } from './utils/types';
import { KFormatType, KeyEncoding } from './utils';
export class Dsa {
native: DsaKeyPair;
constructor(modulusLength: number, divisorLength?: number) {
this.native = NitroModules.createHybridObject<DsaKeyPair>('DsaKeyPair');
this.native.setModulusLength(modulusLength);
if (divisorLength !== undefined && divisorLength >= 0) {
this.native.setDivisorLength(divisorLength);
}
}
async generateKeyPair(): Promise<void> {
await this.native.generateKeyPair();
}
generateKeyPairSync(): void {
this.native.generateKeyPairSync();
}
}
// FIPS 186-4 §4.2: only L = 1024, 2048, 3072 are sanctioned. NIST has
// deprecated DSA-1024 for new applications, but we retain it for
// interop with legacy systems and match Node's permissive default. We
// reject anything below 1024 outright.
const DSA_MIN_MODULUS_LENGTH = 1024;
function dsa_prepareKeyGenParams(
options: GenerateKeyPairOptions | undefined,
): Dsa {
if (!options) {
throw new Error('Options are required for DSA key generation');
}
const { modulusLength, divisorLength } = options;
if (!modulusLength || modulusLength < DSA_MIN_MODULUS_LENGTH) {
throw new RangeError(
`DSA modulusLength must be at least ${DSA_MIN_MODULUS_LENGTH} bits ` +
`(got ${modulusLength ?? 0})`,
);
}
return new Dsa(modulusLength, divisorLength);
}
function dsa_formatKeyPairOutput(
dsa: Dsa,
encoding: KeyPairGenConfig,
): {
publicKey: PublicKeyObject | Buffer | string | ArrayBuffer;
privateKey: PrivateKeyObject | Buffer | string | ArrayBuffer;
} {
const { publicFormat, privateFormat, cipher, passphrase } = encoding;
const publicKeyData = dsa.native.getPublicKey();
const privateKeyData = dsa.native.getPrivateKey();
const pub = KeyObject.createKeyObject(
'public',
publicKeyData,
KFormatType.DER,
KeyEncoding.SPKI,
) as PublicKeyObject;
const priv = KeyObject.createKeyObject(
'private',
privateKeyData,
KFormatType.DER,
KeyEncoding.PKCS8,
) as PrivateKeyObject;
let publicKey: PublicKeyObject | Buffer | string | ArrayBuffer;
let privateKey: PrivateKeyObject | Buffer | string | ArrayBuffer;
if (
publicFormat === 'raw-public' ||
privateFormat === 'raw-private' ||
privateFormat === 'raw-seed'
) {
throw new Error('Raw key formats are not supported for DSA keys');
}
if (publicFormat === -1) {
publicKey = pub;
} else {
const format =
publicFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
const exported = pub.handle.exportKey(format, KeyEncoding.SPKI);
if (format === KFormatType.PEM) {
publicKey = Buffer.from(new Uint8Array(exported)).toString('utf-8');
} else {
publicKey = exported;
}
}
if (privateFormat === -1) {
privateKey = priv;
} else {
const format =
privateFormat === KFormatType.PEM ? KFormatType.PEM : KFormatType.DER;
const exported = priv.handle.exportKey(
format,
KeyEncoding.PKCS8,
cipher,
passphrase,
);
if (format === KFormatType.PEM) {
privateKey = Buffer.from(new Uint8Array(exported)).toString('utf-8');
} else {
privateKey = exported;
}
}
return { publicKey, privateKey };
}
export async function dsa_generateKeyPairNode(
options: GenerateKeyPairOptions | undefined,
encoding: KeyPairGenConfig,
): Promise<{
publicKey: PublicKeyObject | Buffer | string | ArrayBuffer;
privateKey: PrivateKeyObject | Buffer | string | ArrayBuffer;
}> {
const dsa = dsa_prepareKeyGenParams(options);
await dsa.generateKeyPair();
return dsa_formatKeyPairOutput(dsa, encoding);
}
export function dsa_generateKeyPairNodeSync(
options: GenerateKeyPairOptions | undefined,
encoding: KeyPairGenConfig,
): {
publicKey: PublicKeyObject | Buffer | string | ArrayBuffer;
privateKey: PrivateKeyObject | Buffer | string | ArrayBuffer;
} {
const dsa = dsa_prepareKeyGenParams(options);
dsa.generateKeyPairSync();
return dsa_formatKeyPairOutput(dsa, encoding);
}