108 lines
2.8 KiB
Python
108 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from flask import Flask, Response, request, send_from_directory
|
|
from ecdsa import SigningKey, VerifyingKey, BadSignatureError
|
|
from base64 import b64decode, b64encode
|
|
from hashlib import sha256
|
|
from random import randrange
|
|
import os
|
|
import time
|
|
|
|
|
|
...
|
|
|
|
if not FLAG:
|
|
import sys
|
|
sys.exit(1)
|
|
|
|
app_dir = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
admin_priv = SigningKey.from_pem(open('{}/keys/admin_priv.pem'.format(app_dir)).read(), hashfunc=sha256)
|
|
admin_pub = admin_priv.verifying_key
|
|
user_keys = dict()
|
|
|
|
for u in ['john', 'kate', 'silberman']:
|
|
user_keys[u] = VerifyingKey.from_pem(open('{}/keys/{}_pub.pem'.format(app_dir, u)).read(), hashfunc=sha256)
|
|
|
|
K_COEFF = [randrange(0, admin_pub.curve.order) for _ in range(5)]
|
|
chal_ts = None
|
|
chal_sig = None
|
|
chal_k = randrange(0, admin_pub.curve.order)
|
|
|
|
app = Flask(__name__, static_folder='{}/static'.format(app_dir))
|
|
|
|
...
|
|
|
|
def timestamp():
|
|
t = int(time.time())
|
|
|
|
return t - t % 30
|
|
|
|
def sigdecode(sig, order):
|
|
bl = (order.bit_length() + 7) // 8
|
|
sig = b64decode(sig.encode('utf-8'))
|
|
assert len(sig) == 2 * bl
|
|
r = int.from_bytes(sig[:bl], 'big')
|
|
s = int.from_bytes(sig[bl:], 'big')
|
|
|
|
return r % order, s % order
|
|
|
|
def sigencode(r, s, order):
|
|
bl = (order.bit_length() + 7) // 8
|
|
r = (r % order).to_bytes(bl, 'big')
|
|
s = (s % order).to_bytes(bl, 'big')
|
|
|
|
return b64encode(r + s).decode('utf-8')
|
|
|
|
def sigcheck(pubkey, sig):
|
|
try:
|
|
if pubkey.verify(sig, str(chal_ts).encode('utf-8') + b'login', hashfunc=sha256, sigdecode=sigdecode):
|
|
return True
|
|
except BadSignatureError:
|
|
pass
|
|
|
|
return False
|
|
|
|
@app.route('/static/<path:path>', methods=['GET'])
|
|
def serve_static(path):
|
|
return send_from_directory('static', path)
|
|
|
|
@app.route('/', methods=['GET'])
|
|
def index():
|
|
return app.send_static_file('index.html')
|
|
|
|
@app.route('/login', methods=['POST'])
|
|
def login():
|
|
data = request.get_json()
|
|
|
|
if 'user' not in data or 'sig' not in data:
|
|
return Response("Invalid request format", status=400)
|
|
|
|
user = data['user']
|
|
|
|
if user not in user_keys:
|
|
return { 'invalid': 'Invalid username' }
|
|
|
|
sig = data['sig']
|
|
|
|
if sigcheck(user_keys[user], sig) or sigcheck(admin_pub, sig):
|
|
return { 'valid': '{}'.format(FLAG) }
|
|
|
|
return { 'invalid': 'Invalid private key' }
|
|
|
|
@app.route('/challenge', methods=['GET'])
|
|
def challenge():
|
|
global chal_ts, chal_sig, chal_k
|
|
ts = timestamp()
|
|
|
|
if ts != chal_ts:
|
|
chal_ts = ts
|
|
chal_k = sum(a * chal_k ** i for i, a in enumerate(K_COEFF)) % admin_pub.curve.order
|
|
chal_sig = admin_priv.sign(str(chal_ts).encode('utf-8') + b'challenge', hashfunc=sha256, sigencode=sigencode, k=chal_k)
|
|
|
|
return { 'challenge': str(chal_ts), 'sig': chal_sig }
|
|
|
|
|
|
if __name__ == '__main__':
|
|
...
|