65 lines
2.1 KiB
TypeScript
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();
|
|
}
|
|
} |