attack

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package attack is Keyway's generative, invariant-based JWT attack harness. It replaces a fixed list of hand-written attack tokens with taxonomy-driven generators: each generator emits one or more concrete tokens tagged with the threat (from internal/threats) it exercises and the verdict a *correct* verifier must return. A reference verifier (oracle, built on go-jose) encodes the security invariants, so the corpus is self-validating — every "must reject" token is provably rejected by a known-correct implementation — and Evaluate can then fire the same corpus at a live target and flag any it wrongly accepts.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CoveredThreatIDs

func CoveredThreatIDs(corpus []Token) []string

CoveredThreatIDs returns only the threats the harness can detect end-to-end at a single endpoint (self-contained). This is the honest bridge to the coverage taxonomy: it excludes callback-dependent checks (jku/x5u) that are generated but not yet actionable against a live target.

func NewContext

func NewContext() (GenContext, Policy, error)

NewContext builds a GenContext with a local trusted signer and fresh attacker key, plus the matching Policy for the reference oracle (same trust key). Used for offline corpus validation.

func ThreatIDs

func ThreatIDs(corpus []Token) []string

ThreatIDs returns the distinct, sorted set of threat IDs the corpus exercises.

Types

type Finding

type Finding struct {
	Token  Token
	Actual Verdict
}

Finding is a token whose actual verdict differed from the correct one — i.e. an attack token that a verifier wrongly accepted (or a control it wrongly rejected).

func Evaluate

func Evaluate(ctx context.Context, corpus []Token, verify VerifyFunc) ([]Finding, error)

Evaluate runs every token in the corpus through verify and returns the tokens whose actual verdict disagreed with the correct one. Against the reference oracle this must be empty (the corpus is valid); against a vulnerable target it lists exactly what that target got wrong.

func (Finding) Vulnerability

func (f Finding) Vulnerability() bool

Vulnerability reports whether this finding is an accepted attack (the dangerous direction), as opposed to a rejected control (a functional problem).

type GenContext

type GenContext struct {
	Trusted        TrustedSigner
	AttackerKey    *rsa.PrivateKey
	Issuer         string
	Audience       string
	RequiredClaims []string
	Now            time.Time
}

GenContext holds what the generators build tokens around: the trusted issuer (a TrustedSigner — a local key offline, or the real issuer's MintFunc when scanning live), an attacker key for forgeries, and the expected issuer/audience /required claims the correct token must carry.

func NewLiveContext

func NewLiveContext(trusted TrustedSigner, issuer, audience string, required []string, now time.Time) (GenContext, error)

NewLiveContext builds a GenContext for scanning a real target: claim-level attacks are signed by the given trusted signer (the real issuer), while forgeries use a fresh attacker key. issuer/audience/required come from the consumer's expected contract so the control token is one the target accepts.

type HTTPTarget

type HTTPTarget struct {
	URL         string
	Method      string // default GET
	Header      string // default "Authorization"
	Prefix      string // default "Bearer "
	AcceptCodes []int  // default {200,201,202,204}
	Client      *http.Client
}

HTTPTarget describes a live endpoint that verifies a bearer token. AcceptCodes are the HTTP statuses that mean "token honored"; everything else is a reject.

func (HTTPTarget) Verify

func (t HTTPTarget) Verify(ctx context.Context, token string) (Verdict, error)

Verify sends the token to the target and maps the response status to a verdict. It is the live counterpart to the reference oracle, so the same corpus that is proven correct offline can scan a real service.

type Oracle

type Oracle struct{ Policy Policy }

Oracle is the reference verifier — a from-spec-correct implementation of the invariants, built on go-jose (a mature, independent JOSE library). It pins the algorithm to RS256 and the key to the configured trust key, ignores any key material carried inside the token (jku/x5u/jwk/x5c/kid), and validates claims. Using a real library as the oracle keeps corpus validation from being circular: "a known-correct verifier rejects every attack token."

func (Oracle) Verify

func (o Oracle) Verify(_ context.Context, token string) (Verdict, error)

Verify returns Accept only when go-jose confirms an RS256 signature under the trusted key AND every claim invariant holds; otherwise Reject.

type Policy

type Policy struct {
	TrustedKey     *rsa.PublicKey
	Issuer         string
	Audience       string
	RequiredClaims []string
	Now            time.Time
	Skew           time.Duration
}

Policy is what a correctly-configured verifier enforces: a pinned algorithm and trust key, the expected issuer/audience, and any required claims.

type Token

type Token struct {
	ThreatID  string  // maps to internal/threats (e.g. "SIG-01")
	Name      string  // stable, e.g. "alg_none"
	Rationale string  // the invariant this probes
	JWS       string  // the compact JWS
	Expect    Verdict // what a correct verifier must do
	// SelfContained is true when firing this single token at one endpoint reveals
	// a vulnerable target with no extra infrastructure. Checks that are NOT
	// self-contained (e.g. jku/x5u, which need Keyway to host an attacker JWKS)
	// are still generated and oracle-validated, but do not count toward detection
	// coverage until their callback server lands.
	SelfContained bool
}

Token is one generated token: the raw JWS, the threat it exercises, and the verdict a correct verifier MUST return. Controls carry Expect=Accept.

func Corpus

func Corpus(c GenContext) ([]Token, error)

Corpus generates the full attack-token corpus for the given context. The control and the claim-level attacks are signed by the trusted issuer (so a live target's signature check passes and its claim validation is what's exercised); forgeries are constructed directly.

type TrustedSigner

type TrustedSigner interface {
	// Sign returns a compact JWS validly signed by the trusted issuer over claims.
	Sign(claims map[string]any) (string, error)
	// PublicKeyPEM returns the issuer's public signing key (PKIX PEM), used to
	// build the RS/HS confusion attack. Empty means that generator is skipped.
	PublicKeyPEM() string
}

TrustedSigner is the seam between the harness and the *trusted issuer*. The harness signs its control token and its claim-level attacks (bad claims, valid signature) through this, so in production those tokens are signed by the real issuer's key (via the probe engine's MintFunc) and a live target's signature check passes — leaving the claim validation as the thing actually under test. In tests it is backed by a local key so the corpus can be validated offline.

type Verdict

type Verdict int

Verdict is the accept/reject decision a verifier makes about a token.

const (
	Reject Verdict = iota // the token was refused (the safe outcome for an attack)
	Accept                // the token was honored
)

func (Verdict) String

func (v Verdict) String() string

type VerifyFunc

type VerifyFunc func(ctx context.Context, token string) (Verdict, error)

VerifyFunc decides a single token — implemented by the reference oracle (for self-validation) and by the live HTTP runner (for scanning a target).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL