httpsig

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 20 Imported by: 0

README

httpsig

HTTP Message Signatures (RFC 9421) for Go with optional Content-Digest (RFC 9530) support.

go get github.com/vitalvas/kasper/httpsig

Algorithms

Algorithm Constant Key Type
Ed25519 AlgorithmEd25519 ed25519.PrivateKey / ed25519.PublicKey
ECDSA P-256 SHA-256 AlgorithmECDSAP256SHA256 *ecdsa.PrivateKey / *ecdsa.PublicKey (P-256)
ECDSA P-384 SHA-384 AlgorithmECDSAP384SHA384 *ecdsa.PrivateKey / *ecdsa.PublicKey (P-384)
RSA-PSS SHA-512 AlgorithmRSAPSSSHA512 *rsa.PrivateKey / *rsa.PublicKey (2048+ bits)
RSA v1.5 SHA-256 AlgorithmRSAv15SHA256 *rsa.PrivateKey / *rsa.PublicKey (2048+ bits)
HMAC SHA-256 AlgorithmHMACSHA256 []byte (32+ bytes)

Creating Keys

Each algorithm has a NewXxxSigner / NewXxxVerifier constructor pair. Key material is validated at construction time (nil check, curve check, minimum key size).

// Ed25519
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
signer, err := httpsig.NewEd25519Signer("my-key-id", priv)
verifier, err := httpsig.NewEd25519Verifier("my-key-id", pub)

// ECDSA P-256
ecKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
signer, err := httpsig.NewECDSAP256Signer("ec-key", ecKey)
verifier, err := httpsig.NewECDSAP256Verifier("ec-key", &ecKey.PublicKey)

// ECDSA P-384
ecKey384, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
signer, err := httpsig.NewECDSAP384Signer("ec384-key", ecKey384)
verifier, err := httpsig.NewECDSAP384Verifier("ec384-key", &ecKey384.PublicKey)

// RSA-PSS (2048+ bits)
rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048)
signer, err := httpsig.NewRSAPSSSigner("rsa-key", rsaKey)
verifier, err := httpsig.NewRSAPSSVerifier("rsa-key", &rsaKey.PublicKey)

// RSA v1.5 (2048+ bits)
signer, err := httpsig.NewRSAv15Signer("rsa15-key", rsaKey)
verifier, err := httpsig.NewRSAv15Verifier("rsa15-key", &rsaKey.PublicKey)

// HMAC-SHA256 (32+ byte key)
hmacKey := make([]byte, 32)
rand.Read(hmacKey)
signer, err := httpsig.NewHMACSHA256Signer("hmac-key", hmacKey)
verifier, err := httpsig.NewHMACSHA256Verifier("hmac-key", hmacKey)

Signing Requests

SignRequest adds Signature and Signature-Input headers to an HTTP request in-place.

signer, err := httpsig.NewEd25519Signer("my-key-id", privateKey)
if err != nil {
    log.Fatal(err)
}

req, _ := http.NewRequest(http.MethodPost, "https://api.example.com/orders", body)

err = httpsig.SignRequest(req, httpsig.SignConfig{
    Signer: signer,
})
if err != nil {
    log.Fatal(err)
}
// req now has Signature and Signature-Input headers.
SignConfig Options

All fields except Signer are optional.

err := httpsig.SignRequest(req, httpsig.SignConfig{
    // Required: produces signatures.
    Signer: signer,

    // Signature label in Signature/Signature-Input headers.
    // Default: "sig1".
    Label: "my-sig",

    // Component identifiers to include in the signature base.
    // Default: [ComponentMethod, ComponentAuthority, ComponentPath].
    CoveredComponents: []string{
        httpsig.ComponentMethod,
        httpsig.ComponentAuthority,
        httpsig.ComponentPath,
        httpsig.ComponentQuery,
        "content-type",
    },

    // Optional nonce for replay protection.
    Nonce: "unique-request-id",

    // Optional application-specific tag.
    Tag: "my-app",

    // Signature creation time. Zero value = time.Now().
    // Created: time.Unix(1700000000, 0),

    // Signature expiration time. Zero value = no expiration.
    Expires: time.Now().Add(5 * time.Minute),

    // When set, computes Content-Digest (RFC 9530) and adds
    // "content-digest" to covered components automatically.
    DigestAlgorithm: httpsig.DigestSHA256,
})

Verifying Requests

