SenShaMart/sensor/sensor.js

108 lines
No EOL
2.7 KiB
JavaScript

const Websocket = require('ws');
const ChainUtil = require('../chain-util');
const crypto = require('crypto');
const STATE_SERVER_HELLOING = 0;
const STATE_SERVER_FINNING = 1;
const STATE_OPERATIONAL = 2;
function onServerHelloing(sensor, data) {
const serverNonce = data.toString();
if (typeof serverNonce !== 'string') {
console.log("Bad server hello");
sensor.close();
return;
}
sensor.serverNonce = serverNonce;
crypto.randomBytes(2048, (err, buf) => {
if (err) {
console.log(`Couldn't generate client nonce: ${err}`);
sensor.close();
return;
}
sensor.clientNonce = buf.toString('hex');
sensor.socket.send(JSON.stringify({
owner: sensor.keyPair.getPublic().encode('hex'),
sensor: sensor.sensorId,
signature: sensor.keyPair.sign(sensor.serverNonce + sensor.clientNonce),
clientNonce: sensor.clientNonce
}));
sensor.state = STATE_SERVER_FINNING;
});
}
function onServerFinning(sensor, data) {
const signature = JSON.parse(data);
if (typeof signature !== 'object') {
console.log("Bad server fin");
sensor.close();
return;
}
if (sensor.brokerPublicKey !== null) {
const verifyRes = ChainUtil.verifySignature(sensor.brokerPublicKey, data, sensor.clientNonce + sensor.serverNonce);
if (!verifyRes.result) {
console.log("Bad server fin singature: " + verifyRes.reason);
sensor.close();
return;
}
console.log("Broker authed, operational");
} else {
console.log("No broker public key stored, blindly trusting the broker");
}
sensor.state = STATE_OPERATIONAL;
for (const msg of sensor.queue) {
sensor.send(msg);
}
}
function onOperational(_, _) {
}
function onSocketMessage(sensor, data) {
switch (sensor.state) {
case STATE_SERVER_HELLOING: onServerHelloing(sensor, data); break;
case STATE_SERVER_FINNING: onServerFinning(sensor, data); break;
case STATE_OPERATIONAL: onOperational(sensor, data); break;
default: throw Error("Invalid internal state");
}
}
function onConnection(sensor) {
sensor.socket.on('message', (data) => {
onSocketMessage(sensor, data);
});
}
class Sensor {
constructor(keyPair, sensorId, brokerLocation, brokerPublicKey) {
this.keyPair = keyPair;
this.sensorId = sensorId;
this.brokerPublicKey = brokerPublicKey;
this.state = STATE_SERVER_HELLOING;
this.queue = [];
this.socket = new Websocket(brokerLocation);
this.socket.on('open', (_) => onConnection(this));
}
send(data) {
if (this.state != STATE_OPERATIONAL) {
this.queue.push(data);
} else {
this.socket.send(data);
}
}
close() {
this.socket.close();
}
}
module.exports = Sensor;