Documentation
¶
Index ¶
- Constants
- Variables
- func Int64ToHex(v int64) string
- func IpToInt64(addr *netip.Addr, ipV4SignificantBits int, ipV6SignificantBits int) (version int, integer int64)
- func Prng(seed string, length int) string
- func WithIPv4SignificantBits(bits int) func(rl *RateLimitOptions)
- func WithIPv6SignificantBits(bits int) func(rl *RateLimitOptions)
- func WithMaxChallengesPerIP(max int) func(rl *RateLimitOptions)
- func WithMaxChallengesWindow(window time.Duration) func(rl *RateLimitOptions)
- type Cap
- type Challenge
- type ChallengeParams
- type ChallengeRequest
- type ChallengeResponse
- type Driver
- type RateLimitOptions
- type RedeemData
- type VerifySolutionsRequest
Constants ¶
const DefaultIPv4SignificantBits = 32
const DefaultIPv6SignificantBits = 64
const DefaultMaxChallengesPerIP = 60
const DefaultMaxChallengesWindow = 1 * time.Minute
const DefaultValidDuration = 10 * time.Minute
DefaultValidDuration is the default duration that a Cap challenge is valid before it expires.
Variables ¶
var DefaultChallengeParams = ChallengeParams{
Difficulty: 4,
Count: 50,
SaltSize: 32,
}
DefaultChallengeParams are the default parameters to use for challenges.
var ErrChallengeNotFound = errors.New("challenge not found (or is expired or already redeemed)")
ErrChallengeNotFound is returned when a challenge is not found, expired, or already redeemed.
var ErrInsufficientSolutions = errors.New("insufficient solutions provided for challenge")
ErrInsufficientSolutions is returned when not enough solutions were provided for a challenge.
var ErrInvalidSolution = errors.New("invalid solution provided for challenge")
ErrInvalidSolution is returned when a solution is invalid.
var ErrRateLimited = errors.New("captcha could not be created because a rate limit was hit")
ErrRateLimited is returned when a rate limit for an IP has been reached. This can be returned by Driver.Store when an IP address is specified. Rate limits are defined by driver implementations.
Functions ¶
func Int64ToHex ¶
Int64ToHex converts an int64 into a hex string. It uses little endian byte order.
func IpToInt64 ¶
func IpToInt64(addr *netip.Addr, ipV4SignificantBits int, ipV6SignificantBits int) (version int, integer int64)
IpToInt64 converts an IP address to an integer, containing the significant bits specified. The ipV4SignificantBits parameter must be between 0 and 32 (inclusive), and the ipV6SignificantBits parameter must be between 0 and 64 (inclusive). IPv6 significant bits are capped to 64 instead of 128 because properly configured IPv6 networks tend not to provide smaller than /64 blocks. We also want to be able to store the IP in a 64 bit integer.
If the significant bits parameters are out of bounds, this function panics.
func Prng ¶
Prng generates a deterministic hex string of given length from a string seed. `seed` is the initial seed value. `length` is the output hex string length.
func WithIPv4SignificantBits ¶
func WithIPv4SignificantBits(bits int) func(rl *RateLimitOptions)
WithIPv4SignificantBits sets the significant bits (netmask) to use for rate limit counting on IPv4 addresses. When not specified, uses DefaultIPv4SignificantBits.
func WithIPv6SignificantBits ¶
func WithIPv6SignificantBits(bits int) func(rl *RateLimitOptions)
WithIPv6SignificantBits sets the significant bits (netmask) to use for rate limit counting on IPv6 addresses. When not specified, uses DefaultIPv6SignificantBits.
func WithMaxChallengesPerIP ¶
func WithMaxChallengesPerIP(max int) func(rl *RateLimitOptions)
WithMaxChallengesPerIP sets the maximum allowed challenges that can be generated per IP. The underlying window algorithm (e.g. sliding window, fixed window, etc.) is determined by the specific driver. When not specified, uses DefaultMaxChallengesPerIP.
func WithMaxChallengesWindow ¶
func WithMaxChallengesWindow(window time.Duration) func(rl *RateLimitOptions)
WithMaxChallengesWindow sets the window of time in which challenge creations are counted. The underlying window algorithm (e.g. sliding window, fixed window, etc.) is determined by the specific driver. When not specified, uses DefaultMaxChallengesWindow.
Types ¶
type Cap ¶
type Cap struct {
// contains filtered or unexported fields
}
Cap is an implementation of the Cap server. It can create challenges, accept solutions, and redeem tokens. It uses a driver for storing challenges (and optional rate limiting).
func (*Cap) CreateChallenge ¶
CreateChallenge generates a new challenge. If the request IP is set and the driver has rate limiting enabled, the function may return ErrRateLimited.
func (*Cap) UseRedeemToken ¶
UseRedeemToken uses up a redeem token and returns whether it was valid, invalidating it either way.
func (*Cap) VerifyChallengeSolutions ¶
func (s *Cap) VerifyChallengeSolutions(ctx context.Context, req VerifySolutionsRequest) (*RedeemData, error)
VerifyChallengeSolutions verifies a challenge's solution in exchange for a redeem token. Returns ErrChallengeNotFound if no challenge with the specified token exists. Returns ErrInsufficientSolutions if not enough solutions were provided. Returns ErrInvalidSolution if any solution is invalid.
type Challenge ¶
type Challenge struct {
// The token used to identify the challenge.
ChallengeToken string
// The redeem token, returned for correct solutions.
// This can be redeemed once by a client once received.
RedeemToken string
// The parameters used to generate the challenge and verify its solution.
Params ChallengeParams
// The expiration time, when solutions will no longer be accepted and the redeem token
// will no longer be accepted.
Expires time.Time
}
Challenge is a Cap challenge. It includes a challenge token, used to identify the challenge, a redeem token, which will be returned to clients who successfully solve the challenge, and params which are used to verify the challenge solution. The expiration time is the cutoff point where the challenge can no longer be solved, and its redeem token can no longer be redeemed.
func (*Challenge) ToResponse ¶
func (c *Challenge) ToResponse() ChallengeResponse
ToResponse returns a ChallengeResponse with the data inside the Challenge struct.
type ChallengeParams ¶
type ChallengeParams struct {
// The difficulty level of the challenge.
Difficulty int `json:"d"`
// The number of challenges to generate.
Count int `json:"c"`
// The size of the salt in bytes.
SaltSize int `json:"s"`
}
ChallengeParams are the parameters for creating and validating a challenge. This struct can be serialized into a valid JSON challenge response.
type ChallengeRequest ¶
type ChallengeRequest struct {
// The parameters for the challenge.
Params ChallengeParams
// The IP address that is requesting the challenge.
// Can be nil.
// Used by the driver for optional rate limiting.
IP *netip.Addr
// The duration for which the challenge is valid.
ValidDuration time.Duration
}
ChallengeRequest is a request to create a challenge
type ChallengeResponse ¶
type ChallengeResponse struct {
// The challenge parameters.
Params ChallengeParams `json:"challenge"`
// The challenge hash/token.
ChallengeHash string `json:"token"`
// The UNIX millisecond timestamp when the challenge expires.
ExpiresMs int64 `json:"expires"`
}
ChallengeResponse is a challenge response that can be sent to a client that requested one. It can be serialized to JSON and used as the JSON response for the challenge endpoint.
type Driver ¶
type Driver interface {
// Store stores a challenge.
// The challenge must not be nil.
// The driver is responsible for clearing expired challenges.
//
// If `ip` is not nil, it can be used for rate limiting, and the
// driver may return ErrRateLimited.
Store(ctx context.Context, challenge *Challenge, ip *netip.Addr) error
// GetUnredeemedChallenge returns the unredeemed challenge with the specified challenge token.
// Returns nil if the challenge does not exist, is expired, or is already redeemed.
//
// Will not return ErrRateLimited.
GetUnredeemedChallenge(ctx context.Context, challengeToken string) (*Challenge, error)
// UseRedeemToken redeems the specified redeem token.
// If the challenge did not exist, was expired, or was already redeemed, returns false.
// If the redemption was successful, returns true.
// The redeem token must not be able to be re-used after calling this function with it.
//
// Will not return ErrRateLimited.
UseRedeemToken(ctx context.Context, redeemToken string) (wasRedeemed bool, err error)
}
Driver is a driver for managing Cap challenges. A driver is responsible for storing, updating and retrieving challenges. It is also responsible for clearing expired challenges, and optionally enforcing rate limits.
type RateLimitOptions ¶
type RateLimitOptions struct {
IPv4SignificantBits int
IPv6SignificantBits int
MaxChallengesPerIP int
MaxChallengesWindow time.Duration
}
RateLimitOptions are options for applying rate limiting to the Cap drivers. It limits challenge creation based on IP address. The specific rate limit algorithm and implementation is defined by the driver. IP addresses are truncated to a specified number of bits. For example, you can limit based on the /24 subnet for IPv4 and /48 for IPv6 instead of the default /32 and /64.
func NewDefaultRateLimitOptions ¶
func NewDefaultRateLimitOptions() *RateLimitOptions
NewDefaultRateLimitOptions returns a new RateLimitOptions with default values.
type RedeemData ¶
RedeemData is the redemption data returned after verifying a successful solution.
type VerifySolutionsRequest ¶
type VerifySolutionsRequest struct {
ChallengeToken string `json:"token"`
Solutions []uint32 `json:"solutions"`
}
VerifySolutionsRequest is the request for verifying a challenge solution. It can be used to deserialize a JSON request body.