py-spectre/spectre/spectre.py

69 lines
2.0 KiB
Python
Raw Normal View History

import hashlib
import struct
from hmac import HMAC
from spectre.scope import Scope, Scoper, default_scoper
from spectre.template import Template, _characters, _templates
class Spectre:
def __init__(self, name: str, secret: str, scoper: Scoper = default_scoper):
self._name = name
self._secret = secret
self._scoper = scoper
self._key = self._user_key()
def _user_key(self) -> bytes:
name_bytes = self._name.encode()
secret_bytes = self._secret.encode()
key_scope = self._scoper.scope(Scope.AUTHENTICATION).encode()
name_bytes_len = len(name_bytes)
key_salt = key_scope + _big_endian(name_bytes_len) + name_bytes
n = 32768 # N
r = 8
p = 2
m = 128 * r * (n + p + 2)
return hashlib.scrypt(
secret_bytes, maxmem=m, salt=key_salt, n=n, r=r, p=p, dklen=64
)
def _site_key(self, name: str, counter: int, scope: Scope) -> bytes:
name_bytes = name.encode()
scope_bytes = self._scoper.scope(scope).encode()
name_bytes_len = len(name_bytes)
key_salt = (
scope_bytes
+ _big_endian(name_bytes_len)
+ name_bytes
+ _big_endian(counter)
)
return HMAC(self._key, msg=key_salt, digestmod=hashlib.sha256).digest()
def site(
self,
site_name: str,
template_type: Template = None,
counter: int = 1,
scope: Scope = Scope.AUTHENTICATION,
) -> str:
if not template_type:
template_type = scope.default_template()
site_key = self._site_key(site_name, counter, scope)
template_set = _templates[template_type]
template = template_set[site_key[0] % len(template_set)]
out = ""
for idx, b in enumerate(template):
chars = _characters[b]
char = chars[site_key[idx + 1] % len(chars)]
out += char
return out
def _big_endian(n: int) -> bytes:
return struct.pack(">I", n)