const crypto = require('crypto'); const Cell = require('./Cell') class CircuitNodeCryptoState { /** @type Hash */ _forward_digest = null; /** @type Hash */ _backward_digest = null; /** @type Cipher */ _backward_cipher = null; /** @type Cipher */ _forward_cipher = null; constructor (key_material) { this._forward_digest = crypto.createHash('sha1'); const df = key_material.slice(0, 20); this._forward_digest.update(df); this._backward_digest = crypto.createHash('sha1'); const db = key_material.slice(20, 20 + 20); this._backward_digest.update(db); const kf = key_material.slice(20 + 20, 20 + 20 + 16); this._forward_cipher = crypto.createCipheriv('aes-128-ctr', kf, Buffer.alloc(16)); const kb = key_material.slice(20 + 20 + 16, 20 + 20 + 16 + 16); this._backward_cipher = crypto.createCipheriv('aes-128-ctr', kb, Buffer.alloc(16)); } /** * @param {RelayCell} cell */ encrypt_forward_cell(cell) { const relay_payload_bytes = Buffer.alloc(Cell.payload_size); if (cell.payload == null) { relay_payload_bytes.writeUInt8(cell.relay_command, 0); relay_payload_bytes.writeUInt16BE(0, 1); // 'recognized' relay_payload_bytes.writeUInt16BE(cell.stream_id, 3); relay_payload_bytes.writeInt32BE(0, 5); // digest placeholder relay_payload_bytes.writeUInt16BE(cell.relay_payload.length, 9); cell.relay_payload.copy(relay_payload_bytes, 11); // // update digest field in the payload // this._forward_digest.update(relay_payload_bytes); const digest = this._forward_digest.copy().digest(); // requires node >= 12.16 digest.copy(relay_payload_bytes, 5, 0, 4); } else { cell.payload.copy(relay_payload_bytes, 0); } // // encrypt the payload // cell.payload = this._forward_cipher.update(relay_payload_bytes); // console.debug("CircuitNodeCryptoState.encrypt_forward_cell(): %s %s", // relay_payload_bytes.toString('hex'), // cell.payload.toString('hex')); } /** * @param {Cell} cell * @returns {boolean} */ decrypt_backward_cell(cell) { cell.payload = this._backward_cipher.update(cell.payload); // // check if this is a cell for us. // if (cell.is_recognized()) { // // remove the digest from the payload // const payload_without_digest = Buffer.alloc(cell.payload.length); cell.payload.copy(payload_without_digest); payload_without_digest.writeInt32BE(0, 5); const backward_digest_clone = this._backward_digest.copy(); backward_digest_clone.update(payload_without_digest); const digest = backward_digest_clone.digest(); if (digest.compare(cell.payload, 5, 5 + 4, 0, 4) === 0) { this._backward_digest.update(payload_without_digest); return true; } } return false; } } module.exports = CircuitNodeCryptoState;