207 lines
No EOL
5.1 KiB
HTML
207 lines
No EOL
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Document</title>
|
|
<style>
|
|
body {
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 2em;
|
|
display: grid;
|
|
grid-template-columns: max-content auto;
|
|
gap: 2em;
|
|
}
|
|
|
|
.labelgrid {
|
|
display: grid;
|
|
grid-template-columns: min-content auto;
|
|
column-gap: 1em;
|
|
}
|
|
|
|
.buttongrid {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-basis: 0;
|
|
width: 100%;
|
|
padding-top: 5px;
|
|
}
|
|
|
|
.buttongrid>* {
|
|
width: 0;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
#text {
|
|
display: none;
|
|
}
|
|
|
|
#votebuttons {
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div>
|
|
<div class="labelgrid">
|
|
<label for="id"> ID</label>
|
|
<input id="id" />
|
|
|
|
<label for="passcode"> passcode</label>
|
|
<input id="passcode" />
|
|
|
|
<label for="pin"> pin</label>
|
|
<input id="pin" />
|
|
|
|
<label for="auth"> authenticated</label>
|
|
<input id="auth" type=checkbox onclick="return false" />
|
|
</div>
|
|
|
|
<div class="buttongrid" id="authbuttons">
|
|
<button onclick="startRegistration()">registrate</button>
|
|
<button onclick="startAuthentication()">authenticate</button>
|
|
</div>
|
|
|
|
<div class="buttongrid" id="votebuttons">
|
|
<button onclick="vote(1)">1</button>
|
|
<button onclick="vote(2)">2</button>
|
|
<button onclick="vote(3)">3</button>
|
|
<button onclick="vote(4)">4</button>
|
|
<button onclick="vote(5)">5</button>
|
|
</div>
|
|
|
|
</div>
|
|
<div>
|
|
<h1 id="text"></h1>
|
|
<div id="logger"></div>
|
|
</div>
|
|
|
|
<script>
|
|
function preventDefault(e) {alert(); e.preventDefault()}
|
|
function toHex(data) {
|
|
return Array.from(data).map((b) => ("00" + b.toString(16)).slice(-2)).join(
|
|
"",
|
|
);
|
|
}
|
|
async function generateHMAC(key, message) {
|
|
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));
|
|
}
|
|
function log(text) {
|
|
logger.innerHTML += text + "<br>";
|
|
}
|
|
function disableAuthBtns() {
|
|
document.querySelectorAll("#authbuttons").forEach(v => v.disabled = false);
|
|
}
|
|
|
|
const id = document.querySelector("#id");
|
|
const pass = document.querySelector("#passcode");
|
|
const pin = document.querySelector("#pin");
|
|
const logger = document.querySelector("#logger")
|
|
const text = document.querySelector("#text")
|
|
const votebuttons = document.querySelector("#votebuttons")
|
|
const auth = document.querySelector("#auth")
|
|
|
|
function websock() {
|
|
let socket = new WebSocket("ws://localhost:8000/ws");
|
|
|
|
socket.sendln = (...e) => {
|
|
log("device: " + e.join("\n"));
|
|
socket.send(...e)
|
|
}
|
|
|
|
socket.addEventListener("message", async (event) => {
|
|
log("server: " + event.data);
|
|
|
|
let data = JSON.parse(event.data);
|
|
switch (data.c) {
|
|
case "ping": {
|
|
socket.sendln(JSON.stringify({c: "pong"}));
|
|
break
|
|
}
|
|
case "reg_pin": {
|
|
pin.value = data.d.pin;
|
|
break
|
|
}
|
|
case "reg_ok": {
|
|
id.value = data.d.id;
|
|
pass.value = data.d.password;
|
|
socket.close();
|
|
|
|
pin.value = "";
|
|
|
|
log("### closing and reopening socket");
|
|
websock()
|
|
break
|
|
}
|
|
case "auth_nonce": {
|
|
const signature = await generateHMAC(pass.value, data.d.nonce)
|
|
socket.sendln(JSON.stringify({c: "auth_validate", d: {signature}}));
|
|
break
|
|
}
|
|
case "auth_ok": {
|
|
auth.checked = true;
|
|
disableAuthBtns()
|
|
break;
|
|
}
|
|
case "session_start": {
|
|
text.style.display = "block";
|
|
votebuttons.style.display = "flex";
|
|
text.innerText = data.d.text;
|
|
break;
|
|
}
|
|
case "session_stop": {
|
|
text.style.display = "none";
|
|
votebuttons.style.display = "none";
|
|
text.innerText = "";
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
socket.addEventListener("close", () => {
|
|
auth.checked = false;
|
|
disableAuthBtns()
|
|
window.startRegistration = undefined;
|
|
window.startAuthentication = undefined;
|
|
text.innerText = ""
|
|
})
|
|
|
|
socket.addEventListener("open", () => {
|
|
window.startRegistration = () => {
|
|
socket.sendln(JSON.stringify({c: "reg_start"}))
|
|
}
|
|
window.startAuthentication = () => {
|
|
socket.sendln(JSON.stringify({c: "auth_start", d: {id: id.value}}))
|
|
}
|
|
window.vote = (num) => {
|
|
socket.sendln(JSON.stringify({c: "session_vote", d: {vote: num}}))
|
|
}
|
|
})
|
|
|
|
}
|
|
websock()
|
|
</script>
|
|
</body>
|
|
|
|
</html> |