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(); 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(` 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'));