VerifyRequest checks the Signature and Signature-Input headers on an incoming request. A KeyResolver function looks up the verifier for each key ID and algorithm.

// Key resolver maps key IDs to verifiers. This is where you load
// public keys from a database, file, or in-memory store.
resolver := func(r *http.Request, keyID string, alg httpsig.Algorithm) (httpsig.Verifier, error) {
    switch keyID {
    case "client-a":
        return httpsig.NewEd25519Verifier(keyID, clientAPublicKey)
    case "client-b":
        return httpsig.NewECDSAP256Verifier(keyID, clientBPublicKey)
    default:
        return nil, fmt.Errorf("unknown key: %s", keyID)
    }
}

err := httpsig.VerifyRequest(req, httpsig.VerifyConfig{
    // Required: looks up the verifier for a given key ID and algorithm.
    Resolver: resolver,

    // When empty, the first signature found is verified.
    // Set to verify a specific signature label.
    Label: "sig1",

    // Verification fails if any of these components are missing
    // from the signature's covered components.
    RequiredComponents: []string{
        httpsig.ComponentMethod, httpsig.ComponentAuthority, httpsig.ComponentPath,
    },

    // Reject signatures older than this duration. Zero = no age check.
    MaxAge: 5 * time.Minute,

    // When true, also verifies the Content-Digest header against
    // the request body before checking the signature.
    RequireDigest: true,
})
if err != nil {
    // Handle verification failure.
    // Possible errors: ErrSignatureNotFound, ErrSignatureInvalid,
    // ErrSignatureExpired, ErrMissingComponent, ErrMalformedHeader,
    // ErrDigestMismatch, ErrDigestNotFound.
    log.Printf("signature verification failed: %v", err)
}

Client Transport

NewTransport creates an http.RoundTripper that automatically signs every outgoing request. It accepts an *http.Transport for full control over proxy, TLS, timeouts, and connection pooling. Pass nil for sensible defaults (cloned from http.DefaultTransport). The original request is cloned before signing, so it is never mutated.

Basic usage with defaults:

signer, err := httpsig.NewEd25519Signer("my-key-id", privateKey)
if err != nil {
    log.Fatal(err)
}

client := &http.Client{
    Transport: httpsig.NewTransport(nil, httpsig.SignConfig{
        Signer: signer,
        CoveredComponents: []string{
            httpsig.ComponentMethod, httpsig.ComponentAuthority,
            httpsig.ComponentPath, "content-type",
        },
        DigestAlgorithm: httpsig.DigestSHA256,
    }),
}

resp, err := client.Post(
    "https://api.example.com/orders",
    "application/json",
    strings.NewReader(`{"item": "widget", "qty": 1}`),
)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

With HTTP/SOCKS proxy, custom TLS, and timeouts:

base := &http.Transport{
    // HTTP proxy from environment (HTTP_PROXY, HTTPS_PROXY, NO_PROXY).
    Proxy: http.ProxyFromEnvironment,

    // Or use a fixed SOCKS5 proxy:
    // Proxy: func(*http.Request) (*url.URL, error) {
    //     return url.Parse("socks5://proxy.example.com:1080")
    // },

    TLSClientConfig: &tls.Config{
        MinVersion: tls.VersionTLS13,
    },
    TLSHandshakeTimeout:   10 * time.Second,
    IdleConnTimeout:        90 * time.Second,
    ResponseHeaderTimeout:  30 * time.Second,
    MaxIdleConns:           100,
    MaxIdleConnsPerHost:    10,
    DisableCompression:     false,
}

client := &http.Client{
    Transport: httpsig.NewTransport(base, httpsig.SignConfig{
        Signer: signer,
    }),
}

Server Middleware

Middleware returns a mux.MiddlewareFunc for the kasper/mux router. Unsigned or invalid requests receive a 401 Unauthorized response by default.

resolver := func(r *http.Request, keyID string, alg httpsig.Algorithm) (httpsig.Verifier, error) {
    // Look up verifier by key ID.
    v, ok := verifiers[keyID]
    if !ok {
        return nil, fmt.Errorf("unknown key: %s", keyID)
    }
    return v, nil
}

