pomerium/pkg/hpke/http.go

77 lines
2 KiB
Go

package hpke
import (
"context"
"fmt"
"io"
"net/http"
"github.com/gregjones/httpcache"
)
const (
defaultMaxBodySize = 1024 * 1024 * 4
jwkType = "OKP"
jwkID = "pomerium/hpke"
jwkCurve = "X25519"
)
// JWK is the JSON Web Key representation of an HPKE key.
// Defined in RFC8037.
type JWK struct {
Type string `json:"kty"`
ID string `json:"kid"`
Curve string `json:"crv"`
X string `json:"x"`
D string `json:"d,omitempty"`
}
// FetchPublicKey fetches the HPKE public key from the hpke-public-key endpoint.
func FetchPublicKey(ctx context.Context, client *http.Client, endpoint string) (*PublicKey, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("hpke: error building hpke-public-key http request: %w", err)
}
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("hpke: error requesting hpke-public-key endpoint: %w", err)
}
defer res.Body.Close()
if res.StatusCode/100 != 2 {
return nil, fmt.Errorf("hpke: error requesting hpke-public-key endpoint, invalid status code: %d", res.StatusCode)
}
bs, err := io.ReadAll(io.LimitReader(res.Body, defaultMaxBodySize))
if err != nil {
return nil, fmt.Errorf("hpke: error reading hpke-public-key endpoint: %w", err)
}
return PublicKeyFromBytes(bs)
}
// A KeyFetcher fetches public keys.
type KeyFetcher interface {
FetchPublicKey(ctx context.Context) (*PublicKey, error)
}
type fetcher struct {
client *http.Client
endpoint string
}
func (fetcher *fetcher) FetchPublicKey(ctx context.Context) (*PublicKey, error) {
return FetchPublicKey(ctx, fetcher.client, fetcher.endpoint)
}
// NewKeyFetcher returns a new KeyFetcher which fetches keys using an in-memory HTTP cache.
func NewKeyFetcher(endpoint string, transport http.RoundTripper) KeyFetcher {
return &fetcher{
client: (&httpcache.Transport{
Transport: transport,
Cache: httpcache.NewMemoryCache(),
}).Client(),
endpoint: endpoint,
}
}