Initial commit

This commit is contained in:
Andrew Dinh 2021-02-23 13:14:29 -08:00
parent 9c6ad076c5
commit ca5bfda7ff
Signed by: andrewkdinh
GPG Key ID: 2B557D93C6C08B86
10 changed files with 384 additions and 1 deletions

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
# docker build -t death-code .
# docker run -d --restart unless-stopped --name death-code -p 2020:80 death-code
# curl 127.0.0.1:2020
FROM tiangolo/uwsgi-nginx-flask:python3.8
LABEL maintainer="Andrew Dinh <death-code@andrewkdinh.com>"
LABEL version="0.3.0"
EXPOSE 80
ADD ./requirements.txt /app/requirements.txt
RUN python3 -m pip install --upgrade pip && python3 -m pip install -r /app/requirements.txt
ADD ./templates /app/templates/
ADD ./main.py /app/main.py
HEALTHCHECK CMD curl http://localhost

View File

@ -658,4 +658,4 @@ specific requirements.
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.

25
README.md Normal file
View File

@ -0,0 +1,25 @@
# Death Code
Death Code is an entirely self-hosted web application that utilizes [Sharmir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) to share secrets after you die. After splitting a secret among a group of people, the secret can only be reconstructed when a sufficient number of people combine their parts together, presumably only after you are gone from this earth.
## Building
1. Install `git`, `docker`, and `docker-compose`
2.
```bash
git clone https://github.com/andrewkdinh/death-code.git
cd death-code
cp .env.example .env
# Edit .env
git clone https://github.com/daniel-e/rust-captcha.git rust-captcha
docker-compose up -d
```
## Credits
- Built with Python Flask and Docker
Mirrors: [GitHub](https://github.com/andrewkdinh/death-code) (main), [Gitea](https://gitea.andrewkdinh.com/andrewkdinh/death-code)
Licensed under [AGPL](./LICENSE) | Copyright (c) 2021 Andrew Dinh

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
version: '3.0'
services:
death-code:
container_name: death-code
build:
context: .
restart: unless-stopped
ports:
- 33284:80
depends_on:
- rust-captcha
environment:
- END_URL=${END_URL} # If attempt is successful, redirect to this URL
- DOMAIN=${DOMAIN} # The domain this will be hosted on
- COEFFICIENTS_AND_MOD=${COEFFICIENTS_AND_MOD} # For example, [3, 5, 23] represents the function f(x)=3x+5 mod 23
- ALIVE_PATH=${ALIVE_PATH} # Path to deny allowing attempts immediately and start the countdown (optional)
- DAYS_TO_ALLOW=${DAYS_TO_ALLOW} # How many days to GET alive endpoint until death code starts allowing attemps (optional)
- DEAD_PATH=${DEAD_PATH} # Path to start allowing attemps immediately (optional)
rust-captcha:
container_name: rust-captcha
build:
context: ./rust-captcha/docker
restart: unless-stopped

176
main.py Executable file
View File

@ -0,0 +1,176 @@
#!/usr/bin/env python3
from flask import Flask, render_template, redirect, request
import requests
from typing import List
from datetime import datetime
import os
import json
END_URL = os.getenv('END_URL')
COEFFICIENTS_AND_MOD = json.loads(os.getenv('COEFFICIENTS_AND_MOD')) # For example, [3, 5, 23] represents the function f(x)=3x+5 mod 23
DOMAIN = os.getenv('DOMAIN')
DAYS_TO_ALLOW = os.getenv('DAYS_TO_ALLOW') # How many days to GET endpoint until death code functions
if DAYS_TO_ALLOW:
DAYS_TO_ALLOW = int(DAYS_TO_ALLOW)
# DAYS_TO_ALLOW = 7
ALIVE_PATH = os.getenv('ALIVE_PATH') # Path to deny allowing attempts immediately and start the countdown (optional)
# ALIVE_PATH = "i-am-alive"
DEAD_PATH = os.getenv('DEAD_PATH') # Path to start allowing attemps immediately (optional)
# DEAD_PATH = "i-am-dead"
LAST_ENDPOINT_GET: datetime = None
app = Flask(__name__)
@app.route("/", methods=["GET", "POST"])
def index():
global POLYNOMIAL, LAST_ENDPOINT_GET, DAYS_TO_ALLOW
if request.method == "POST":
if ALIVE_PATH and DAYS_TO_ALLOW and LAST_ENDPOINT_GET and (datetime.today() - LAST_ENDPOINT_GET).days < DAYS_TO_ALLOW:
return render_template("message.html", message="Andrew is still alive. Try again another time")
captcha_id = request.form.get('captcha-id')
captcha_solution = request.form.get('captcha-solution')
v = captcha_validate(captcha_id, captcha_solution)
if not v[0]:
return render_template('message.html', message = "Failed captcha", attempts_left = v[1])
try:
coords = []
for key in request.form:
if 'x' == key[0]:
coords.append(Coordinate(int(request.form[key]), int(request.form['y' + key[1]])))
if POLYNOMIAL.valid_combination(coords):
return render_template("congrats.html", polynomial=POLYNOMIAL, domain=DOMAIN)
return render_template("message.html", message="Those points weren't valid", attempts_left = v[1])
except Exception as e:
print(e)
return render_template("message.html", message="Invalid data")
captcha = captcha_get(ttl = 300)
return render_template("index.html", polynomial = POLYNOMIAL, captcha_id = captcha[0], captcha_png = captcha[1])
@app.route("/<attempt_num>", methods=["GET", "POST"])
def attempt(attempt_num):
global POLYNOMIAL, LAST_ENDPOINT_GET, DAYS_TO_ALLOW
try:
attempt_num = int(attempt_num)
if request.method == "POST":
captcha_id = request.form.get('captcha-id')
captcha_solution = request.form.get('captcha-solution')
v = captcha_validate(captcha_id, captcha_solution)
if v[0]:
if ALIVE_PATH and LAST_ENDPOINT_GET and (datetime.today() - LAST_ENDPOINT_GET).days < DAYS_TO_ALLOW:
return render_template("message.html", message="Andrew is still alive. Try again another time")
num = int(attempt_num)
if num == POLYNOMIAL.x_zero_point:
return redirect(END_URL, code=302)
return render_template('message.html', message="Incorrect guess for f(0)", attempts_left = v[1])
return render_template('message.html', message="Failed captcha", attempts_left = v[1])
captcha = captcha_get(ttl = 30, difficulty="hard")
return render_template('attempt.html', captcha_id = captcha[0], captcha_png = captcha[1])
except ValueError as e:
print(e)
return render_template('message.html', message="URL path must be an integer")
except Exception as e:
print(e)
return render_template('message.html', message="Error ocurred")
if ALIVE_PATH and DAYS_TO_ALLOW:
@app.route("/" + ALIVE_PATH, methods=["GET"])
def am_alive():
global LAST_ENDPOINT_GET
LAST_ENDPOINT_GET = datetime.today()
return "OK"
if DEAD_PATH:
@app.route("/" + DEAD_PATH, methods=["GET"])
def am_dead():
global LAST_ENDPOINT_GET
LAST_ENDPOINT_GET = None
return "OK"
def captcha_validate(captcha_id: str, captcha_solution: str) -> List:
""" Validates a captcha and returns [success, trials_left] """
response = requests.post(f"http://rust-captcha:8000/solution/{captcha_id}/{captcha_solution}", headers={'X-Client-ID': 'Death Code'}).json()
if response["error_code"] != 0:
print(f"http://rust-captcha:8000/solution/{captcha_id}/{captcha_solution}")
raise Exception(response)
if response["result"]["solution"] == "accepted":
return [True, 0]
return [False, response["result"]["trials_left"]]
def captcha_get(max_tries: int = 3, ttl: int = 120, difficulty: str = "medium") -> List[str]:
""" Creates a captcha and returns [id, base64 encoded png] """
response = requests.post(f"http://rust-captcha:8000/new/{difficulty}/{max_tries}/{ttl}", headers={'X-Client-ID': 'Death Code'}).json()
if response["error_code"] != 0:
raise Exception(response)
return [response["result"]["id"], response["result"]["png"]]
class Coordinate:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __repr__(self) -> str:
return "({}, {})".format(self.x, self.y)
def equals(self, coord):
""" Returns whether this coordinate is equal to the other """
return self.x == coord.x and self.y == coord.y
class Polynomial:
def __init__(self, coefficients: List, modulo: int):
while (coefficients[0] == 0):
coefficients = coefficients[1:]
self.degree = len(coefficients) - 1
self.coefficients = coefficients
self.modulo = modulo
self.x_zero_point = coefficients[-1] % modulo
def __repr__(self):
i = 0
s = "y="
exponent = self.degree
while exponent > 1:
if s != "y=":
s += "+"
s += str(self.coefficients[i]) + "x^" + str(exponent)
exponent -= 1
i += 1
if i != len(self.coefficients):
if s != "y=":
s += "+"
s += str(self.coefficients[i]) + "x"
i += 1
if i != len(self.coefficients):
if s != "y=":
s += "+"
s += str(self.coefficients[-1])
return s + " mod " + str(self.modulo)
def valid_combination(self, coordinates: List[Coordinate]) -> bool:
""" Returns whether there are enough valid coordinates in `coordinates` to extract this polyomial """
count = 0
seen_coords = []
for coord in coordinates:
if self.valid_coord(coord) and all([not coord.equals(coordinate) for coordinate in seen_coords]):
count += 1
seen_coords.append(coord)
return count >= self.degree + 1
def valid_coord(self, coord: Coordinate) -> bool:
""" Returns whether `coord` is a valid coordinate for this polynomial """
exponent = self.degree
coefficients = self.coefficients
value = 0
i = 0
while exponent >= 0:
value += coefficients[i] * pow(coord.x, exponent)
exponent -= 1
i += 1
return value % self.modulo == coord.y
POLYNOMIAL = Polynomial(COEFFICIENTS_AND_MOD[:-1], COEFFICIENTS_AND_MOD[-1])
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=8888)

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
Flask~=1.1.2
requests~=2.25.1

23
templates/attempt.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Death Code</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- water.css -->
<style>
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;color:#dbdbdb;background:#202b38;text-rendering:optimizeLegibility}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px}h1,h2,h3,h4,h5,h6,strong{color:#fff}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}blockquote{border-left:4px solid rgba(0,150,191,.67);margin:1.5em 0;padding:.5em 1em;font-style:italic}blockquote>footer{margin-top:10px;font-style:normal}address,blockquote cite{font-style:normal}a[href^=mailto]:before{content:"📧 "}a[href^=tel]:before{content:"📞 "}a[href^=sms]:before{content:"💬 "}button,input[type=button],input[type=checkbox],input[type=submit]{cursor:pointer}input:not([type=checkbox]):not([type=radio]),select{display:block}button,input,select,textarea{color:#fff;background-color:#161f27;font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}button,input:not([type=checkbox]):not([type=radio]),select,textarea{-webkit-appearance:none}textarea{margin-right:0;width:100%;box-sizing:border-box;resize:vertical}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=button]:hover,input[type=submit]:hover{background:#324759}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67)}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-webkit-input-placeholder{color:#a9a9a9}:-ms-input-placeholder{color:#a9a9a9}::-ms-input-placeholder{color:#a9a9a9}::placeholder{color:#a9a9a9}a{text-decoration:none;color:#41adff}a:hover{text-decoration:underline}code,kbd{background:#161f27;color:#ffbe85;padding:5px;border-radius:6px}pre>code{padding:10px;display:block;overflow-x:auto}img{max-width:100%}hr{border:none;border-top:1px solid #dbdbdb}table{border-collapse:collapse;margin-bottom:10px;width:100%}td,th{padding:6px;text-align:left}th{border-bottom:1px solid #dbdbdb}tbody tr:nth-child(2n){background-color:#161f27}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#161f27;border-radius:6px}::-webkit-scrollbar-thumb{background:#324759;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#415c73}
</style>
</head>
<body>
<p>Do this captcha because I don't trust y'all (captcha only valid for 30 seconds)</p>
<form method="POST">
<img src="data:image/png;base64, {{ captcha_png }}" />
<input required type="text" name="captcha-solution">
<input type="hidden" name="captcha-id" value="{{ captcha_id }}">
<button type="submit">Validate</button>
</form>
</body>
</html>

