summaryrefslogtreecommitdiffstats
path: root/app/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/util.py')
-rw-r--r--app/util.py66
1 files changed, 66 insertions, 0 deletions
diff --git a/app/util.py b/app/util.py
new file mode 100644
index 0000000..69b3ea7
--- /dev/null
+++ b/app/util.py
@@ -0,0 +1,66 @@
+from base64 import b32encode, b32decode
+import logging
+from hashlib import blake2b # because it is fast as fuck
+
+log = logging.getLogger('blure')
+
+
+class URLCoder:
+ '''
+ URLCoder shuffles sequential ids into non-sequential strings,
+ that can be used as urls
+ '''
+ _pad = 1
+ _bs = 2
+ _bound = 2 ** (8 * _bs * 2)
+ _endian = 'big'
+
+ @classmethod
+ def _humanify(self, data: bytes):
+ b32 = b32encode(data).decode('ascii')
+ if self._pad > 0:
+ return b32[:-self._pad]
+ else:
+ return b32
+
+ @classmethod
+ def _dehumanify(self, data: str):
+ binary = b32decode(data + '=' * self._pad)
+ assert len(binary) == self._bs * 2
+ return binary
+
+ def __init__(self, secret: bytes):
+ assert len(secret) % (self._bs) == 0
+
+ self.keys = [
+ secret[key_start:key_start + self._bs]
+ for key_start in range(0, len(secret), self._bs)
+ ]
+ self.reverse_keys = self.keys[::-1]
+
+ def to_url(self, id: int) -> str:
+ assert 0 <= id and id <= self._bound
+ as_bytes = int.to_bytes(id, self._bs * 2, self._endian)
+ encoded = self._blake_enc(as_bytes, self.keys)
+ return self._humanify(encoded)
+
+ def to_id(self, url: str) -> int:
+ binary = self._dehumanify(url.upper())
+ as_bytes = self._blake_enc(binary, self.reverse_keys)
+ return int.from_bytes(as_bytes, self._endian)
+
+ def _blake_enc(self, data: bytes, keys: bytes):
+ bs = self._bs
+
+ def xor(bytes1, bytes2):
+ return bytes([b1 ^ b2 for b1, b2 in zip(bytes1, bytes2)])
+
+ def blake_round(data, key):
+ return blake2b(data, digest_size=bs, key=key).digest()
+
+ left = data[:bs]
+ right = data[bs:]
+ for key in keys:
+ left, right = right, xor(left, blake_round(right, key))
+
+ return right + left