mirror of
https://github.com/andrewkdinh/death-code.git
synced 2024-09-19 14:55:06 -07:00
Initial commit
This commit is contained in:
parent
9c6ad076c5
commit
ca5bfda7ff
17
Dockerfile
Normal file
17
Dockerfile
Normal 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
|
2
LICENSE
2
LICENSE
@ -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
25
README.md
Normal 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
24
docker-compose.yml
Normal 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
176
main.py
Executable 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
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Flask~=1.1.2
|
||||
requests~=2.25.1
|
23
templates/attempt.html
Normal file
23
templates/attempt.html
Normal 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
30
templates/congrats.html
Normal 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
62
templates/index.html
Normal 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
24
templates/message.html
Normal 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>
|
Loading…
Reference in New Issue
Block a user