From 8639ade964bd9408ef397665fe7cfea329ba748e Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 21 Aug 2026 11:38:59 +0200 Subject: [PATCH] feat(signup-credit): digest an identity into a keyed fingerprint The ledger has to outlive the account that wrote it, so it cannot store a subject id or an email. HMAC under SECRET_KEY rather than a bare hash: the space of Google subject ids and email addresses is small enough to exhaust offline against a plain SHA-256. --- .../app/signup_credit/__init__.py | 1 + .../app/signup_credit/identity/fingerprint.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 surfsense_backend/app/signup_credit/__init__.py create mode 100644 surfsense_backend/app/signup_credit/identity/fingerprint.py diff --git a/surfsense_backend/app/signup_credit/__init__.py b/surfsense_backend/app/signup_credit/__init__.py new file mode 100644 index 000000000..8fdfd8ff5 --- /dev/null +++ b/surfsense_backend/app/signup_credit/__init__.py @@ -0,0 +1 @@ +"""The signup credit: granted once per person, not once per account.""" diff --git a/surfsense_backend/app/signup_credit/identity/fingerprint.py b/surfsense_backend/app/signup_credit/identity/fingerprint.py new file mode 100644 index 000000000..48ea7270e --- /dev/null +++ b/surfsense_backend/app/signup_credit/identity/fingerprint.py @@ -0,0 +1,20 @@ +"""One-way, keyed digest of an identity value.""" + +from __future__ import annotations + +import hashlib +import hmac + +from app.config import config + + +def fingerprint(value: str) -> str: + """Digest an identity so it stays comparable but never readable.""" + # Keyed rather than a bare hash: the space of subject ids and email + # addresses is small enough to exhaust offline. + if not config.SECRET_KEY: + raise RuntimeError("SECRET_KEY must be set before identities can be claimed.") + + return hmac.new( + config.SECRET_KEY.encode(), value.encode(), hashlib.sha256 + ).hexdigest()