12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607 |
- // TODO:
- // * make max packet size configurable
- // * if decompression is enabled, use `._packet` in decipher instances as
- // input to (sync) zlib inflater with appropriate offset and length to
- // avoid an additional copy of payload data before inflation
- // * factor decompression status into packet length checks
- 'use strict';
- const {
- createCipheriv, createDecipheriv, createHmac, randomFillSync, timingSafeEqual
- } = require('crypto');
- const { readUInt32BE, writeUInt32BE } = require('./utils.js');
- const FastBuffer = Buffer[Symbol.species];
- const MAX_SEQNO = 2 ** 32 - 1;
- const EMPTY_BUFFER = Buffer.alloc(0);
- const BUF_INT = Buffer.alloc(4);
- const DISCARD_CACHE = new Map();
- const MAX_PACKET_SIZE = 35000;
- let binding;
- let AESGCMCipher;
- let ChaChaPolyCipher;
- let GenericCipher;
- let AESGCMDecipher;
- let ChaChaPolyDecipher;
- let GenericDecipher;
- try {
- binding = require('./crypto/build/Release/sshcrypto.node');
- ({ AESGCMCipher, ChaChaPolyCipher, GenericCipher,
- AESGCMDecipher, ChaChaPolyDecipher, GenericDecipher } = binding);
- } catch {}
- const CIPHER_STREAM = 1 << 0;
- const CIPHER_INFO = (() => {
- function info(sslName, blockLen, keyLen, ivLen, authLen, discardLen, flags) {
- return {
- sslName,
- blockLen,
- keyLen,
- ivLen: (ivLen !== 0 || (flags & CIPHER_STREAM)
- ? ivLen
- : blockLen),
- authLen,
- discardLen,
- stream: !!(flags & CIPHER_STREAM),
- };
- }
- return {
- 'chacha20-poly1305@openssh.com':
- info('chacha20', 8, 64, 0, 16, 0, CIPHER_STREAM),
- 'aes128-gcm': info('aes-128-gcm', 16, 16, 12, 16, 0, CIPHER_STREAM),
- 'aes256-gcm': info('aes-256-gcm', 16, 32, 12, 16, 0, CIPHER_STREAM),
- 'aes128-gcm@openssh.com':
- info('aes-128-gcm', 16, 16, 12, 16, 0, CIPHER_STREAM),
- 'aes256-gcm@openssh.com':
- info('aes-256-gcm', 16, 32, 12, 16, 0, CIPHER_STREAM),
- 'aes128-cbc': info('aes-128-cbc', 16, 16, 0, 0, 0, 0),
- 'aes192-cbc': info('aes-192-cbc', 16, 24, 0, 0, 0, 0),
- 'aes256-cbc': info('aes-256-cbc', 16, 32, 0, 0, 0, 0),
- 'rijndael-cbc@lysator.liu.se': info('aes-256-cbc', 16, 32, 0, 0, 0, 0),
- '3des-cbc': info('des-ede3-cbc', 8, 24, 0, 0, 0, 0),
- 'blowfish-cbc': info('bf-cbc', 8, 16, 0, 0, 0, 0),
- 'idea-cbc': info('idea-cbc', 8, 16, 0, 0, 0, 0),
- 'cast128-cbc': info('cast-cbc', 8, 16, 0, 0, 0, 0),
- 'aes128-ctr': info('aes-128-ctr', 16, 16, 16, 0, 0, CIPHER_STREAM),
- 'aes192-ctr': info('aes-192-ctr', 16, 24, 16, 0, 0, CIPHER_STREAM),
- 'aes256-ctr': info('aes-256-ctr', 16, 32, 16, 0, 0, CIPHER_STREAM),
- '3des-ctr': info('des-ede3', 8, 24, 8, 0, 0, CIPHER_STREAM),
- 'blowfish-ctr': info('bf-ecb', 8, 16, 8, 0, 0, CIPHER_STREAM),
- 'cast128-ctr': info('cast5-ecb', 8, 16, 8, 0, 0, CIPHER_STREAM),
- /* The "arcfour128" algorithm is the RC4 cipher, as described in
- [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
- generated by the cipher MUST be discarded, and the first byte of the
- first encrypted packet MUST be encrypted using the 1537th byte of
- keystream.
- -- http://tools.ietf.org/html/rfc4345#section-4 */
- 'arcfour': info('rc4', 8, 16, 0, 0, 1536, CIPHER_STREAM),
- 'arcfour128': info('rc4', 8, 16, 0, 0, 1536, CIPHER_STREAM),
- 'arcfour256': info('rc4', 8, 32, 0, 0, 1536, CIPHER_STREAM),
- 'arcfour512': info('rc4', 8, 64, 0, 0, 1536, CIPHER_STREAM),
- };
- })();
- const MAC_INFO = (() => {
- function info(sslName, len, actualLen, isETM) {
- return {
- sslName,
- len,
- actualLen,
- isETM,
- };
- }
- return {
- 'hmac-md5': info('md5', 16, 16, false),
- 'hmac-md5-96': info('md5', 16, 12, false),
- 'hmac-ripemd160': info('ripemd160', 20, 20, false),
- 'hmac-sha1': info('sha1', 20, 20, false),
- 'hmac-sha1-etm@openssh.com': info('sha1', 20, 20, true),
- 'hmac-sha1-96': info('sha1', 20, 12, false),
- 'hmac-sha2-256': info('sha256', 32, 32, false),
- 'hmac-sha2-256-etm@openssh.com': info('sha256', 32, 32, true),
- 'hmac-sha2-256-96': info('sha256', 32, 12, false),
- 'hmac-sha2-512': info('sha512', 64, 64, false),
- 'hmac-sha2-512-etm@openssh.com': info('sha512', 64, 64, true),
- 'hmac-sha2-512-96': info('sha512', 64, 12, false),
- };
- })();
- // Should only_be used during the initial handshake
- class NullCipher {
- constructor(seqno, onWrite) {
- this.outSeqno = seqno;
- this._onWrite = onWrite;
- this._dead = false;
- }
- free() {
- this._dead = true;
- }
- allocPacket(payloadLen) {
- let pktLen = 4 + 1 + payloadLen;
- let padLen = 8 - (pktLen & (8 - 1));
- if (padLen < 4)
- padLen += 8;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- this._onWrite(packet);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- const POLY1305_ZEROS = Buffer.alloc(32);
- const POLY1305_OUT_COMPUTE = Buffer.alloc(16);
- let POLY1305_WASM_MODULE;
- let POLY1305_RESULT_MALLOC;
- let poly1305_auth;
- class ChaChaPolyCipherNative {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._encKeyMain = enc.cipherKey.slice(0, 32);
- this._encKeyPktLen = enc.cipherKey.slice(32);
- this._dead = false;
- }
- free() {
- this._dead = true;
- }
- allocPacket(payloadLen) {
- let pktLen = 4 + 1 + payloadLen;
- let padLen = 8 - ((pktLen - 4) & (8 - 1));
- if (padLen < 4)
- padLen += 8;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- // Generate Poly1305 key
- POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
- writeUInt32BE(POLY1305_OUT_COMPUTE, this.outSeqno, 12);
- const polyKey =
- createCipheriv('chacha20', this._encKeyMain, POLY1305_OUT_COMPUTE)
- .update(POLY1305_ZEROS);
- // Encrypt packet length
- const pktLenEnc =
- createCipheriv('chacha20', this._encKeyPktLen, POLY1305_OUT_COMPUTE)
- .update(packet.slice(0, 4));
- this._onWrite(pktLenEnc);
- // Encrypt rest of packet
- POLY1305_OUT_COMPUTE[0] = 1; // Set counter to 1 (little endian)
- const payloadEnc =
- createCipheriv('chacha20', this._encKeyMain, POLY1305_OUT_COMPUTE)
- .update(packet.slice(4));
- this._onWrite(payloadEnc);
- // Calculate Poly1305 MAC
- poly1305_auth(POLY1305_RESULT_MALLOC,
- pktLenEnc,
- pktLenEnc.length,
- payloadEnc,
- payloadEnc.length,
- polyKey);
- const mac = Buffer.allocUnsafe(16);
- mac.set(
- new Uint8Array(POLY1305_WASM_MODULE.HEAPU8.buffer,
- POLY1305_RESULT_MALLOC,
- 16),
- 0
- );
- this._onWrite(mac);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class ChaChaPolyCipherBinding {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._instance = new ChaChaPolyCipher(enc.cipherKey);
- this._dead = false;
- }
- free() {
- this._dead = true;
- this._instance.free();
- }
- allocPacket(payloadLen) {
- let pktLen = 4 + 1 + payloadLen;
- let padLen = 8 - ((pktLen - 4) & (8 - 1));
- if (padLen < 4)
- padLen += 8;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen + 16/* MAC */);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- // Encrypts in-place
- this._instance.encrypt(packet, this.outSeqno);
- this._onWrite(packet);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class AESGCMCipherNative {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._encSSLName = enc.cipherInfo.sslName;
- this._encKey = enc.cipherKey;
- this._encIV = enc.cipherIV;
- this._dead = false;
- }
- free() {
- this._dead = true;
- }
- allocPacket(payloadLen) {
- let pktLen = 4 + 1 + payloadLen;
- let padLen = 16 - ((pktLen - 4) & (16 - 1));
- if (padLen < 4)
- padLen += 16;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- const cipher = createCipheriv(this._encSSLName, this._encKey, this._encIV);
- cipher.setAutoPadding(false);
- const lenData = packet.slice(0, 4);
- cipher.setAAD(lenData);
- this._onWrite(lenData);
- // Encrypt pad length, payload, and padding
- const encrypted = cipher.update(packet.slice(4));
- this._onWrite(encrypted);
- const final = cipher.final();
- // XXX: final.length === 0 always?
- if (final.length)
- this._onWrite(final);
- // Generate MAC
- const tag = cipher.getAuthTag();
- this._onWrite(tag);
- // Increment counter in IV by 1 for next packet
- ivIncrement(this._encIV);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class AESGCMCipherBinding {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._instance = new AESGCMCipher(enc.cipherInfo.sslName,
- enc.cipherKey,
- enc.cipherIV);
- this._dead = false;
- }
- free() {
- this._dead = true;
- this._instance.free();
- }
- allocPacket(payloadLen) {
- let pktLen = 4 + 1 + payloadLen;
- let padLen = 16 - ((pktLen - 4) & (16 - 1));
- if (padLen < 4)
- padLen += 16;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen + 16/* authTag */);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- // Encrypts in-place
- this._instance.encrypt(packet);
- this._onWrite(packet);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class GenericCipherNative {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._encBlockLen = enc.cipherInfo.blockLen;
- this._cipherInstance = createCipheriv(enc.cipherInfo.sslName,
- enc.cipherKey,
- enc.cipherIV);
- this._macSSLName = enc.macInfo.sslName;
- this._macKey = enc.macKey;
- this._macActualLen = enc.macInfo.actualLen;
- this._macETM = enc.macInfo.isETM;
- this._aadLen = (this._macETM ? 4 : 0);
- this._dead = false;
- const discardLen = enc.cipherInfo.discardLen;
- if (discardLen) {
- let discard = DISCARD_CACHE.get(discardLen);
- if (discard === undefined) {
- discard = Buffer.alloc(discardLen);
- DISCARD_CACHE.set(discardLen, discard);
- }
- this._cipherInstance.update(discard);
- }
- }
- free() {
- this._dead = true;
- }
- allocPacket(payloadLen) {
- const blockLen = this._encBlockLen;
- let pktLen = 4 + 1 + payloadLen;
- let padLen = blockLen - ((pktLen - this._aadLen) & (blockLen - 1));
- if (padLen < 4)
- padLen += blockLen;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- let mac;
- if (this._macETM) {
- // Encrypt pad length, payload, and padding
- const lenBytes = new Uint8Array(packet.buffer, packet.byteOffset, 4);
- const encrypted = this._cipherInstance.update(
- new Uint8Array(packet.buffer,
- packet.byteOffset + 4,
- packet.length - 4)
- );
- this._onWrite(lenBytes);
- this._onWrite(encrypted);
- // TODO: look into storing seqno as 4-byte buffer and incrementing like we
- // do for AES-GCM IVs to avoid having to (re)write all 4 bytes every time
- mac = createHmac(this._macSSLName, this._macKey);
- writeUInt32BE(BUF_INT, this.outSeqno, 0);
- mac.update(BUF_INT);
- mac.update(lenBytes);
- mac.update(encrypted);
- } else {
- // Encrypt length field, pad length, payload, and padding
- const encrypted = this._cipherInstance.update(packet);
- this._onWrite(encrypted);
- // TODO: look into storing seqno as 4-byte buffer and incrementing like we
- // do for AES-GCM IVs to avoid having to (re)write all 4 bytes every time
- mac = createHmac(this._macSSLName, this._macKey);
- writeUInt32BE(BUF_INT, this.outSeqno, 0);
- mac.update(BUF_INT);
- mac.update(packet);
- }
- let digest = mac.digest();
- if (digest.length > this._macActualLen)
- digest = digest.slice(0, this._macActualLen);
- this._onWrite(digest);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class GenericCipherBinding {
- constructor(config) {
- const enc = config.outbound;
- this.outSeqno = enc.seqno;
- this._onWrite = enc.onWrite;
- this._encBlockLen = enc.cipherInfo.blockLen;
- this._macLen = enc.macInfo.len;
- this._macActualLen = enc.macInfo.actualLen;
- this._aadLen = (enc.macInfo.isETM ? 4 : 0);
- this._instance = new GenericCipher(enc.cipherInfo.sslName,
- enc.cipherKey,
- enc.cipherIV,
- enc.macInfo.sslName,
- enc.macKey,
- enc.macInfo.isETM);
- this._dead = false;
- }
- free() {
- this._dead = true;
- this._instance.free();
- }
- allocPacket(payloadLen) {
- const blockLen = this._encBlockLen;
- let pktLen = 4 + 1 + payloadLen;
- let padLen = blockLen - ((pktLen - this._aadLen) & (blockLen - 1));
- if (padLen < 4)
- padLen += blockLen;
- pktLen += padLen;
- const packet = Buffer.allocUnsafe(pktLen + this._macLen);
- writeUInt32BE(packet, pktLen - 4, 0);
- packet[4] = padLen;
- randomFillSync(packet, 5 + payloadLen, padLen);
- return packet;
- }
- encrypt(packet) {
- // `packet` === unencrypted packet
- if (this._dead)
- return;
- // Encrypts in-place
- this._instance.encrypt(packet, this.outSeqno);
- if (this._macActualLen < this._macLen) {
- packet = new FastBuffer(packet.buffer,
- packet.byteOffset,
- (packet.length
- - (this._macLen - this._macActualLen)));
- }
- this._onWrite(packet);
- this.outSeqno = (this.outSeqno + 1) >>> 0;
- }
- }
- class NullDecipher {
- constructor(seqno, onPayload) {
- this.inSeqno = seqno;
- this._onPayload = onPayload;
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._packetPos = 0;
- }
- free() {}
- decrypt(data, p, dataLen) {
- while (p < dataLen) {
- // Read packet length
- if (this._lenBytes < 4) {
- let nb = Math.min(4 - this._lenBytes, dataLen - p);
- this._lenBytes += nb;
- while (nb--)
- this._len = (this._len << 8) + data[p++];
- if (this._lenBytes < 4)
- return;
- if (this._len > MAX_PACKET_SIZE
- || this._len < 8
- || (4 + this._len & 7) !== 0) {
- throw new Error('Bad packet length');
- }
- if (p >= dataLen)
- return;
- }
- // Read padding length, payload, and padding
- if (this._packetPos < this._len) {
- const nb = Math.min(this._len - this._packetPos, dataLen - p);
- if (p !== 0 || nb !== dataLen) {
- if (nb === this._len) {
- this._packet = new FastBuffer(data.buffer, data.byteOffset + p, nb);
- } else {
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._packetPos
- );
- }
- } else if (nb === this._len) {
- this._packet = data;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(data, this._packetPos);
- }
- p += nb;
- this._packetPos += nb;
- if (this._packetPos < this._len)
- return;
- }
- const payload = (!this._packet
- ? EMPTY_BUFFER
- : new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length
- - this._packet[0] - 1));
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._packetPos = 0;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- class ChaChaPolyDecipherNative {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._decKeyMain = dec.decipherKey.slice(0, 32);
- this._decKeyPktLen = dec.decipherKey.slice(32);
- this._len = 0;
- this._lenBuf = Buffer.alloc(4);
- this._lenPos = 0;
- this._packet = null;
- this._pktLen = 0;
- this._mac = Buffer.allocUnsafe(16);
- this._calcMac = Buffer.allocUnsafe(16);
- this._macPos = 0;
- }
- free() {}
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read packet length
- if (this._lenPos < 4) {
- let nb = Math.min(4 - this._lenPos, dataLen - p);
- while (nb--)
- this._lenBuf[this._lenPos++] = data[p++];
- if (this._lenPos < 4)
- return;
- POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
- writeUInt32BE(POLY1305_OUT_COMPUTE, this.inSeqno, 12);
- const decLenBytes =
- createDecipheriv('chacha20', this._decKeyPktLen, POLY1305_OUT_COMPUTE)
- .update(this._lenBuf);
- this._len = readUInt32BE(decLenBytes, 0);
- if (this._len > MAX_PACKET_SIZE
- || this._len < 8
- || (this._len & 7) !== 0) {
- throw new Error('Bad packet length');
- }
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- if (p >= dataLen)
- return;
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let encrypted;
- if (p !== 0 || nb !== dataLen)
- encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
- else
- encrypted = data;
- if (nb === this._len) {
- this._packet = encrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(encrypted, this._pktLen);
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read Poly1305 MAC
- {
- const nb = Math.min(16 - this._macPos, dataLen - p);
- // TODO: avoid copying if entire MAC is in current chunk
- if (p !== 0 || nb !== dataLen) {
- this._mac.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._macPos
- );
- } else {
- this._mac.set(data, this._macPos);
- }
- p += nb;
- this._macPos += nb;
- if (this._macPos < 16)
- return;
- }
- // Generate Poly1305 key
- POLY1305_OUT_COMPUTE[0] = 0; // Set counter to 0 (little endian)
- writeUInt32BE(POLY1305_OUT_COMPUTE, this.inSeqno, 12);
- const polyKey =
- createCipheriv('chacha20', this._decKeyMain, POLY1305_OUT_COMPUTE)
- .update(POLY1305_ZEROS);
- // Calculate and compare Poly1305 MACs
- poly1305_auth(POLY1305_RESULT_MALLOC,
- this._lenBuf,
- 4,
- this._packet,
- this._packet.length,
- polyKey);
- this._calcMac.set(
- new Uint8Array(POLY1305_WASM_MODULE.HEAPU8.buffer,
- POLY1305_RESULT_MALLOC,
- 16),
- 0
- );
- if (!timingSafeEqual(this._calcMac, this._mac))
- throw new Error('Invalid MAC');
- // Decrypt packet
- POLY1305_OUT_COMPUTE[0] = 1; // Set counter to 1 (little endian)
- const packet =
- createDecipheriv('chacha20', this._decKeyMain, POLY1305_OUT_COMPUTE)
- .update(this._packet);
- const payload = new FastBuffer(packet.buffer,
- packet.byteOffset + 1,
- packet.length - packet[0] - 1);
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._len = 0;
- this._lenPos = 0;
- this._packet = null;
- this._pktLen = 0;
- this._macPos = 0;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- class ChaChaPolyDecipherBinding {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._instance = new ChaChaPolyDecipher(dec.decipherKey);
- this._len = 0;
- this._lenBuf = Buffer.alloc(4);
- this._lenPos = 0;
- this._packet = null;
- this._pktLen = 0;
- this._mac = Buffer.allocUnsafe(16);
- this._macPos = 0;
- }
- free() {
- this._instance.free();
- }
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read packet length
- if (this._lenPos < 4) {
- let nb = Math.min(4 - this._lenPos, dataLen - p);
- while (nb--)
- this._lenBuf[this._lenPos++] = data[p++];
- if (this._lenPos < 4)
- return;
- this._len = this._instance.decryptLen(this._lenBuf, this.inSeqno);
- if (this._len > MAX_PACKET_SIZE
- || this._len < 8
- || (this._len & 7) !== 0) {
- throw new Error('Bad packet length');
- }
- if (p >= dataLen)
- return;
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let encrypted;
- if (p !== 0 || nb !== dataLen)
- encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
- else
- encrypted = data;
- if (nb === this._len) {
- this._packet = encrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(encrypted, this._pktLen);
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read Poly1305 MAC
- {
- const nb = Math.min(16 - this._macPos, dataLen - p);
- // TODO: avoid copying if entire MAC is in current chunk
- if (p !== 0 || nb !== dataLen) {
- this._mac.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._macPos
- );
- } else {
- this._mac.set(data, this._macPos);
- }
- p += nb;
- this._macPos += nb;
- if (this._macPos < 16)
- return;
- }
- this._instance.decrypt(this._packet, this._mac, this.inSeqno);
- const payload = new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length - this._packet[0] - 1);
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._len = 0;
- this._lenPos = 0;
- this._packet = null;
- this._pktLen = 0;
- this._macPos = 0;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- class AESGCMDecipherNative {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._decipherInstance = null;
- this._decipherSSLName = dec.decipherInfo.sslName;
- this._decipherKey = dec.decipherKey;
- this._decipherIV = dec.decipherIV;
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._packetPos = 0;
- this._pktLen = 0;
- this._tag = Buffer.allocUnsafe(16);
- this._tagPos = 0;
- }
- free() {}
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read packet length (unencrypted, but AAD)
- if (this._lenBytes < 4) {
- let nb = Math.min(4 - this._lenBytes, dataLen - p);
- this._lenBytes += nb;
- while (nb--)
- this._len = (this._len << 8) + data[p++];
- if (this._lenBytes < 4)
- return;
- if ((this._len + 20) > MAX_PACKET_SIZE
- || this._len < 16
- || (this._len & 15) !== 0) {
- throw new Error('Bad packet length');
- }
- this._decipherInstance = createDecipheriv(
- this._decipherSSLName,
- this._decipherKey,
- this._decipherIV
- );
- this._decipherInstance.setAutoPadding(false);
- this._decipherInstance.setAAD(intToBytes(this._len));
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- if (p >= dataLen)
- return;
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let decrypted;
- if (p !== 0 || nb !== dataLen) {
- decrypted = this._decipherInstance.update(
- new Uint8Array(data.buffer, data.byteOffset + p, nb)
- );
- } else {
- decrypted = this._decipherInstance.update(data);
- }
- if (decrypted.length) {
- if (nb === this._len) {
- this._packet = decrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(decrypted, this._packetPos);
- }
- this._packetPos += decrypted.length;
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read authentication tag
- {
- const nb = Math.min(16 - this._tagPos, dataLen - p);
- if (p !== 0 || nb !== dataLen) {
- this._tag.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._tagPos
- );
- } else {
- this._tag.set(data, this._tagPos);
- }
- p += nb;
- this._tagPos += nb;
- if (this._tagPos < 16)
- return;
- }
- {
- // Verify authentication tag
- this._decipherInstance.setAuthTag(this._tag);
- const decrypted = this._decipherInstance.final();
- // XXX: this should never output any data since stream ciphers always
- // return data from .update() and block ciphers must end on a multiple
- // of the block length, which would have caused an exception to be
- // thrown if the total input was not...
- if (decrypted.length) {
- if (this._packet)
- this._packet.set(decrypted, this._packetPos);
- else
- this._packet = decrypted;
- }
- }
- const payload = (!this._packet
- ? EMPTY_BUFFER
- : new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length
- - this._packet[0] - 1));
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- ivIncrement(this._decipherIV);
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._packetPos = 0;
- this._pktLen = 0;
- this._tagPos = 0;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- class AESGCMDecipherBinding {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._instance = new AESGCMDecipher(dec.decipherInfo.sslName,
- dec.decipherKey,
- dec.decipherIV);
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._pktLen = 0;
- this._tag = Buffer.allocUnsafe(16);
- this._tagPos = 0;
- }
- free() {}
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read packet length (unencrypted, but AAD)
- if (this._lenBytes < 4) {
- let nb = Math.min(4 - this._lenBytes, dataLen - p);
- this._lenBytes += nb;
- while (nb--)
- this._len = (this._len << 8) + data[p++];
- if (this._lenBytes < 4)
- return;
- if ((this._len + 20) > MAX_PACKET_SIZE
- || this._len < 16
- || (this._len & 15) !== 0) {
- throw new Error(`Bad packet length: ${this._len}`);
- }
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- if (p >= dataLen)
- return;
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let encrypted;
- if (p !== 0 || nb !== dataLen)
- encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
- else
- encrypted = data;
- if (nb === this._len) {
- this._packet = encrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(encrypted, this._pktLen);
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read authentication tag
- {
- const nb = Math.min(16 - this._tagPos, dataLen - p);
- if (p !== 0 || nb !== dataLen) {
- this._tag.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._tagPos
- );
- } else {
- this._tag.set(data, this._tagPos);
- }
- p += nb;
- this._tagPos += nb;
- if (this._tagPos < 16)
- return;
- }
- this._instance.decrypt(this._packet, this._len, this._tag);
- const payload = new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length - this._packet[0] - 1);
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._len = 0;
- this._lenBytes = 0;
- this._packet = null;
- this._pktLen = 0;
- this._tagPos = 0;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- // TODO: test incremental .update()s vs. copying to _packet and doing a single
- // .update() after entire packet read -- a single .update() would allow
- // verifying MAC before decrypting for ETM MACs
- class GenericDecipherNative {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._decipherInstance = createDecipheriv(dec.decipherInfo.sslName,
- dec.decipherKey,
- dec.decipherIV);
- this._decipherInstance.setAutoPadding(false);
- this._block = Buffer.allocUnsafe(
- dec.macInfo.isETM ? 4 : dec.decipherInfo.blockLen
- );
- this._blockSize = dec.decipherInfo.blockLen;
- this._blockPos = 0;
- this._len = 0;
- this._packet = null;
- this._packetPos = 0;
- this._pktLen = 0;
- this._mac = Buffer.allocUnsafe(dec.macInfo.actualLen);
- this._macPos = 0;
- this._macSSLName = dec.macInfo.sslName;
- this._macKey = dec.macKey;
- this._macActualLen = dec.macInfo.actualLen;
- this._macETM = dec.macInfo.isETM;
- this._macInstance = null;
- const discardLen = dec.decipherInfo.discardLen;
- if (discardLen) {
- let discard = DISCARD_CACHE.get(discardLen);
- if (discard === undefined) {
- discard = Buffer.alloc(discardLen);
- DISCARD_CACHE.set(discardLen, discard);
- }
- this._decipherInstance.update(discard);
- }
- }
- free() {}
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read first encrypted block
- if (this._blockPos < this._block.length) {
- const nb = Math.min(this._block.length - this._blockPos, dataLen - p);
- if (p !== 0 || nb !== dataLen || nb < data.length) {
- this._block.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._blockPos
- );
- } else {
- this._block.set(data, this._blockPos);
- }
- p += nb;
- this._blockPos += nb;
- if (this._blockPos < this._block.length)
- return;
- let decrypted;
- let need;
- if (this._macETM) {
- this._len = need = readUInt32BE(this._block, 0);
- } else {
- // Decrypt first block to get packet length
- decrypted = this._decipherInstance.update(this._block);
- this._len = readUInt32BE(decrypted, 0);
- need = 4 + this._len - this._blockSize;
- }
- if (this._len > MAX_PACKET_SIZE
- || this._len < 5
- || (need & (this._blockSize - 1)) !== 0) {
- throw new Error('Bad packet length');
- }
- // Create MAC up front to calculate in parallel with decryption
- this._macInstance = createHmac(this._macSSLName, this._macKey);
- writeUInt32BE(BUF_INT, this.inSeqno, 0);
- this._macInstance.update(BUF_INT);
- if (this._macETM) {
- this._macInstance.update(this._block);
- } else {
- this._macInstance.update(new Uint8Array(decrypted.buffer,
- decrypted.byteOffset,
- 4));
- this._pktLen = decrypted.length - 4;
- this._packetPos = this._pktLen;
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(
- new Uint8Array(decrypted.buffer,
- decrypted.byteOffset + 4,
- this._packetPos),
- 0
- );
- }
- if (p >= dataLen)
- return;
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let encrypted;
- if (p !== 0 || nb !== dataLen)
- encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
- else
- encrypted = data;
- if (this._macETM)
- this._macInstance.update(encrypted);
- const decrypted = this._decipherInstance.update(encrypted);
- if (decrypted.length) {
- if (nb === this._len) {
- this._packet = decrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(decrypted, this._packetPos);
- }
- this._packetPos += decrypted.length;
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read MAC
- {
- const nb = Math.min(this._macActualLen - this._macPos, dataLen - p);
- if (p !== 0 || nb !== dataLen) {
- this._mac.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._macPos
- );
- } else {
- this._mac.set(data, this._macPos);
- }
- p += nb;
- this._macPos += nb;
- if (this._macPos < this._macActualLen)
- return;
- }
- // Verify MAC
- if (!this._macETM)
- this._macInstance.update(this._packet);
- let calculated = this._macInstance.digest();
- if (this._macActualLen < calculated.length) {
- calculated = new Uint8Array(calculated.buffer,
- calculated.byteOffset,
- this._macActualLen);
- }
- if (!timingSafeEquals(calculated, this._mac))
- throw new Error('Invalid MAC');
- const payload = new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length - this._packet[0] - 1);
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._blockPos = 0;
- this._len = 0;
- this._packet = null;
- this._packetPos = 0;
- this._pktLen = 0;
- this._macPos = 0;
- this._macInstance = null;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- class GenericDecipherBinding {
- constructor(config) {
- const dec = config.inbound;
- this.inSeqno = dec.seqno;
- this._onPayload = dec.onPayload;
- this._instance = new GenericDecipher(dec.decipherInfo.sslName,
- dec.decipherKey,
- dec.decipherIV,
- dec.macInfo.sslName,
- dec.macKey,
- dec.macInfo.isETM,
- dec.macInfo.actualLen);
- this._block = Buffer.allocUnsafe(
- dec.macInfo.isETM || dec.decipherInfo.stream
- ? 4
- : dec.decipherInfo.blockLen
- );
- this._blockPos = 0;
- this._len = 0;
- this._packet = null;
- this._pktLen = 0;
- this._mac = Buffer.allocUnsafe(dec.macInfo.actualLen);
- this._macPos = 0;
- this._macActualLen = dec.macInfo.actualLen;
- this._macETM = dec.macInfo.isETM;
- }
- free() {
- this._instance.free();
- }
- decrypt(data, p, dataLen) {
- // `data` === encrypted data
- while (p < dataLen) {
- // Read first encrypted block
- if (this._blockPos < this._block.length) {
- const nb = Math.min(this._block.length - this._blockPos, dataLen - p);
- if (p !== 0 || nb !== dataLen || nb < data.length) {
- this._block.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._blockPos
- );
- } else {
- this._block.set(data, this._blockPos);
- }
- p += nb;
- this._blockPos += nb;
- if (this._blockPos < this._block.length)
- return;
- let need;
- if (this._macETM) {
- this._len = need = readUInt32BE(this._block, 0);
- } else {
- // Decrypt first block to get packet length
- this._instance.decryptBlock(this._block);
- this._len = readUInt32BE(this._block, 0);
- need = 4 + this._len - this._block.length;
- }
- if (this._len > MAX_PACKET_SIZE
- || this._len < 5
- || (need & (this._block.length - 1)) !== 0) {
- throw new Error('Bad packet length');
- }
- if (!this._macETM) {
- this._pktLen = (this._block.length - 4);
- if (this._pktLen) {
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(
- new Uint8Array(this._block.buffer,
- this._block.byteOffset + 4,
- this._pktLen),
- 0
- );
- }
- }
- if (p >= dataLen)
- return;
- }
- // Read padding length, payload, and padding
- if (this._pktLen < this._len) {
- const nb = Math.min(this._len - this._pktLen, dataLen - p);
- let encrypted;
- if (p !== 0 || nb !== dataLen)
- encrypted = new Uint8Array(data.buffer, data.byteOffset + p, nb);
- else
- encrypted = data;
- if (nb === this._len) {
- this._packet = encrypted;
- } else {
- if (!this._packet)
- this._packet = Buffer.allocUnsafe(this._len);
- this._packet.set(encrypted, this._pktLen);
- }
- p += nb;
- this._pktLen += nb;
- if (this._pktLen < this._len || p >= dataLen)
- return;
- }
- // Read MAC
- {
- const nb = Math.min(this._macActualLen - this._macPos, dataLen - p);
- if (p !== 0 || nb !== dataLen) {
- this._mac.set(
- new Uint8Array(data.buffer, data.byteOffset + p, nb),
- this._macPos
- );
- } else {
- this._mac.set(data, this._macPos);
- }
- p += nb;
- this._macPos += nb;
- if (this._macPos < this._macActualLen)
- return;
- }
- // Decrypt and verify MAC
- this._instance.decrypt(this._packet,
- this.inSeqno,
- this._block,
- this._mac);
- const payload = new FastBuffer(this._packet.buffer,
- this._packet.byteOffset + 1,
- this._packet.length - this._packet[0] - 1);
- // Prepare for next packet
- this.inSeqno = (this.inSeqno + 1) >>> 0;
- this._blockPos = 0;
- this._len = 0;
- this._packet = null;
- this._pktLen = 0;
- this._macPos = 0;
- this._macInstance = null;
- {
- const ret = this._onPayload(payload);
- if (ret !== undefined)
- return (ret === false ? p : ret);
- }
- }
- }
- }
- // Increments unsigned, big endian counter (last 8 bytes) of AES-GCM IV
- function ivIncrement(iv) {
- // eslint-disable-next-line no-unused-expressions
- ++iv[11] >>> 8
- && ++iv[10] >>> 8
- && ++iv[9] >>> 8
- && ++iv[8] >>> 8
- && ++iv[7] >>> 8
- && ++iv[6] >>> 8
- && ++iv[5] >>> 8
- && ++iv[4] >>> 8;
- }
- const intToBytes = (() => {
- const ret = Buffer.alloc(4);
- return (n) => {
- ret[0] = (n >>> 24);
- ret[1] = (n >>> 16);
- ret[2] = (n >>> 8);
- ret[3] = n;
- return ret;
- };
- })();
- function timingSafeEquals(a, b) {
- if (a.length !== b.length) {
- timingSafeEqual(a, a);
- return false;
- }
- return timingSafeEqual(a, b);
- }
- function createCipher(config) {
- if (typeof config !== 'object' || config === null)
- throw new Error('Invalid config');
- if (typeof config.outbound !== 'object' || config.outbound === null)
- throw new Error('Invalid outbound');
- const outbound = config.outbound;
- if (typeof outbound.onWrite !== 'function')
- throw new Error('Invalid outbound.onWrite');
- if (typeof outbound.cipherInfo !== 'object' || outbound.cipherInfo === null)
- throw new Error('Invalid outbound.cipherInfo');
- if (!Buffer.isBuffer(outbound.cipherKey)
- || outbound.cipherKey.length !== outbound.cipherInfo.keyLen) {
- throw new Error('Invalid outbound.cipherKey');
- }
- if (outbound.cipherInfo.ivLen
- && (!Buffer.isBuffer(outbound.cipherIV)
- || outbound.cipherIV.length !== outbound.cipherInfo.ivLen)) {
- throw new Error('Invalid outbound.cipherIV');
- }
- if (typeof outbound.seqno !== 'number'
- || outbound.seqno < 0
- || outbound.seqno > MAX_SEQNO) {
- throw new Error('Invalid outbound.seqno');
- }
- const forceNative = !!outbound.forceNative;
- switch (outbound.cipherInfo.sslName) {
- case 'aes-128-gcm':
- case 'aes-256-gcm':
- return (AESGCMCipher && !forceNative
- ? new AESGCMCipherBinding(config)
- : new AESGCMCipherNative(config));
- case 'chacha20':
- return (ChaChaPolyCipher && !forceNative
- ? new ChaChaPolyCipherBinding(config)
- : new ChaChaPolyCipherNative(config));
- default: {
- if (typeof outbound.macInfo !== 'object' || outbound.macInfo === null)
- throw new Error('Invalid outbound.macInfo');
- if (!Buffer.isBuffer(outbound.macKey)
- || outbound.macKey.length !== outbound.macInfo.len) {
- throw new Error('Invalid outbound.macKey');
- }
- return (GenericCipher && !forceNative
- ? new GenericCipherBinding(config)
- : new GenericCipherNative(config));
- }
- }
- }
- function createDecipher(config) {
- if (typeof config !== 'object' || config === null)
- throw new Error('Invalid config');
- if (typeof config.inbound !== 'object' || config.inbound === null)
- throw new Error('Invalid inbound');
- const inbound = config.inbound;
- if (typeof inbound.onPayload !== 'function')
- throw new Error('Invalid inbound.onPayload');
- if (typeof inbound.decipherInfo !== 'object'
- || inbound.decipherInfo === null) {
- throw new Error('Invalid inbound.decipherInfo');
- }
- if (!Buffer.isBuffer(inbound.decipherKey)
- || inbound.decipherKey.length !== inbound.decipherInfo.keyLen) {
- throw new Error('Invalid inbound.decipherKey');
- }
- if (inbound.decipherInfo.ivLen
- && (!Buffer.isBuffer(inbound.decipherIV)
- || inbound.decipherIV.length !== inbound.decipherInfo.ivLen)) {
- throw new Error('Invalid inbound.decipherIV');
- }
- if (typeof inbound.seqno !== 'number'
- || inbound.seqno < 0
- || inbound.seqno > MAX_SEQNO) {
- throw new Error('Invalid inbound.seqno');
- }
- const forceNative = !!inbound.forceNative;
- switch (inbound.decipherInfo.sslName) {
- case 'aes-128-gcm':
- case 'aes-256-gcm':
- return (AESGCMDecipher && !forceNative
- ? new AESGCMDecipherBinding(config)
- : new AESGCMDecipherNative(config));
- case 'chacha20':
- return (ChaChaPolyDecipher && !forceNative
- ? new ChaChaPolyDecipherBinding(config)
- : new ChaChaPolyDecipherNative(config));
- default: {
- if (typeof inbound.macInfo !== 'object' || inbound.macInfo === null)
- throw new Error('Invalid inbound.macInfo');
- if (!Buffer.isBuffer(inbound.macKey)
- || inbound.macKey.length !== inbound.macInfo.len) {
- throw new Error('Invalid inbound.macKey');
- }
- return (GenericDecipher && !forceNative
- ? new GenericDecipherBinding(config)
- : new GenericDecipherNative(config));
- }
- }
- }
- module.exports = {
- CIPHER_INFO,
- MAC_INFO,
- bindingAvailable: !!binding,
- init: (() => {
- // eslint-disable-next-line no-async-promise-executor
- return new Promise(async (resolve, reject) => {
- try {
- POLY1305_WASM_MODULE = await require('./crypto/poly1305.js')();
- POLY1305_RESULT_MALLOC = POLY1305_WASM_MODULE._malloc(16);
- poly1305_auth = POLY1305_WASM_MODULE.cwrap(
- 'poly1305_auth',
- null,
- ['number', 'array', 'number', 'array', 'number', 'array']
- );
- } catch (ex) {
- return reject(ex);
- }
- resolve();
- });
- })(),
- NullCipher,
- createCipher,
- NullDecipher,
- createDecipher,
- };
|