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,
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
<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>