mw, err := httpsig.Middleware(httpsig.MiddlewareConfig{
    Verify: httpsig.VerifyConfig{
        Resolver:           resolver,
        RequiredComponents: []string{
                httpsig.ComponentMethod, httpsig.ComponentAuthority, httpsig.ComponentPath,
            },
        MaxAge:             5 * time.Minute,
        RequireDigest:      true,
    },

    // Optional custom error handler. Default: 401 with no body.
    OnError: func(w http.ResponseWriter, r *http.Request, err error) {
        http.Error(w, "signature verification failed", http.StatusUnauthorized)
    },
})
if err != nil {
    log.Fatal(err)
}

router := mux.NewRouter()
router.Use(mw)
router.HandleFunc("/api/v1/orders", handleOrders).Methods(http.MethodPost)

Content-Digest (RFC 9530)

Standalone Content-Digest generation and verification, independent of message signatures. Supports SHA-256 (DigestSHA256) and SHA-512 (DigestSHA512).

// Generate: reads the body, sets the Content-Digest header,
// and restores the body for downstream readers.
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(body))
err := httpsig.SetContentDigest(req, httpsig.DigestSHA256)
// req.Header.Get("Content-Digest") is now "sha-256=:<base64>:"

// Verify: reads the body, checks the Content-Digest header,
// and restores the body.
err = httpsig.VerifyContentDigest(req)
// Returns ErrDigestMismatch if the body was tampered with.
// Returns ErrDigestNotFound if the header is missing.
// Returns ErrUnsupportedDigest if the algorithm is not recognized.

When used with SignRequest, setting DigestAlgorithm computes the digest and adds "content-digest" to the covered components automatically:

err := httpsig.SignRequest(req, httpsig.SignConfig{
    Signer:          signer,
    DigestAlgorithm: httpsig.DigestSHA256,
})
// Both Content-Digest and Signature headers are set.
// The signature covers the digest, binding body integrity to the signature.

End-to-End Example

A complete client-server example using NewTransport and Middleware together.

package main

import (
    "crypto/ed25519"
    "crypto/rand"
    "fmt"
    "log"
    "net/http"
    "strings"
    "time"

    "github.com/vitalvas/kasper/httpsig"
    "github.com/vitalvas/kasper/mux"
)

