s2s

package
v0.0.0-...-640e9e9 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 19 Imported by: 1

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// HeadersToSign is the list of headers that will be used to generate the
	// Draft version of HTTP-Signature
	//
	// In regular builds, this list contains the "Date" header which makes it
	// compatible with the wider fediverse, at the expense of debuggability.
	HeadersToSign = []string{httpsig.RequestTarget, "host", "date"}

	// FetchCoveredComponents is the list of components to be used for generating the
	// RFC9421 Signature Base for GET and HEAD requests.
	// https://www.rfc-editor.org/rfc/rfc9421.html#name-derived-components
	FetchCoveredComponents = []string{"@method", "@target-uri", "date"}
	// AdditionalPostCoveredComponents is the list of components to be used for generating the
	// RFC9421 Signature Base for POST, PUT, DELETE requests.
	AdditionalPostCoveredComponents = []string{"content-type"}
)
View Source
var ErrRetry = errors.Newf("retry")

Functions

func NoRFC9421

func NoRFC9421(h *Transport) error

Types

type KeyEncoding

type KeyEncoding int
const (
	KeyTypeUnknown KeyEncoding = 0
	KeyTypePKCS    KeyEncoding = 1
	KeyTypePSS     KeyEncoding = 2
)

type OptionFn

type OptionFn func(transport *Transport) error

func WithActor

func WithActor(act *vocab.Actor, prv crypto.PrivateKey) OptionFn

func WithAlg

func WithAlg(alg KeyEncoding) OptionFn

func WithApplicationTag

func WithApplicationTag(t string) OptionFn

func WithCoveredComponents

func WithCoveredComponents(comp ...string) OptionFn

func WithLogger

func WithLogger(l lw.Logger) OptionFn

func WithNonce

func WithNonce(nonceFn func() (string, error)) OptionFn

func WithTransport

func WithTransport(tr http.RoundTripper) OptionFn

type Transport

type Transport struct {
	Base http.RoundTripper

	Alg   KeyEncoding
	Key   crypto.PrivateKey
	Actor *vocab.Actor
	// contains filtered or unexported fields
}

func New

func New(initFns ...OptionFn) *Transport

New initializes the Transport TODO(marius): we need to add to the return values the errors that might come from the initialization functions.

func (*Transport) RoundTrip

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

RoundTrip dispatches the received request after signing it. We currently use the double knocking mechanism Mastodon popularized: * we first attempt to sign the request with RFC9421 compliant signature, * if it failed, we try again using a Cavage draft 8 version signature. Additionally, if everything failed, and we're operating with a fetch request, we make one last, non-signed attempt.

Example (Draft)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("%s\n", r.Header.Get("Signature"))
	w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

tr := New(WithTransport(http.DefaultTransport), WithActor(jdoeActor, prv), NoRFC9421)

// The below functionality would be equivalent to the following usage:
//http.DefaultClient.Transport = tr
//res, err := http.Get(srv.URL)

req := httptest.NewRequest(http.MethodGet, srv.URL, nil)
host := strings.TrimPrefix(srv.URL, "http://")
host = host[:strings.Index(host, ":")]
req.Header.Set("Host", host)
req.Header.Set("Date", millenium.Format(http.TimeFormat))
_, _ = tr.RoundTrip(req)
Output:
keyId="https://example.com/~johndoe#main",algorithm="hs2019",headers="(request-target) host",signature="RhsET77hrToaCyh/2++dFw0PGn64AoZBR3X2r+rVFWDT1CtobC1sVwXcc91v2c2HmB3A6P3EH1truRnhbNpL2sOgmqUbkRGBoO5afsgaRzRg/z8BwKDlnP9w/6zYlvoYH2VcgQpCTKPUkYDGUexFQDxBJMFime+d361I3ptO/Jc="
Example (Rfc9421)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	verifier, _ := httpsig.NewVerifier(
		mockKeyResolver{},
		httpsig.WithNonceChecker(mockNonceChecker(true)),
		httpsig.WithValidateAllSignatures(),
		httpsig.WithValidityTolerance(time.Hour),
		httpsig.WithMaxAge(time.Hour),
	)

	err := verifier.Verify(httpsig.MessageFromRequest(r))
	if err != nil {
		fmt.Printf("Verification failed: %s\n", err)
	} else {
		fmt.Printf("Verification succeeded\n")
	}
	w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

tr := New(WithTransport(http.DefaultTransport), WithActor(&exActorRSA, prvKeyRSA), WithAlg(KeyTypePKCS), WithNonce(sameNonce))
req := httptest.NewRequest(http.MethodPost, srv.URL, strings.NewReader(`{"hello": "world"}`))
req.Header.Set("Host", "example.com")
req.Header.Set("Date", millenium.Format(http.TimeFormat))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Length", "18")

_, _ = tr.RoundTrip(req)
Output:
Verification succeeded

Jump to

Keyboard shortcuts

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