Documentation
¶
Overview ¶
Package crypto provides cryptographic primitives for authserver.
Index ¶
- Constants
- Variables
- func AssertionAlgorithms() []jose.SignatureAlgorithm
- func BuildJWKS(keys ...*KeyPair) jose.JSONWebKeySet
- func CompareBcrypt(hash, plaintext string) error
- func CompareClientSecret(hash, plaintext string) error
- func CompareHash(hash, plain string) bool
- func ComputeATH(accessToken string) string
- func ComputeJKT(jwk jose.JSONWebKey) (string, error)
- func ComputeS256Challenge(verifier string) string
- func CreateDPoPProof(signer jose.Signer, jti, htm, htu string, iat time.Time, nonce, ath string) (string, error)
- func GenerateAuthCode() string
- func GenerateClientID() string
- func GenerateClientSecret() string
- func GenerateNonce() string
- func GenerateRandomString(n int) string
- func GenerateVerifier() string
- func HashBcrypt(plaintext string) (string, error)
- func HashClientSecret(plaintext string) (string, error)
- func HashSHA256(s string) string
- func IsDPoPBound(claims *AccessTokenClaims) bool
- func NewDPoPSigner(privateKey interface{}, alg jose.SignatureAlgorithm) (jose.Signer, error)
- func SetClientSecretPepper(pepper string)
- func SignAccessToken(kp *KeyPair, claims AccessTokenClaims) (string, error)
- func ValidateAccessTokenClaims(c AccessTokenClaims) error
- func ValidateChallengeMethod(method string) error
- func ValidateIDJAG(raw string, trustedKeys *jose.JSONWebKeySet, expectedAudience string, ...) (*token.IdentityAssertion, error)
- func VerifyS256(verifier, challenge string) error
- type AccessTokenClaims
- type DPoPResult
- type KeyPair
Constants ¶
const ( // PKCEMinVerifierLength is the minimum code_verifier length per RFC 7636 §4.1. PKCEMinVerifierLength = 43 // PKCEMaxVerifierLength is the maximum code_verifier length per RFC 7636 §4.1. PKCEMaxVerifierLength = 128 )
const DefaultBcryptCost = 12
DefaultBcryptCost is the bcrypt cost factor for hashing client secrets.
Variables ¶
var ErrClientSecretMismatch = errors.New("client secret mismatch")
ErrClientSecretMismatch is returned when a client secret does not match its stored HMAC hash.
Functions ¶
func AssertionAlgorithms ¶
func AssertionAlgorithms() []jose.SignatureAlgorithm
AssertionAlgorithms returns the list of algorithms accepted for ID-JAG assertions.
func BuildJWKS ¶
func BuildJWKS(keys ...*KeyPair) jose.JSONWebKeySet
BuildJWKS constructs a jose.JSONWebKeySet from one or more key pairs.
func CompareBcrypt ¶
CompareBcrypt compares a bcrypt hash with a plaintext string. Returns nil on match, error otherwise.
func CompareClientSecret ¶
CompareClientSecret verifies a client secret against its stored hash, auto-detecting the scheme (HMAC-SHA256 vs legacy bcrypt). Returns nil on a match. The comparison is constant-time.
func CompareHash ¶
CompareHash performs a constant-time comparison of a hex-encoded SHA-256 hash against the hash of plain. Returns true if they match.
func ComputeATH ¶
ComputeATH computes the access token hash for DPoP ath claim. Returns base64url(SHA-256(access_token)).
func ComputeJKT ¶
func ComputeJKT(jwk jose.JSONWebKey) (string, error)
ComputeJKT computes the JWK Thumbprint per RFC 7638 using SHA-256. Returns the base64url-encoded (no padding) SHA-256 hash of the JWK's canonical form (sorted, minimal members as per the key type).
func ComputeS256Challenge ¶
ComputeS256Challenge computes the S256 code_challenge for the given verifier. challenge = BASE64URL(SHA256(verifier)) per RFC 7636 §4.2.
func CreateDPoPProof ¶
func CreateDPoPProof(signer jose.Signer, jti, htm, htu string, iat time.Time, nonce, ath string) (string, error)
CreateDPoPProof creates a DPoP proof JWT. Used in tests and as a helper.
func GenerateAuthCode ¶
func GenerateAuthCode() string
GenerateAuthCode returns a random authorization code (43 chars, 32 bytes entropy).
func GenerateClientID ¶
func GenerateClientID() string
GenerateClientID returns a random client identifier (22 chars, 16 bytes entropy).
func GenerateClientSecret ¶
func GenerateClientSecret() string
GenerateClientSecret returns a random client secret (43 chars, 32 bytes entropy).
func GenerateNonce ¶
func GenerateNonce() string
GenerateNonce creates a cryptographically random nonce for DPoP. Returns a 32-byte base64url-encoded string (43 chars).
func GenerateRandomString ¶
GenerateRandomString returns a URL-safe base64-encoded random string of n random bytes (the encoded string will be longer than n).
func GenerateVerifier ¶
func GenerateVerifier() string
GenerateVerifier returns a cryptographically random PKCE code_verifier (43 chars from 32 bytes of entropy, URL-safe base64).
func HashBcrypt ¶
HashBcrypt returns the bcrypt hash of the given plaintext.
func HashClientSecret ¶
HashClientSecret hashes a client secret for storage. With a pepper configured it uses HMAC-SHA256 (fast); otherwise it falls back to bcrypt.
func HashSHA256 ¶
HashSHA256 returns the hex-encoded SHA-256 hash of s. Used for hashing auth codes and refresh tokens before storage.
func IsDPoPBound ¶
func IsDPoPBound(claims *AccessTokenClaims) bool
IsDPoPBound checks if an access token has a cnf.jkt claim indicating DPoP binding. This is used to determine if a resource request requires a DPoP proof.
func NewDPoPSigner ¶
func NewDPoPSigner(privateKey interface{}, alg jose.SignatureAlgorithm) (jose.Signer, error)
NewDPoPSigner creates a jose.Signer for DPoP proofs with the correct headers. Sets typ: dpop+jwt and embeds the public key in the jwk header.
func SetClientSecretPepper ¶
func SetClientSecretPepper(pepper string)
SetClientSecretPepper configures the HMAC key used to hash and verify client secrets. With a non-empty pepper, HashClientSecret produces HMAC-SHA256 hashes; with an empty pepper it falls back to bcrypt. Existing hashes of either scheme continue to verify regardless. Call once at startup, before serving requests.
func SignAccessToken ¶
func SignAccessToken(kp *KeyPair, claims AccessTokenClaims) (string, error)
SignAccessToken signs an RFC 9068 JWT access token with typ: at+jwt. Returns an error if required claims are missing.
For concrete key types (*ecdsa.PrivateKey, *rsa.PrivateKey) the key is passed directly to go-jose. For opaque crypto.Signer implementations (e.g. Vault Transit, KMS) the key is wrapped as a jose.OpaqueSigner so go-jose delegates signing without needing access to raw key material.
func ValidateAccessTokenClaims ¶
func ValidateAccessTokenClaims(c AccessTokenClaims) error
ValidateAccessTokenClaims checks that required JWT claims are non-empty before signing.
func ValidateChallengeMethod ¶
ValidateChallengeMethod rejects anything other than S256. plain is never accepted per security invariant.
func ValidateIDJAG ¶
func ValidateIDJAG(raw string, trustedKeys *jose.JSONWebKeySet, expectedAudience string, maxAge time.Duration) (*token.IdentityAssertion, error)
ValidateIDJAG parses and validates an ID-JAG (Identity Assertion Authorization Grant) JWT per RFC 7523 and the MCP Enterprise-Managed Authorization extension.
Validation steps:
- Parse as signed JWT — reject alg:none and HS* algorithms.
- Verify typ header == "oauth-id-jag+jwt".
- Verify signature against provided JWKS.
- Validate exp (not expired, 30s clock skew).
- Validate iat (not in future, not too old per maxAge).
- Validate aud matches expectedAudience.
- Validate iss, sub, client_id, jti are all non-empty.
func VerifyS256 ¶
VerifyS256 verifies a PKCE code_verifier against a stored code_challenge. Returns nil on success.
Types ¶
type AccessTokenClaims ¶
type AccessTokenClaims struct {
Issuer string `json:"iss"`
Subject string `json:"sub"`
Audience []string `json:"aud"`
ClientID string `json:"client_id"`
Scope string `json:"scope,omitempty"`
JTI string `json:"jti"`
IssuedAt int64 `json:"iat"`
Expiry int64 `json:"exp"`
NotBefore int64 `json:"nbf"`
Cnf map[string]interface{} `json:"cnf,omitempty"` // DPoP confirmation claim (RFC 9449 §6): {"jkt": "<thumbprint>"}
Act map[string]interface{} `json:"act,omitempty"` // RFC 8693 §4.1: delegation chain {"sub": "...", "act": {...}}
MayAct map[string]interface{} `json:"may_act,omitempty"` // RFC 8693 §5: authorized actors {"sub": "..."}
AgentID string `json:"agent_id,omitempty"` // Authplane extension: client_id of the acting agent
AgentChain []string `json:"agent_chain,omitempty"` // Authplane extension: ordered delegation chain [root, ..., acting_agent]
}
AccessTokenClaims are the claims in an RFC 9068 JWT access token. Audience is []string for JSON marshal flexibility. Per RFC 8707 we bind each token to a single resource, so Audience will have exactly one element. JSON serializes as ["https://..."] (array), which is valid per RFC 7519 §4.1.3.
func VerifyAccessToken ¶
func VerifyAccessToken(token string, jwks *jose.JSONWebKeySet) (*AccessTokenClaims, error)
VerifyAccessToken parses and verifies a JWT access token against the given JWKS. It checks the signature, typ header, and expiry.
func VerifyAccessTokenWithIssuer ¶
func VerifyAccessTokenWithIssuer(token string, jwks *jose.JSONWebKeySet, expectedIssuer string) (*AccessTokenClaims, error)
VerifyAccessTokenWithIssuer is like VerifyAccessToken but also validates the issuer claim. CODE FIX: Matrix 1.5.16 — issuer MUST be verified to prevent cross-issuer token injection.
func (*AccessTokenClaims) HasAudience ¶
func (c *AccessTokenClaims) HasAudience(aud string) bool
HasAudience returns true if the given value is present in the Audience slice.
type DPoPResult ¶
type DPoPResult struct {
JKT string // JWK thumbprint (base64url-encoded SHA-256)
JTI string // unique identifier from the proof, for replay detection
Nonce string // nonce from the proof, if present
}
DPoPResult holds the validated output from a DPoP proof.
func ValidateProof ¶
func ValidateProof(proof, method, reqURL, serverNonce, accessTokenHash string, proofLifetime time.Duration) (*DPoPResult, error)
ValidateProof validates a DPoP proof JWT per RFC 9449 §4.3.
Parameters:
- proof: the raw DPoP proof JWT string from the DPoP header
- method: the HTTP method of the request (e.g. "POST")
- reqURL: the HTTP URL of the request (scheme + host + path; query is stripped)
- serverNonce: the expected server nonce (empty string if nonce is not required)
- accessTokenHash: base64url(SHA-256(access_token)) for ath validation (empty if not applicable)
- proofLifetime: maximum age of the proof (|now - iat| must be within this)
Returns:
- DPoPResult with JKT, JTI, and Nonce on success
- domain.ErrDPoPInvalidProof on structural/cryptographic errors
- domain.ErrDPoPNonceRequired if serverNonce is non-empty but proof has no nonce
- domain.ErrDPoPNonceMismatch if proof nonce doesn't match serverNonce
type KeyPair ¶
type KeyPair struct {
PrivateKey crypto.Signer
PublicKey crypto.PublicKey
Algorithm jose.SignatureAlgorithm
KeyID string
}
KeyPair holds a signing key and its metadata.
func GenerateKeyPair ¶
GenerateKeyPair generates an ECDSA or RSA key pair for the given algorithm. Supported: ES256 (P-256), RS256 (RSA 2048).