cordis/client/webapp/socket-verifier.ts

68 lines
2.2 KiB
TypeScript
Raw Normal View History

import * as crypto from 'crypto';
import * as socketio from 'socket.io-client';
2021-11-22 02:47:29 +00:00
import { EventEmitter } from 'tsee';
import DedupAwaiter from "./dedup-awaiter";
import Util from './util';
2021-11-21 18:29:42 +00:00
// Automatically re-verifies the socket when connected
2021-11-22 02:47:29 +00:00
export default class SocketVerifier extends EventEmitter<{ 'verified': () => void }> {
public isVerified = false;
private memberId: string | null = null;
private verifyDedup = new DedupAwaiter(async () => { return await this.doVerify(); });
constructor(
private socket: socketio.Socket,
private publicKey: crypto.KeyObject,
private privateKey: crypto.KeyObject
) {
2021-11-22 02:47:29 +00:00
super();
socket.on('connect', async () => {
await this.verify();
});
socket.on('disconnect', () => {
this.isVerified = false;
});
}
/** Verifies this client with the server. This function must be called before the server will send messages or give results */
private async doVerify(): Promise<string> {
if (this.socket.disconnected) throw new Error('socket is disconnected');
if (this.memberId) return this.memberId;
return await new Promise<string>(async (resolve, reject) => {
// Solve the server's challenge
let publicKeyBuff = this.publicKey.export({ type: 'spki', format: 'der' });
Util.socketEmitTimeout(this.socket, 5000, 'challenge', publicKeyBuff, (errMsg: string, algo: string, type: crypto.BinaryToTextEncoding, challenge: Buffer) => {
if (errMsg) {
reject(new Error('challenge request failed: ' + errMsg));
return;
}
const sign = crypto.createSign(algo);
sign.write(challenge);
sign.end();
let signature = sign.sign(this.privateKey, type);
Util.socketEmitTimeout(this.socket, 5000, 'verify', signature, (errMsg: string, memberId: string) => {
if (errMsg) {
reject(new Error('verification request failed: ' + errMsg));
return;
}
this.memberId = null;
resolve(memberId);
2021-11-22 02:47:29 +00:00
this.emit('verified');
});
});
});
}
// returns the memberId
public async verify(): Promise<string> {
return await this.verifyDedup.call();
}
// TODO: ensureVerified for send/update requests that may need to be in-order?
public async ensureVerified() {
if (this.isVerified) return;
await this.verify();
}
}