30
templates/congrats.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Death Code</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- water.css -->
<style>
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;color:#dbdbdb;background:#202b38;text-rendering:optimizeLegibility}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px}h1,h2,h3,h4,h5,h6,strong{color:#fff}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}blockquote{border-left:4px solid rgba(0,150,191,.67);margin:1.5em 0;padding:.5em 1em;font-style:italic}blockquote>footer{margin-top:10px;font-style:normal}address,blockquote cite{font-style:normal}a[href^=mailto]:before{content:"📧 "}a[href^=tel]:before{content:"📞 "}a[href^=sms]:before{content:"💬 "}button,input[type=button],input[type=checkbox],input[type=submit]{cursor:pointer}input:not([type=checkbox]):not([type=radio]),select{display:block}button,input,select,textarea{color:#fff;background-color:#161f27;font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}button,input:not([type=checkbox]):not([type=radio]),select,textarea{-webkit-appearance:none}textarea{margin-right:0;width:100%;box-sizing:border-box;resize:vertical}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=button]:hover,input[type=submit]:hover{background:#324759}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67)}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-webkit-input-placeholder{color:#a9a9a9}:-ms-input-placeholder{color:#a9a9a9}::-ms-input-placeholder{color:#a9a9a9}::placeholder{color:#a9a9a9}a{text-decoration:none;color:#41adff}a:hover{text-decoration:underline}code,kbd{background:#161f27;color:#ffbe85;padding:5px;border-radius:6px}pre>code{padding:10px;display:block;overflow-x:auto}img{max-width:100%}hr{border:none;border-top:1px solid #dbdbdb}table{border-collapse:collapse;margin-bottom:10px;width:100%}td,th{padding:6px;text-align:left}th{border-bottom:1px solid #dbdbdb}tbody tr:nth-child(2n){background-color:#161f27}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#161f27;border-radius:6px}::-webkit-scrollbar-thumb{background:#324759;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#415c73}
body {
background-color: #ff75ff;
color: black;
}
a {
color: rgb(0, 47, 255);
}
</style>
</head>
<body>
<p>Congrats, you got it! The polynomial is {{ polynomial }} and the point at x=0 is {{ polynomial.x_zero_point }}. </p>
<p>Now, visit <a rel="noopener noreferrer" href="/{{ polynomial.x_zero_point }}">https://{{ domain }}/{{ polynomial.x_zero_point }}</a></p>
<p>(A pink background just for you Vicki!)</p>
</body>
</html>

