basic version working
This commit is contained in:
commit
39f6e99abe
11 changed files with 782 additions and 0 deletions
87
src/auth.ts
Normal file
87
src/auth.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import {
|
||||
buildCommand,
|
||||
buildError,
|
||||
Command,
|
||||
parseCommand,
|
||||
} from "./commandbuilder.ts";
|
||||
import { Device } from "./device.ts";
|
||||
|
||||
function toHex(data: Uint8Array): string {
|
||||
return Array.from(data).map((b) => ("00" + b.toString(16)).slice(-2)).join(
|
||||
"",
|
||||
);
|
||||
}
|
||||
async function generateHMAC(key: string, message: string) {
|
||||
const encoder = new TextEncoder();
|
||||
const keyData = encoder.encode(key);
|
||||
const messageData = encoder.encode(message);
|
||||
|
||||
const cryptoKey = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
keyData,
|
||||
{ name: "HMAC", hash: { name: "SHA-256" } },
|
||||
false,
|
||||
["sign"],
|
||||
);
|
||||
|
||||
const signature = await crypto.subtle.sign(
|
||||
"HMAC",
|
||||
cryptoKey,
|
||||
messageData,
|
||||
);
|
||||
|
||||
return toHex(new Uint8Array(signature));
|
||||
}
|
||||
|
||||
export class Authentication {
|
||||
_socket: WebSocket;
|
||||
_encoder: TextEncoder;
|
||||
nonce: string;
|
||||
device: Device | undefined;
|
||||
signature: string | undefined;
|
||||
|
||||
constructor(socket: WebSocket) {
|
||||
this._socket = socket;
|
||||
this._encoder = new TextEncoder();
|
||||
this.nonce = toHex(crypto.getRandomValues(new Uint8Array(32)));
|
||||
}
|
||||
|
||||
async authenticate(message: Command) { // ugh, js doesn't have async constructors, hence the seeprate init function.
|
||||
if (
|
||||
message.d == undefined || !("id" in message.d) ||
|
||||
typeof (message.d.id) !== "string" || !(message.d.id in Device.devices)
|
||||
) {
|
||||
this._socket.send(buildError(1, "Invalid packet. Missing ID"));
|
||||
this._socket.close();
|
||||
return;
|
||||
}
|
||||
|
||||
this._socket.addEventListener("message", (msg) => this._validate(msg));
|
||||
this.device = Device.devices[message.d.id];
|
||||
this.signature = await generateHMAC(this.device.password, this.nonce);
|
||||
|
||||
this._socket.send(
|
||||
buildCommand("auth_nonce", { nonce: this.nonce }),
|
||||
);
|
||||
}
|
||||
|
||||
_validate(msg: MessageEvent<any>) {
|
||||
if (!this.device || !this.signature) return;
|
||||
|
||||
const parsed = parseCommand(msg.data);
|
||||
if (
|
||||
parsed === null || parsed.c !== "auth_validate" ||
|
||||
parsed.d === undefined || !("signature" in parsed.d)
|
||||
) return;
|
||||
|
||||
if (this.signature !== parsed.d.signature) {
|
||||
this._socket.send(buildError(2, "Invalid signature."));
|
||||
this._socket.close;
|
||||
return;
|
||||
}
|
||||
|
||||
this._socket.send(buildCommand("auth_ok"));
|
||||
this.device?.connect(this._socket);
|
||||
this._socket.removeEventListener("message", this._validate);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue