cordis/client/webapp/socket-verifier.ts

65 lines
2.1 KiB
TypeScript

import * as crypto from 'crypto';
import * as socketio from 'socket.io-client';
import DedupAwaiter from "./dedup-awaiter";
import Util from './util';
export default class SocketVerifier {
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
) {
socket.on('connect', async () => {
await this.verify();
});
socket.on('disconnect', () => {
this.isVerified = false;
});
}
// TODO: Move this to a "query/queryDedup" request
/** 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);
});
});
});
}
// 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();
}
}