62
templates/index.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Death Code</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- water.css -->
<style>
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;color:#dbdbdb;background:#202b38;text-rendering:optimizeLegibility}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px}h1,h2,h3,h4,h5,h6,strong{color:#fff}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}blockquote{border-left:4px solid rgba(0,150,191,.67);margin:1.5em 0;padding:.5em 1em;font-style:italic}blockquote>footer{margin-top:10px;font-style:normal}address,blockquote cite{font-style:normal}a[href^=mailto]:before{content:"📧 "}a[href^=tel]:before{content:"📞 "}a[href^=sms]:before{content:"💬 "}button,input[type=button],input[type=checkbox],input[type=submit]{cursor:pointer}input:not([type=checkbox]):not([type=radio]),select{display:block}button,input,select,textarea{color:#fff;background-color:#161f27;font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}button,input:not([type=checkbox]):not([type=radio]),select,textarea{-webkit-appearance:none}textarea{margin-right:0;width:100%;box-sizing:border-box;resize:vertical}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=button]:hover,input[type=submit]:hover{background:#324759}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67)}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-webkit-input-placeholder{color:#a9a9a9}:-ms-input-placeholder{color:#a9a9a9}::-ms-input-placeholder{color:#a9a9a9}::placeholder{color:#a9a9a9}a{text-decoration:none;color:#41adff}a:hover{text-decoration:underline}code,kbd{background:#161f27;color:#ffbe85;padding:5px;border-radius:6px}pre>code{padding:10px;display:block;overflow-x:auto}img{max-width:100%}hr{border:none;border-top:1px solid #dbdbdb}table{border-collapse:collapse;margin-bottom:10px;width:100%}td,th{padding:6px;text-align:left}th{border-bottom:1px solid #dbdbdb}tbody tr:nth-child(2n){background-color:#161f27}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#161f27;border-radius:6px}::-webkit-scrollbar-thumb{background:#324759;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#415c73}
input:not([type="checkbox"]):not([type="radio"]), select {
display: initial;
}
</style>
</head>
<body>
<h1>Death Code</h1>
<hr />
<p>The polynomial is degree {{ polynomial.degree }}, so you'll need at least {{ polynomial.degree + 1 }} points to find it</p>
<form action="/" method="POST">
{% for i in range(polynomial.degree + 1) %}
<div>
<input required type="number" name="x{{i+1}}" placeholder="x{{i+1}}">
<input required type="number" name="y{{i+1}}" placeholder="y{{i+1}}">
</div>
{% endfor %}
<div style="padding: 5px 0">
<img src="data:image/png;base64, {{ captcha_png }}" />
</div>
<input required type="text" name="captcha-solution">
<input type="hidden" name="captcha-id" value="{{ captcha_id }}">
<button type="submit">Validate</button>
</form>
<p>Once you get the value of f(0), go to https://rip.andrewkdinh.com/f(0)</p>
<hr />
<p>Here's resources with more information (for now, just contact info for others with a coordinate). Password should've been given to you</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://nextcloud.andrewkdinh.com/s/ADmFKLCZgdycRH8">Nextcloud</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://nextcloud.abhikahuja.com/s/TajPLXWLEkPQq6y">Abhik Ahuja's Nextcloud</a></li>
<li>
<details>
<summary><a target="_blank" rel="noopener noreferrer" href="https://mega.nz/folder/r44kgQCA">MEGA</a></summary>
<p>Decryption key: <code>echo "U2FsdGVkX1/fG6A2L6D50C2kCsrMw/sVdrjxJF7SfLy5z/92gR3So4LXBm8CYl3K"|openssl base64 -d|openssl enc -d -pbkdf2 -aes-256-cbc -k "PASSWORD_HERE"</code></p>
</details>
</li>
</ul>
<hr />
<p>Some other tools to do <a target="_blank" rel="noopener noreferrer" href="https://en.wikipedia.org/wiki/Polynomial_interpolation">Lagrange/polynomial interpolation</a>:</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://tools.timodenk.com/polynomial-interpolation">Timo Denk</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://www.dcode.fr/lagrange-interpolating-polynomial">dCode</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://www.wolframalpha.com/input/?i=interpolating+polynomial+calculator">WolframAlpha</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://planetcalc.com/8692/">PlanetCalc</a></li>
</ul>
</body>
</html>