func main() {
    // Generate a key pair.
    pub, priv, err := ed25519.GenerateKey(rand.Reader)
    if err != nil {
        log.Fatal(err)
    }

    signer, err := httpsig.NewEd25519Signer("app-key", priv)
    if err != nil {
        log.Fatal(err)
    }

    verifier, err := httpsig.NewEd25519Verifier("app-key", pub)
    if err != nil {
        log.Fatal(err)
    }

    // Server: create a router with signature verification middleware.
    resolver := func(r *http.Request, keyID string, alg httpsig.Algorithm) (httpsig.Verifier, error) {
        if keyID == "app-key" && alg == httpsig.AlgorithmEd25519 {
            return verifier, nil
        }
        return nil, fmt.Errorf("unknown key: %s", keyID)
    }

    mw, err := httpsig.Middleware(httpsig.MiddlewareConfig{
        Verify: httpsig.VerifyConfig{
            Resolver:           resolver,
            RequiredComponents: []string{
                httpsig.ComponentMethod, httpsig.ComponentAuthority, httpsig.ComponentPath,
            },
            MaxAge:             5 * time.Minute,
            RequireDigest:      true,
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    router := mux.NewRouter()
    router.Use(mw)
    router.HandleFunc("/api/v1/orders", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"status":"created"}`))
    }).Methods(http.MethodPost)

    server := &http.Server{Addr: ":8080", Handler: router}
    go server.ListenAndServe()

    // Client: create an HTTP client with automatic signing.
    client := &http.Client{
        Transport: httpsig.NewTransport(nil, httpsig.SignConfig{
            Signer:          signer,
            DigestAlgorithm: httpsig.DigestSHA256,
        }),
    }

    resp, err := client.Post(
        "http://localhost:8080/api/v1/orders",
        "application/json",
        strings.NewReader(`{"item":"widget"}`),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    fmt.Println("Status:", resp.StatusCode) // Status: 201
}

Covered Components

Derived components (prefixed with @):

Constant Value Description
ComponentMethod @method HTTP method (e.g., GET, POST)
ComponentAuthority @authority Host header, lowercased (e.g., example.com:8080)
ComponentPath @path Request path (e.g., /api/v1/orders)
ComponentQuery @query Query string with ? prefix (e.g., ?page=2&sort=name)
ComponentScheme @scheme Request scheme (http or https)
ComponentTargetURI @target-uri Full target URI (e.g., https://example.com/path?q=1)
ComponentRequestTarget @request-target Path and optional query (e.g., /path?q=1)

Header fields: any HTTP header name, lowercased (e.g., content-type, content-digest, authorization). Multi-value headers are joined with , .

Default covered components when none specified: ComponentMethod, ComponentAuthority, ComponentPath.

Errors

Error Description
ErrNoSigner SignConfig.Signer is nil
ErrNoCoveredComponents SignConfig.CoveredComponents is empty after defaults
ErrNoResolver VerifyConfig.Resolver is nil
ErrSignatureNotFound Signature label not found in headers
ErrSignatureInvalid Cryptographic verification failed
ErrSignatureExpired Signature age exceeds MaxAge
ErrMissingComponent Required component absent from signature
ErrMalformedHeader Cannot parse Signature or Signature-Input headers
ErrInvalidKey Key material is nil, wrong curve, or too small
ErrDigestMismatch Content-Digest does not match body
ErrDigestNotFound Content-Digest header missing when required
ErrUnsupportedDigest Digest algorithm not recognized
ErrUnknownComponent Unrecognized component identifier

Standards

Documentation

Overview

Package httpsig implements HTTP Message Signatures per RFC 9421 with optional Content-Digest support per RFC 9530.

It provides both client-side signing (via Transport) and server-side verification (via Middleware) for the kasper HTTP toolkit.

Supported Algorithms

Six signature algorithms are supported:

  • ed25519 (Edwards-Curve DSA)
  • ecdsa-p256-sha256 (ECDSA P-256)
  • ecdsa-p384-sha384 (ECDSA P-384)
  • rsa-pss-sha512 (RSASSA-PSS)
  • rsa-v1_5-sha256 (RSASSA-PKCS1-v1_5)
  • hmac-sha256 (HMAC)

Signing Requests

Use SignRequest to add Signature and Signature-Input headers to an HTTP request:

signer, err := httpsig.NewEd25519Signer("my-key-id", privateKey)
if err != nil {
    log.Fatal(err)
}

err = httpsig.SignRequest(req, httpsig.SignConfig{
    Signer:            signer,
    CoveredComponents: []string{httpsig.ComponentMethod, httpsig.ComponentAuthority, httpsig.ComponentPath},
})
if err != nil {
    log.Fatal(err)
}

Verifying Requests

Use VerifyRequest to verify the signature on an incoming request:

resolver := func(r *http.Request, keyID string, alg httpsig.Algorithm) (httpsig.Verifier, error) {
    // Look up the verifier for the given key ID and algorithm.
    return verifier, nil
}

err := httpsig.VerifyRequest(req, httpsig.VerifyConfig{
    Resolver:           resolver,
    RequiredComponents: []string{httpsig.ComponentMethod, httpsig.ComponentAuthority},
    MaxAge:             5 * time.Minute,
})

Client Transport

NewTransport creates an http.RoundTripper that automatically signs all outgoing requests. Pass an *http.Transport to configure proxy, TLS, and timeout settings. Pass nil for sensible defaults:

client := &http.Client{
    Transport: httpsig.NewTransport(nil, httpsig.SignConfig{
        Signer: signer,
    }),
}

resp, err := client.Get("https://api.example.com/resource")

With custom proxy and TLS:

base := &http.Transport{
    Proxy:           http.ProxyFromEnvironment,
    TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS13},
}
client := &http.Client{
    Transport: httpsig.NewTransport(base, httpsig.SignConfig{
        Signer: signer,
    }),
}

Server Middleware

Middleware returns a mux.MiddlewareFunc that verifies signatures on incoming requests. It integrates with the kasper/mux router:

mw, err := httpsig.Middleware(httpsig.MiddlewareConfig{
    Verify: httpsig.VerifyConfig{
        Resolver: resolver,
    },
})
if err != nil {
    log.Fatal(err)
}
router.Use(mw)

Content-Digest

Optional Content-Digest support (RFC 9530) can be used standalone or integrated with signing:

// Standalone usage:
err := httpsig.SetContentDigest(req, httpsig.DigestSHA256)

// Integrated with signing (adds Content-Digest and includes it
// in covered components automatically):
err := httpsig.SignRequest(req, httpsig.SignConfig{
    Signer:          signer,
    DigestAlgorithm: httpsig.DigestSHA256,
})

Index

Constants

View Source
const (
	ComponentMethod        = "@method"
	ComponentAuthority     = "@authority"
	ComponentPath          = "@path"
	ComponentQuery         = "@query"
	ComponentTargetURI     = "@target-uri"
	ComponentScheme        = "@scheme"
	ComponentRequestTarget = "@request-target"
)

Derived component identifiers per RFC 9421 Section 2.2.

Variables

View Source
var (
	// ErrNoSigner is returned when SignConfig has no Signer configured.
	ErrNoSigner = errors.New("httpsig: signer must not be nil")

	// ErrNoCoveredComponents is returned when SignConfig has an empty
	// CoveredComponents slice.
	ErrNoCoveredComponents = errors.New("httpsig: covered components must not be empty")
)

Signing errors.

View Source
var (
	// ErrNoResolver is returned when VerifyConfig has no KeyResolver configured.
	ErrNoResolver = errors.New("httpsig: key resolver must not be nil")

	// ErrSignatureNotFound is returned when the expected signature label is
	// not present in the Signature-Input header.
	ErrSignatureNotFound = errors.New("httpsig: signature not found")

	// ErrSignatureInvalid is returned when signature verification fails.
	ErrSignatureInvalid = errors.New("httpsig: signature verification failed")

	// ErrSignatureExpired is returned when the signature has exceeded its
	// maximum allowed age.
	ErrSignatureExpired = errors.New("httpsig: signature expired")

	// ErrCreatedRequired is returned when MaxAge is set but the signature
	// does not contain a created parameter.
	ErrCreatedRequired = errors.New("httpsig: created parameter required when MaxAge is set")

	// ErrMissingComponent is returned when a required covered component
	// is absent from the signature.
	ErrMissingComponent = errors.New("httpsig: required component missing from signature")

	// ErrMalformedHeader is returned when Signature or Signature-Input
	// headers cannot be parsed.
	ErrMalformedHeader = errors.New("httpsig: malformed signature header")
)

Verification errors.

View Source
var (
	// ErrDigestMismatch is returned when Content-Digest verification fails.
	ErrDigestMismatch = errors.New("httpsig: content digest mismatch")

	// ErrDigestNotFound is returned when Content-Digest header is required
	// but not present.
	ErrDigestNotFound = errors.New("httpsig: content digest not found")

	// ErrUnsupportedDigest is returned when the digest algorithm is not
	// supported.
	ErrUnsupportedDigest = errors.New("httpsig: unsupported digest algorithm")
)

Digest errors.

View Source
var (
	// ErrInvalidKey is returned when key material is invalid (nil, wrong
	// curve, insufficient size, etc.).
	ErrInvalidKey = errors.New("httpsig: invalid key material")
)

Key material errors.

View Source
var (
	// ErrUnknownComponent is returned when an unrecognized derived component
	// identifier is used.
	ErrUnknownComponent = errors.New("httpsig: unknown component identifier")
)

Component errors.

Functions

func GenerateNonce

func GenerateNonce() (string, error)

GenerateNonce returns a cryptographically random nonce string suitable for use in SignConfig.Nonce. The returned value is 16 random bytes encoded as unpadded base64url (22 characters).

func Middleware

func Middleware(cfg MiddlewareConfig) (mux.MiddlewareFunc, error)

Middleware returns a mux.MiddlewareFunc that verifies HTTP message signatures on incoming requests per RFC 9421.

It returns ErrNoResolver if VerifyConfig.Resolver is nil.

func SetContentDigest

func SetContentDigest(r *http.Request, alg DigestAlgorithm) error

SetContentDigest reads the request body, computes the digest using the specified algorithm, sets the Content-Digest header per RFC 9530, and replaces the body so it can be read again.

func SignRequest

func SignRequest(r *http.Request, cfg SignConfig) error

SignRequest signs an HTTP request in-place by adding Signature and Signature-Input headers per RFC 9421.

func VerifyContentDigest

func VerifyContentDigest(r *http.Request) error

VerifyContentDigest verifies the Content-Digest header against the request body per RFC 9530. It supports multiple digest values in the header and verifies the first recognized algorithm.

func VerifyRequest

func VerifyRequest(r *http.Request, cfg VerifyConfig) error

VerifyRequest verifies an HTTP request signature per RFC 9421.

Types

type Algorithm

type Algorithm string

Algorithm identifies the HTTP message signature algorithm per RFC 9421 Section 3.3.

const (
	// AlgorithmRSAPSSSHA512 is RSASSA-PSS using SHA-512.
	AlgorithmRSAPSSSHA512 Algorithm = "rsa-pss-sha512"

	// AlgorithmRSAv15SHA256 is RSASSA-PKCS1-v1_5 using SHA-256.
	AlgorithmRSAv15SHA256 Algorithm = "rsa-v1_5-sha256"

	// AlgorithmHMACSHA256 is HMAC using SHA-256.
	AlgorithmHMACSHA256 Algorithm = "hmac-sha256"

	// AlgorithmECDSAP256SHA256 is ECDSA using curve P-256 and SHA-256.
	AlgorithmECDSAP256SHA256 Algorithm = "ecdsa-p256-sha256"

	// AlgorithmECDSAP384SHA384 is ECDSA using curve P-384 and SHA-384.
	AlgorithmECDSAP384SHA384 Algorithm = "ecdsa-p384-sha384"

	// AlgorithmEd25519 is Edwards-Curve Digital Signature Algorithm
	// using curve 25519.
	AlgorithmEd25519 Algorithm = "ed25519"
)

func (Algorithm) String

func (a Algorithm) String() string

String returns the string representation of the algorithm as registered in the HTTP Signature Algorithms Registry.

type DigestAlgorithm

type DigestAlgorithm string

DigestAlgorithm identifies the hash algorithm for Content-Digest per RFC 9530.

const (
	// DigestSHA256 uses SHA-256 for content digest.
	DigestSHA256 DigestAlgorithm = "sha-256"

	// DigestSHA512 uses SHA-512 for content digest.
	DigestSHA512 DigestAlgorithm = "sha-512"
)

type KeyResolver

type KeyResolver func(r *http.Request, keyID string, alg Algorithm) (Verifier, error)

KeyResolver returns a Verifier for the given key ID and algorithm. It is called during request verification to look up the appropriate key. The request is provided for context (e.g., to select keys based on the request host or path).

type MiddlewareConfig

type MiddlewareConfig struct {
	// Verify configures how signatures are verified.
	Verify VerifyConfig

	// OnError is called when verification fails. When nil, a plain 401
	// Unauthorized response is sent.
	OnError func(w http.ResponseWriter, r *http.Request, err error)
}

MiddlewareConfig configures the server-side signature verification middleware.

type SignConfig

type SignConfig struct {
	// Signer produces signatures. Required.
	Signer Signer

	// Label identifies the signature in Signature/Signature-Input headers.
	// Defaults to "sig1".
	Label string

	// CoveredComponents lists the component identifiers to include in the
	// signature base. Defaults to [ComponentMethod, ComponentAuthority, ComponentPath].
	CoveredComponents []string

	// Nonce is an optional nonce value included in signature parameters.
	Nonce string

	// Tag is an optional application-specific tag for the signature.
	Tag string

	// Created sets the signature creation time. When zero, time.Now() is
	// used.
	Created time.Time

	// Expires sets the signature expiration time. When zero, no expiration
	// is set.
	Expires time.Time

	// DigestAlgorithm, when set, causes SignRequest to compute and set a
	// Content-Digest header (RFC 9530) before signing. The
	// "content-digest" component is automatically added to covered
	// components if not already present.
	DigestAlgorithm DigestAlgorithm
}

SignConfig configures HTTP request signing per RFC 9421.

type Signer

type Signer interface {
	// Sign produces a signature over the given message bytes.
	Sign(message []byte) ([]byte, error)

	// Algorithm returns the algorithm identifier for this signer.
	Algorithm() Algorithm

	// KeyID returns the key identifier included in signature parameters.
	KeyID() string
}

Signer creates signatures over HTTP message signature base strings.

func NewECDSAP256Signer

func NewECDSAP256Signer(keyID string, key *ecdsa.PrivateKey) (Signer, error)

NewECDSAP256Signer creates a Signer using ECDSA with curve P-256 and SHA-256.

func NewECDSAP384Signer

func NewECDSAP384Signer(keyID string, key *ecdsa.PrivateKey) (Signer, error)

NewECDSAP384Signer creates a Signer using ECDSA with curve P-384 and SHA-384.

func NewEd25519Signer

func NewEd25519Signer(keyID string, key ed25519.PrivateKey) (Signer, error)

NewEd25519Signer creates a Signer using Ed25519.

func NewHMACSHA256Signer

func NewHMACSHA256Signer(keyID string, key []byte) (Signer, error)

NewHMACSHA256Signer creates a Signer using HMAC-SHA256. The key must be at least 32 bytes.

func NewRSAPSSSigner

func NewRSAPSSSigner(keyID string, key *rsa.PrivateKey) (Signer, error)

NewRSAPSSSigner creates a Signer using RSASSA-PSS with SHA-512.

func NewRSAv15Signer

func NewRSAv15Signer(keyID string, key *rsa.PrivateKey) (Signer, error)

NewRSAv15Signer creates a Signer using RSASSA-PKCS1-v1_5 with SHA-256.

type Transport

type Transport struct {
	// contains filtered or unexported fields
}

Transport is an http.RoundTripper that signs outgoing requests using HTTP Message Signatures (RFC 9421).

Use NewTransport to create a Transport with a configured *http.Transport for proxy, TLS, and timeout settings.

func NewTransport

func NewTransport(base *http.Transport, cfg SignConfig) *Transport

NewTransport creates a signing Transport that delegates to base after signing each request. When base is nil, a clone of http.DefaultTransport is used, giving an independent connection pool with default proxy, TLS, and timeout settings.

Configure base for custom proxy (HTTP/SOCKS), TLS, timeouts, and connection pool settings:

base := &http.Transport{
    Proxy:           http.ProxyFromEnvironment,
    TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS13},
    IdleConnTimeout: 90 * time.Second,
}
transport := httpsig.NewTransport(base, httpsig.SignConfig{Signer: signer})

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip signs the request and then delegates to the base transport. The original request is cloned before signing to avoid mutation. When GetBody is available, the clone receives its own body copy so that digest computation does not consume the caller's body.

type Verifier

type Verifier interface {
	// Verify checks that signature is valid for the given message bytes.
	// Returns nil on success, non-nil on failure.
	Verify(message, signature []byte) error

	// Algorithm returns the algorithm identifier for this verifier.
	Algorithm() Algorithm

	// KeyID returns the key identifier for this verifier.
	KeyID() string
}

Verifier validates signatures over HTTP message signature base strings.

func NewECDSAP256Verifier

func NewECDSAP256Verifier(keyID string, key *ecdsa.PublicKey) (Verifier, error)

NewECDSAP256Verifier creates a Verifier using ECDSA with curve P-256 and SHA-256.

func NewECDSAP384Verifier

func NewECDSAP384Verifier(keyID string, key *ecdsa.PublicKey) (Verifier, error)

NewECDSAP384Verifier creates a Verifier using ECDSA with curve P-384 and SHA-384.

func NewEd25519Verifier

func NewEd25519Verifier(keyID string, key ed25519.PublicKey) (Verifier, error)

NewEd25519Verifier creates a Verifier using Ed25519.

func NewHMACSHA256Verifier

func NewHMACSHA256Verifier(keyID string, key []byte) (Verifier, error)

NewHMACSHA256Verifier creates a Verifier using HMAC-SHA256. The key must be at least 32 bytes.

func NewRSAPSSVerifier

func NewRSAPSSVerifier(keyID string, key *rsa.PublicKey) (Verifier, error)

NewRSAPSSVerifier creates a Verifier using RSASSA-PSS with SHA-512.

func NewRSAv15Verifier

func NewRSAv15Verifier(keyID string, key *rsa.PublicKey) (Verifier, error)

NewRSAv15Verifier creates a Verifier using RSASSA-PKCS1-v1_5 with SHA-256.

type VerifyConfig

type VerifyConfig struct {
	// Resolver looks up a Verifier for a given key ID and algorithm.
	// Required.
	Resolver KeyResolver

	// Label identifies which signature to verify. When empty, the first
	// signature found in the Signature-Input header is used.
	Label string

	// RequiredComponents lists component identifiers that must be present
	// in the signature's covered components. Verification fails if any
	// required component is missing.
	RequiredComponents []string

	// MaxAge is the maximum acceptable age of the signature. When non-zero,
	// signatures older than MaxAge are rejected. Requires the "created"
	// parameter in the signature.
	MaxAge time.Duration

	// RequireDigest, when true, requires a Content-Digest header and
	// verifies it against the request body before signature verification.
	RequireDigest bool
}

VerifyConfig configures HTTP request signature verification per RFC 9421.

Jump to

Keyboard shortcuts

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