2021-12-10 18:29:03 +01:00
|
|
|
import time
|
|
|
|
import json
|
2021-12-10 17:46:53 +01:00
|
|
|
import random
|
|
|
|
import qrcode
|
|
|
|
import requests
|
|
|
|
import base64
|
|
|
|
import re
|
2022-01-12 19:21:05 +01:00
|
|
|
import os
|
2022-01-16 19:52:12 +01:00
|
|
|
import typer
|
2022-01-15 14:12:51 +01:00
|
|
|
from random_word import RandomWords
|
2021-12-17 16:22:04 +01:00
|
|
|
|
2021-12-10 17:46:53 +01:00
|
|
|
SENDER_LENGTH = 4
|
|
|
|
PASSWORD_LENGTH = 8
|
|
|
|
|
|
|
|
|
|
|
|
import secrets
|
|
|
|
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
|
|
|
|
|
|
|
|
from cryptography.fernet import Fernet
|
|
|
|
from cryptography.hazmat.backends import default_backend
|
|
|
|
from cryptography.hazmat.primitives import hashes
|
|
|
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
|
|
|
|
|
|
r = RandomWords()
|
|
|
|
|
|
|
|
backend = default_backend()
|
|
|
|
iterations = 100_000
|
|
|
|
|
|
|
|
def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
|
|
|
|
"""Derive a secret key from a given password and salt"""
|
|
|
|
kdf = PBKDF2HMAC(
|
|
|
|
algorithm=hashes.SHA256(), length=32, salt=salt,
|
|
|
|
iterations=iterations, backend=backend)
|
|
|
|
return b64e(kdf.derive(password))
|
|
|
|
|
|
|
|
def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
|
|
|
|
salt = secrets.token_bytes(16)
|
|
|
|
key = _derive_key(password.encode(), salt, iterations)
|
|
|
|
return b64e(
|
|
|
|
b'%b%b%b' % (
|
|
|
|
salt,
|
|
|
|
iterations.to_bytes(4, 'big'),
|
|
|
|
b64d(Fernet(key).encrypt(message)),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
def password_decrypt(token: bytes, password: str) -> bytes:
|
|
|
|
decoded = b64d(token)
|
|
|
|
salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
|
|
|
|
iterations = int.from_bytes(iter, 'big')
|
|
|
|
key = _derive_key(password.encode(), salt, iterations)
|
|
|
|
return Fernet(key).decrypt(token)
|
|
|
|
|
|
|
|
class Bump:
|
2022-01-12 19:21:05 +01:00
|
|
|
def __init__(self, secret=None, secrets_file=None):
|
|
|
|
if None == secrets_file:
|
|
|
|
secrets_file = os.path.join(os.path.expanduser('~'), '.config/bump/secrets_file')
|
|
|
|
self.secrets = self._load_secrets(secret, secrets_file)
|
2021-12-10 17:46:53 +01:00
|
|
|
if self.secrets == []:
|
2022-01-12 19:21:05 +01:00
|
|
|
print("you seem to not have a secret in your secrets file! Creating one now...")
|
|
|
|
self.generate_secret(secrets_file)
|
2021-12-10 17:46:53 +01:00
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
self.URL = "https://bump.maenle.net/api/"
|
2021-12-10 17:46:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _load_secrets(self, secret, secrets_file):
|
2021-12-10 17:46:53 +01:00
|
|
|
try:
|
|
|
|
with open(secrets_file, 'r') as f:
|
|
|
|
secrets = f.read().splitlines()
|
|
|
|
except FileNotFoundError:
|
|
|
|
secrets = []
|
|
|
|
if secret is not None and secret not in self.secrets:
|
|
|
|
secrets.append(secret)
|
|
|
|
|
|
|
|
return secrets
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def generate_secret(self, secrets_file):
|
2022-01-16 13:41:04 +01:00
|
|
|
pattern = re.compile('^[a-zA-Z]+$')
|
2021-12-10 17:46:53 +01:00
|
|
|
WORDS = r.get_random_words()
|
|
|
|
secret = ""
|
2022-01-16 13:41:04 +01:00
|
|
|
word_count = 0
|
|
|
|
while word_count < SENDER_LENGTH + PASSWORD_LENGTH or len(secret[SENDER_LENGTH:]) < 32:
|
|
|
|
word = random.choice(WORDS)
|
|
|
|
print(pattern.match(word))
|
|
|
|
if pattern.match(word) and len(word) < 10:
|
|
|
|
secret += word + "-"
|
|
|
|
word_count += 1
|
|
|
|
|
2021-12-10 17:46:53 +01:00
|
|
|
secret = secret[:-1]
|
|
|
|
|
|
|
|
self.secrets.append(secret)
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
if not os.path.exists(os.path.dirname(secrets_file)):
|
|
|
|
os.makedirs(os.path.dirname(secrets_file))
|
|
|
|
|
|
|
|
with open(secrets_file, 'a+') as f:
|
2021-12-10 17:46:53 +01:00
|
|
|
f.write(secret + '\n')
|
|
|
|
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def show_secret(self):
|
2022-01-15 14:12:51 +01:00
|
|
|
print("Scan this QR Code with the Bump app to connect")
|
2021-12-10 17:46:53 +01:00
|
|
|
for secret in self.secrets:
|
2022-01-12 19:21:05 +01:00
|
|
|
qr = qrcode.QRCode(
|
|
|
|
version=1,
|
|
|
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
|
|
|
box_size=10,
|
|
|
|
border=4,
|
|
|
|
)
|
|
|
|
qr.add_data(secret)
|
|
|
|
qr.print_ascii()
|
|
|
|
print("")
|
2021-12-10 17:46:53 +01:00
|
|
|
print(secret)
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _get_password(self, index=0):
|
2021-12-10 17:46:53 +01:00
|
|
|
secret = self.secrets[index].split('-')
|
|
|
|
return "-".join(secret[SENDER_LENGTH:])
|
|
|
|
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _get_sender(self, index=0):
|
2021-12-10 17:46:53 +01:00
|
|
|
words = self.secrets[index].split('-')
|
|
|
|
return "-".join(words[0:SENDER_LENGTH])
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _encrypt(self, data):
|
|
|
|
password = self._get_password()
|
2021-12-10 17:46:53 +01:00
|
|
|
return password_encrypt(data.encode(), password)
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _decrypt(self, data):
|
|
|
|
password = self._get_password()
|
2021-12-10 17:46:53 +01:00
|
|
|
return password_decrypt(data, password).decode()
|
|
|
|
|
2022-01-03 12:28:35 +01:00
|
|
|
def push(self, title='', data=''):
|
2021-12-10 17:46:53 +01:00
|
|
|
params = {
|
2022-01-12 19:21:05 +01:00
|
|
|
'sender': self._get_sender(),
|
2022-01-03 12:28:35 +01:00
|
|
|
'title': title,
|
2022-01-12 19:21:05 +01:00
|
|
|
'data': self._encrypt(data)
|
2021-12-30 16:10:49 +01:00
|
|
|
}
|
2022-01-12 19:21:05 +01:00
|
|
|
return self._set_post("push", params)
|
2021-12-10 17:46:53 +01:00
|
|
|
|
2021-12-17 16:22:04 +01:00
|
|
|
def peek(self):
|
2022-01-12 19:21:05 +01:00
|
|
|
return self._get_post("peek")
|
2021-12-10 17:46:53 +01:00
|
|
|
|
|
|
|
def pop(self):
|
2022-01-12 19:21:05 +01:00
|
|
|
return self._get_post("pop")
|
2021-12-10 17:46:53 +01:00
|
|
|
|
|
|
|
def list(self):
|
2021-12-10 17:59:52 +01:00
|
|
|
params = {
|
|
|
|
'minutes': 2
|
|
|
|
}
|
2022-01-12 19:21:05 +01:00
|
|
|
return self._get_post("list", params)
|
2021-12-10 17:46:53 +01:00
|
|
|
|
2021-12-30 16:10:49 +01:00
|
|
|
|
2021-12-10 17:46:53 +01:00
|
|
|
def clear(self):
|
2022-01-12 19:21:05 +01:00
|
|
|
return self._get_post("clear")
|
2021-12-10 17:46:53 +01:00
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def delete_sender(self):
|
|
|
|
return self._set_post("delete_sender")
|
2022-01-03 13:09:11 +01:00
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _set_post(self, mechanism, add_params = None):
|
2021-12-30 16:10:49 +01:00
|
|
|
url = self.URL + mechanism
|
|
|
|
params = {
|
2022-01-12 19:21:05 +01:00
|
|
|
'sender': self._get_sender(),
|
2021-12-30 16:10:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if add_params != None:
|
|
|
|
params.update(add_params)
|
|
|
|
|
|
|
|
messages = requests.post(url, params).json()
|
|
|
|
print(messages)
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _get_post(self, mechanism, add_params = None):
|
2021-12-10 17:46:53 +01:00
|
|
|
url = self.URL + mechanism
|
2021-12-10 17:59:52 +01:00
|
|
|
|
2021-12-10 17:46:53 +01:00
|
|
|
params = {
|
2022-01-12 19:21:05 +01:00
|
|
|
'sender': self._get_sender()
|
2021-12-10 17:46:53 +01:00
|
|
|
}
|
2021-12-10 17:59:52 +01:00
|
|
|
|
|
|
|
if add_params != None:
|
|
|
|
params.update(add_params)
|
|
|
|
|
2021-12-10 18:29:03 +01:00
|
|
|
messages = requests.post(url, params).json()
|
2021-12-10 17:59:52 +01:00
|
|
|
|
2021-12-17 16:22:04 +01:00
|
|
|
if(messages == {} or messages == None or
|
|
|
|
"messages" in messages and messages["messages"] == []):
|
2021-12-10 18:29:03 +01:00
|
|
|
return [{}]
|
2021-12-17 16:22:04 +01:00
|
|
|
elif "messages" in messages:
|
|
|
|
for message in messages.get("messages"):
|
2022-01-12 19:21:05 +01:00
|
|
|
message['data'] = self._decrypt(message.get('data'))
|
2021-12-10 17:59:52 +01:00
|
|
|
|
|
|
|
return messages
|
|
|
|
else:
|
2022-01-12 19:21:05 +01:00
|
|
|
messages['data'] = self._decrypt(messages.get('data'))
|
2021-12-10 18:29:03 +01:00
|
|
|
return [messages]
|
|
|
|
|
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _load_log(self):
|
2021-12-10 18:29:03 +01:00
|
|
|
with open('.bump_log', "r+") as f:
|
|
|
|
return f.readlines()
|
2021-12-10 17:59:52 +01:00
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _save_log(self, message):
|
2021-12-10 18:29:03 +01:00
|
|
|
with open('.bump_log', "a") as f:
|
2022-01-12 19:21:05 +01:00
|
|
|
f.write(self._to_log_line(message))
|
2021-12-10 18:29:03 +01:00
|
|
|
|
2022-01-12 19:21:05 +01:00
|
|
|
def _to_log_line(self, message):
|
2021-12-10 18:29:03 +01:00
|
|
|
return json.dumps(message) + "\n"
|
|
|
|
|
|
|
|
def alert(self, sleep_time=1):
|
2022-01-12 19:21:05 +01:00
|
|
|
log = self._load_log()
|
2021-12-10 17:59:52 +01:00
|
|
|
while True:
|
2021-12-10 18:29:03 +01:00
|
|
|
time.sleep(sleep_time)
|
|
|
|
messages = self.list()
|
|
|
|
for message in messages:
|
2022-01-12 19:21:05 +01:00
|
|
|
if self._to_log_line(message) not in log and message != {}:
|
2021-12-10 18:29:03 +01:00
|
|
|
print(message['data'])
|
2022-01-12 19:21:05 +01:00
|
|
|
self._save_log(message)
|
2021-12-10 18:29:03 +01:00
|
|
|
log = self.load_log()
|
|
|
|
|