commit ce2633b6496f46f394caec08aefba205bd99de4f
parent 6a4e1cc9196799b241bc9a231593472a495e17cc
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 5 Aug 2026 18:28:55 +0200
clarify paivana cookie computation
Diffstat:
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/design-documents/076-paywall-proxy.rst b/design-documents/076-paywall-proxy.rst
@@ -48,14 +48,27 @@ Steps:
this client IP address and ``{website}`` at this time.
The *Paivana Cookie* is computed as:
- ``cur_time || '-' || crock32(SHA512(website || client_ip || paivana_server_secret || cur_time))``.
+ ``cur_time || '-' || crock32(HKDF(salt=cur_time, ikm=paivana_server_secret, info=website || '\0' || client_ip))``.
where ``cur_time`` in the prefix is the expiration time for the
cookie (and thus the access to the article) in seconds
- (to keep it short) while in the hash it is usually binary GNUnet
- timestamp in network byte order.
+ (to keep it short) while in the salt it is the binary GNUnet
+ absolute time (microseconds) in network byte order.
+ ``HKDF`` is GNUnet's HKDF (``GNUNET_CRYPTO_hkdf_gnunet()``, which
+ extracts with HMAC-SHA-512 and expands with HMAC-SHA-256), and the
+ output is 512 bits.
+ Using a keyed PRF instead of a plain hash over the concatenation
+ ensures that the cookie cannot be forged without the server secret
+ and that the inputs are unambiguously separated: ``website`` is
+ terminated by a zero byte before ``client_ip`` is appended, so
+ different ``(website, client_ip)`` pairs can never yield the same
+ ``info`` string.
``crock32`` is GNUnet's Crockford-inspired base32 encoding.
+ The cookie is computed and verified exclusively by paivana-httpd;
+ the browser only stores and returns it and thus never has to
+ reconstruct this value.
+
* If such a cookie is set and valid, the request is
reverse-proxied to upstream. *Stop.*
* Otherwise, an HTTP 303 See Other to