154 lines
4.6 KiB
TypeScript
154 lines
4.6 KiB
TypeScript
import express, { Request, Response } from 'express';
|
|
import crypto from 'crypto';
|
|
|
|
const app = express();
|
|
app.use(express.urlencoded({ extended: false }));
|
|
|
|
const DIFFICULTY = 20;
|
|
const challengeStore = new Map<string, number>();
|
|
|
|
function generateChallenge(): string {
|
|
return crypto.randomBytes(12).toString('hex');
|
|
}
|
|
|
|
function hashSha256(input: string): Buffer {
|
|
return crypto.createHash('sha256').update(input).digest();
|
|
}
|
|
|
|
function checkDifficulty(hash: Buffer, bits: number): boolean {
|
|
let remaining = bits, i = 0;
|
|
while (remaining > 0 && i < hash.length) {
|
|
const byte = hash[i];
|
|
if (remaining >= 8) {
|
|
if (byte !== 0) return false;
|
|
remaining -= 8;
|
|
} else {
|
|
const mask = 0xFF << (8 - remaining);
|
|
if ((byte & mask) !== 0) return false;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
app.get('/', (_req: Request, res: Response) => {
|
|
const challenge = generateChallenge();
|
|
challengeStore.set(challenge, Date.now());
|
|
res.send(`
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>POWOW POC</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body {
|
|
padding: 20px;
|
|
margin-bottom: 300px;
|
|
background: #cacaca;
|
|
color: black;
|
|
}
|
|
code {
|
|
white-space: break-spaces;
|
|
background: black;
|
|
color: white;
|
|
padding: 10px;
|
|
display: block;
|
|
max-width: 500px;
|
|
}
|
|
.h {
|
|
font-size: 21px;
|
|
font-weight: bolder;
|
|
width: 100%;
|
|
float: left;
|
|
}
|
|
button {
|
|
background: #006ed0;
|
|
color: white;
|
|
border: 1px solid #006ed0;
|
|
padding: 10px 20px;
|
|
margin-top: 10px;
|
|
}
|
|
input {
|
|
background: black;
|
|
color: white;
|
|
padding: 10px;
|
|
display: block;
|
|
max-width: 500px;
|
|
width: 100%;
|
|
border: 0px solid transparent;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>NOJSCAP demo</h1>
|
|
<p><a href="https://git.libroot.org/libroot/NOJSCAP/" target="_blank">https://git.libroot.org/libroot/NOJSCAP/</a></p><p><br></p>
|
|
<p>If you don't already have the <b><em>NOJSCAP</em></b> client:</p>
|
|
<code>$ git clone https://git.libroot.org/libroot/NOJSCAP/
|
|
$ cd NOJSCAP/client/</code>
|
|
<p><br></p>
|
|
<p>
|
|
<strong>Python:</strong>
|
|
</p>
|
|
<p>
|
|
<input type="text" value="python3 pow_client.py ${challenge} ${DIFFICULTY}">
|
|
</p>
|
|
<p>
|
|
<strong>Go:</strong>
|
|
</p>
|
|
<p>
|
|
<input type="text" value="go run pow_client.go ${challenge} ${DIFFICULTY}">
|
|
</p>
|
|
<p>
|
|
<strong>Node.js:</strong>
|
|
</p>
|
|
<p>
|
|
<input type="text" value="node pow_client.js ${challenge} ${DIFFICULTY}">
|
|
</p>
|
|
<p>
|
|
<strong>Rust:</strong>
|
|
</p>
|
|
<p>
|
|
<details><summary>Show compilation commands</summary><code>$ rustc pow_client.rs -o pow_client_rs</code>
|
|
<small>Requires Rust and sha2 crate if using the Cargo version.</small><br></details>
|
|
<input type="text" value="pow_client_rs ${challenge} ${DIFFICULTY}">
|
|
</p>
|
|
<p>
|
|
<strong>C:</strong>
|
|
</p>
|
|
<p>
|
|
<details><summary>Show compilation commands</summary><code>$ gcc -O2 -o pow_client pow_client.c -lssl -lcrypto</code>
|
|
<small>Required: GCC or any C compiler. OpenSSL development libraries (libssl-dev on Debian-based systems)</small><br></details>
|
|
|
|
<input type="text" value="./pow_client ${challenge} ${DIFFICULTY}">
|
|
|
|
</p>
|
|
<form method="POST">
|
|
<input type="hidden" name="challenge" value="${challenge}">
|
|
<label class="h">Enter found nonce: <input name="nonce"></label>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
</body>
|
|
</html>
|
|
`);
|
|
});
|
|
|
|
app.post('/', (req: Request, res: Response) => {
|
|
const { challenge, nonce } = req.body as { challenge?: string; nonce?: string };
|
|
if (!challenge || !nonce || !challengeStore.has(challenge)) {
|
|
return res.send('Invalid input.');
|
|
}
|
|
|
|
challengeStore.delete(challenge);
|
|
const hash = hashSha256(challenge + nonce);
|
|
if (checkDifficulty(hash, DIFFICULTY)) {
|
|
res.send('<p>Success! Valid nonce.</p><a href="/">Try again</a>');
|
|
} else {
|
|
res.send('<p>Invalid nonce.</p><a href="/">Try again</a>');
|
|
}
|
|
});
|
|
|
|
app.listen(3000, () => console.log('Server running at http://localhost:3000'));
|