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)