69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
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)
|