NOJSCAP/server/ts-express/server.ts
2025-08-28 22:21:34 +00:00

158 lines
4.8 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('/NOJSCAP-demo', (_req: Request, res: Response) => {
const challenge = generateChallenge();
challengeStore.set(challenge, Date.now());
res.send(`
<!doctype html>
<html lang="en">
<head>
<title>NOJSCAP demo<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;
}
::selection {
background: #a60000;
color: white;
}
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 nojscap.py ${challenge} ${DIFFICULTY}">
</p>
<p>
<strong>Go:</strong>
</p>
<p>
<input type="text" value="go run nojscap.go ${challenge} ${DIFFICULTY}">
</p>
<p>
<strong>Node.js:</strong>
</p>
<p>
<input type="text" value="node nojscap.js ${challenge} ${DIFFICULTY}">
</p>
<p>
<strong>Rust:</strong>
</p>
<p>
<details><summary>Show compilation commands</summary><code>$ rustc nojscap.rs -o nojscap_rs</code>
<small>Requires Rust and sha2 crate if using the Cargo version.</small><br></details>
<input type="text" value="nojscap_rs ${challenge} ${DIFFICULTY}">
</p>
<p>
<strong>C:</strong>
</p>
<p>
<details><summary>Show compilation commands</summary><code>$ gcc -O2 -o nojscap nojscap.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="./nojscap ${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('/NOJSCAP-demo', (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('<div style="text-align:center;"><h1>Success! Valid nonce.</h1><a href="?">Try again</a></div>');
} else {
res.send('<div style="text-align:center;"><h1>Invalid nonce.</h1><a href="?">Try again</a></div>');
}
});
app.listen(3000, () => console.log('/NOJSCAP-demo server running at http://localhost:3000'));