101 lines
2.9 KiB
JavaScript
101 lines
2.9 KiB
JavaScript
|
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;
|