import { NitroModules } from 'react-native-nitro-modules'; import type { DiffieHellman as DiffieHellmanInterface } from './specs/diffie-hellman.nitro'; import { Buffer } from '@craftzdog/react-native-buffer'; import { DH_GROUPS } from './dh-groups'; import { toArrayBuffer } from './utils/conversion'; export class DiffieHellman { private _hybrid: DiffieHellmanInterface; constructor( sizeOrPrime: number | Buffer | string, generator?: number | Buffer | string, encoding?: BufferEncoding, ) { this._hybrid = NitroModules.createHybridObject('DiffieHellman'); if (typeof sizeOrPrime === 'number') { const gen = typeof generator === 'number' ? generator : 2; this._hybrid.initWithSize(sizeOrPrime, gen); } else { let primeBuf: Buffer; if (Buffer.isBuffer(sizeOrPrime)) { primeBuf = sizeOrPrime; } else { primeBuf = Buffer.from(sizeOrPrime, encoding as BufferEncoding); } let genBuf: Buffer; if (generator === undefined) { genBuf = Buffer.from([2]); } else if (typeof generator === 'number') { genBuf = Buffer.from([generator]); } else if (Buffer.isBuffer(generator)) { genBuf = generator; } else { genBuf = Buffer.from(generator, encoding as BufferEncoding); } this._hybrid.init(toArrayBuffer(primeBuf), toArrayBuffer(genBuf)); } } generateKeys(encoding?: BufferEncoding): Buffer | string { const keys = Buffer.from(this._hybrid.generateKeys()); if (encoding) return keys.toString(encoding); return keys; } computeSecret( otherPublicKey: Buffer | string, inputEncoding?: BufferEncoding, outputEncoding?: BufferEncoding, ): Buffer | string { let keyBuf: Buffer; if (Buffer.isBuffer(otherPublicKey)) { keyBuf = otherPublicKey; } else { keyBuf = Buffer.from(otherPublicKey, inputEncoding); } const secret = Buffer.from( this._hybrid.computeSecret(toArrayBuffer(keyBuf)), ); if (outputEncoding) return secret.toString(outputEncoding); return secret; } getPrime(encoding?: BufferEncoding): Buffer | string { const p = Buffer.from(this._hybrid.getPrime()); if (encoding) return p.toString(encoding); return p; } getGenerator(encoding?: BufferEncoding): Buffer | string { const g = Buffer.from(this._hybrid.getGenerator()); if (encoding) return g.toString(encoding); return g; } getPublicKey(encoding?: BufferEncoding): Buffer | string { const p = Buffer.from(this._hybrid.getPublicKey()); if (encoding) return p.toString(encoding); return p; } getPrivateKey(encoding?: BufferEncoding): Buffer | string { const p = Buffer.from(this._hybrid.getPrivateKey()); if (encoding) return p.toString(encoding); return p; } setPublicKey(publicKey: Buffer | string, encoding?: BufferEncoding): void { let keyBuf: Buffer; if (Buffer.isBuffer(publicKey)) { keyBuf = publicKey; } else { keyBuf = Buffer.from(publicKey, encoding); } this._hybrid.setPublicKey(toArrayBuffer(keyBuf)); } setPrivateKey(privateKey: Buffer | string, encoding?: BufferEncoding): void { let keyBuf: Buffer; if (Buffer.isBuffer(privateKey)) { keyBuf = privateKey; } else { keyBuf = Buffer.from(privateKey, encoding); } this._hybrid.setPrivateKey(toArrayBuffer(keyBuf)); } get verifyError(): number { return this._hybrid.getVerifyError(); } } export function createDiffieHellman( primeOrSize: number | string | Buffer, primeEncodingOrGenerator?: string | number | Buffer, generator?: number | string | Buffer, _generatorEncoding?: string, ): DiffieHellman { if (typeof primeOrSize === 'number') { const gen = typeof primeEncodingOrGenerator === 'number' ? primeEncodingOrGenerator : 2; return new DiffieHellman(primeOrSize, gen); } // Standardize arguments for String/Buffer prime // createDiffieHellman(prime, [encoding], [generator], [encoding]) let prime: Buffer; let generatorVal: Buffer | number | undefined; if (Buffer.isBuffer(primeOrSize)) { prime = primeOrSize; // 2nd arg is generator if not string (encoding) if ( primeEncodingOrGenerator !== undefined && typeof primeEncodingOrGenerator !== 'string' ) { generatorVal = primeEncodingOrGenerator as Buffer | number; } else if (generator !== undefined) { generatorVal = generator as Buffer | number; } else { generatorVal = 2; } } else { // String prime const encoding = typeof primeEncodingOrGenerator === 'string' ? primeEncodingOrGenerator : 'utf8'; // Defaulting to utf8 or hex? Node default is 'binary' usually but utf8 safer for TS. Node docs say: "If no encoding is specified, 'binary' is used." // We'll trust user passed encoding if it's a string, otherwise handle it. prime = Buffer.from(primeOrSize, encoding as BufferEncoding); // Generator handling in this case if (generator !== undefined) { generatorVal = generator as Buffer | number; if (typeof generator === 'string' && _generatorEncoding) { generatorVal = Buffer.from( generator, _generatorEncoding as BufferEncoding, ); } else if (typeof generator === 'string') { // string with no encoding, assume same as prime? or utf8? generatorVal = Buffer.from(generator, encoding as BufferEncoding); } } else if ( typeof primeEncodingOrGenerator !== 'string' && primeEncodingOrGenerator !== undefined ) { // 2nd arg was generator generatorVal = primeEncodingOrGenerator as number; } else { generatorVal = 2; } } return new DiffieHellman(prime, generatorVal); } export function getDiffieHellman(groupName: string): DiffieHellman { const group = DH_GROUPS[groupName]; if (!group) { throw new Error(`Unknown group: ${groupName}`); } // group.prime and group.generator are hex strings return new DiffieHellman(group.prime, group.generator, 'hex'); } export { getDiffieHellman as createDiffieHellmanGroup };