L402: alternative DoS prevention using PoW #205
Draft
hieblmi wants to merge 10 commits into
Draft
Conversation
Introduce the foundational PoW computation and verification used by both server and client sides of L402 PoW authentication. The hash function is SHA256(tokenID || nonce) where the result must have a configurable number of leading zero bits. SolvePoW iterates nonces from 0 to find a valid solution. VerifyPoW performs a single hash check. NewPoWSatisfier returns a Satisfier for the "pow" caveat condition that can be plugged into the existing caveat verification framework. The caveat format is pow=<difficulty>:<hex-nonce>.
Add a MacaroonFromHeader function that extracts just the macaroon from any supported HTTP header format without requiring or validating a preimage. This is needed by PoW authentication (which has no preimage) and rate limiting (which only needs the token ID). A new macOnlyRegex matches both the standard 64-char hex preimage and the "POW" sentinel in the Authorization header, while the existing authRegex remains unchanged for backwards compatibility.
Add PowNonce (uint64) and PowDifficulty (uint32) fields to the Token struct for storing PoW solutions. PoW tokens are never pending since they are solved instantly, so isPending returns false when isPoW is true. SolvedMacaroon clones the base macaroon and adds the pow caveat with the difficulty and nonce, analogous to PaidMacaroon which adds the preimage. Token serialization appends the two PoW fields at the end of the binary format. Deserialization gracefully handles EOF for these optional fields, making the format backwards compatible with existing Lightning tokens.
Implement PoWChallenger which satisfies the mint.Challenger interface without requiring an LND connection. NewChallenge generates a random 32-byte hash as a placeholder payment hash for the L402 identifier and returns the difficulty as the challenge string. Stop is a no-op since there are no resources to clean up.
Add PoWVerificationParams and a VerifyL402PoW method to the Mint. This method follows the same structure as VerifyL402 — decode identifier, look up secret, verify HMAC chain, check caveats — but replaces the preimage check with a PoW satisfier that validates the computational proof embedded in the macaroon's caveats. Tests cover valid PoW, wrong nonce, wrong service, tampered macaroon, and revoked secret scenarios.
Introduce PoWAuthenticator which implements the Authenticator interface using proof-of-work instead of Lightning invoice verification. Accept extracts the macaroon via MacaroonFromHeader and delegates to Mint.VerifyL402PoW. FreshChallengeHeader mints a new L402 and returns a WWW-Authenticate header with the macaroon and pow difficulty parameter. The Minter interface gains a VerifyL402PoW method. The authenticator holds a map of service names to their required difficulties, allowing per-service difficulty configuration.
Split the single authenticator field on Proxy into lnAuthenticator and
powAuthenticator, selecting the appropriate one per request based on the
target service's AuthMethod. The constructor now takes both authenticators
(powAuth may be nil if no PoW services are configured).
Service gains AuthMethod ("POW" or default "lightning") and PowDifficulty
fields with validation in prepareServices. handlePaymentRequired now
receives the authenticator as a parameter to issue the correct challenge
type.
ExtractRateLimitKey in the rate limiter switches from FromHeader to
MacaroonFromHeader so that PoW-authenticated requests are correctly
rate-limited by token ID rather than falling back to IP.
Introduce PoWClientInterceptor, a gRPC client interceptor that handles L402 PoW challenges without requiring an LND connection. It follows the same structure as the existing ClientInterceptor: check store for an existing token, make the request, handle 402 by solving the challenge, then retry. The PoW-specific flow parses the pow="<difficulty>" parameter from the WWW-Authenticate header, extracts the token ID from the macaroon identifier, calls SolvePoW to find a valid nonce, and stores the completed token for reuse on subsequent requests.
In createProxy, detect services with AuthMethod "POW" and create a dedicated PoW mint and authenticator for them. The PoW mint uses a PoWChallenger (no LND needed) and shares the same secret store and service limiter as the Lightning mint. Both authenticators are passed to proxy.New for per-service dispatch. The sample configuration gains a PoW service example showing the authmethod and powdifficulty fields.
Document the L402 proof-of-work authentication feature including the protocol flow, configuration options, difficulty tuning guide, wire formats for both HTTP and gRPC, client integration examples, package architecture, and security considerations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
partially addresses #192
Aperture supports an alternative authentication method based on proof-of-work
(PoW) instead of Lightning payments. This allows services to gate access behind
a computational cost rather than a monetary one, which is useful for rate
limiting, spam prevention, or environments where Lightning infrastructure is
unavailable.
PoW authentication is configured per service. A single Aperture instance can
serve both Lightning-authenticated and PoW-authenticated services
simultaneously.
Protocol Flow: