140 lines
4 KiB
Python
140 lines
4 KiB
Python
from flask import Flask, request
|
|
import hashlib, os, time
|
|
|
|
app = Flask(__name__)
|
|
DIFFICULTY = 20
|
|
challenge_store = {}
|
|
|
|
def generate_challenge():
|
|
return os.urandom(12).hex()
|
|
|
|
def check_difficulty(hash_bytes, bits):
|
|
i = 0
|
|
while bits > 0:
|
|
byte = hash_bytes[i]
|
|
if bits >= 8:
|
|
if byte != 0: return False
|
|
bits -= 8
|
|
else:
|
|
if byte & (0xFF << (8 - bits)) != 0: return False
|
|
bits = 0
|
|
i += 1
|
|
return True
|
|
|
|
@app.route('/', methods=['GET'])
|
|
def index():
|
|
challenge = generate_challenge()
|
|
challenge_store[challenge] = time.time()
|
|
return f'''
|
|
<!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>
|
|
<code>$ python3 nojscap.py {challenge} {DIFFICULTY}</code>
|
|
</p>
|
|
<p>
|
|
<strong>Go:</strong>
|
|
</p>
|
|
<p>
|
|
<code>$ go run nojscap.go {challenge} {DIFFICULTY}</code>
|
|
</p>
|
|
<p>
|
|
<strong>Node.js:</strong>
|
|
</p>
|
|
<p>
|
|
<code>$ node nojscap.js {challenge} {DIFFICULTY}</code>
|
|
</p>
|
|
<p>
|
|
<strong>Rust:</strong>
|
|
</p>
|
|
<p>
|
|
<code>$ rustc nojscap.rs -o nojscap_rs
|
|
$ nojscap_rs {challenge} {DIFFICULTY}
|
|
</code>
|
|
<small>Requires Rust and sha2 crate if using the Cargo version.</small>
|
|
</p>
|
|
<p>
|
|
<strong>C:</strong>
|
|
</p>
|
|
<p>
|
|
<code>$ gcc -O2 -o nojscap nojscap.c -lssl -lcrypto
|
|
$ ./nojscap {challenge} {DIFFICULTY}
|
|
</code>
|
|
<small>Required: GCC or any C compiler. OpenSSL development libraries (libssl-dev on Debian-based systems)</small>
|
|
</p>
|
|
<form method="POST">
|
|
<input type="hidden" name="challenge" value="{challenge}">
|
|
<label class="h">Nonce: <input name="nonce"></label>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
</body>
|
|
</html>
|
|
'''
|
|
|
|
@app.route('/', methods=['POST'])
|
|
def submit():
|
|
challenge = request.form.get("challenge")
|
|
nonce = request.form.get("nonce")
|
|
if challenge not in challenge_store: return "Invalid challenge"
|
|
challenge_store.pop(challenge)
|
|
combined = (challenge + nonce).encode()
|
|
h = hashlib.sha256(combined).digest()
|
|
return "<div style='text-align:center;'><h1>Success! Valid nonce.</h1><a href='/'>Try again</a></div>" if check_difficulty(h, DIFFICULTY) else "<div style='text-align:center;'><h1>Invalid nonce.</h1><a href='/'>Try again</a></div>"
|
|
|
|
app.run(port=3000)
|
|
|