24
templates/message.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Death Code</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- water.css -->
<style>
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;color:#dbdbdb;background:#202b38;text-rendering:optimizeLegibility}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px}h1,h2,h3,h4,h5,h6,strong{color:#fff}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}blockquote{border-left:4px solid rgba(0,150,191,.67);margin:1.5em 0;padding:.5em 1em;font-style:italic}blockquote>footer{margin-top:10px;font-style:normal}address,blockquote cite{font-style:normal}a[href^=mailto]:before{content:"📧 "}a[href^=tel]:before{content:"📞 "}a[href^=sms]:before{content:"💬 "}button,input[type=button],input[type=checkbox],input[type=submit]{cursor:pointer}input:not([type=checkbox]):not([type=radio]),select{display:block}button,input,select,textarea{color:#fff;background-color:#161f27;font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}button,input:not([type=checkbox]):not([type=radio]),select,textarea{-webkit-appearance:none}textarea{margin-right:0;width:100%;box-sizing:border-box;resize:vertical}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=button]:hover,input[type=submit]:hover{background:#324759}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67)}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-webkit-input-placeholder{color:#a9a9a9}:-ms-input-placeholder{color:#a9a9a9}::-ms-input-placeholder{color:#a9a9a9}::placeholder{color:#a9a9a9}a{text-decoration:none;color:#41adff}a:hover{text-decoration:underline}code,kbd{background:#161f27;color:#ffbe85;padding:5px;border-radius:6px}pre>code{padding:10px;display:block;overflow-x:auto}img{max-width:100%}hr{border:none;border-top:1px solid #dbdbdb}table{border-collapse:collapse;margin-bottom:10px;width:100%}td,th{padding:6px;text-align:left}th{border-bottom:1px solid #dbdbdb}tbody tr:nth-child(2n){background-color:#161f27}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#161f27;border-radius:6px}::-webkit-scrollbar-thumb{background:#324759;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#415c73}
</style>
</head>
<body>
<p>{{ message }}</p>
{% if attempts_left %}
<p>You have {{ attempts_left }} attempt(s) left</p>
<input type="button" value="Bo back" onclick="window.history.back()" />
{% elif attempts_left == 0 %}
<p>Captcha is now invalid (reload page after you go back)</p>
<input type="button" value="Bo back" onclick="window.history.back()" />
{% endif %}
</body>
</html>