mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-13 22:40:24 +00:00
fixup: client: conditional content digest
Only calculates the `Content-Digest` header when a request body is present. Should address issues discovered during e2e testing.
This commit is contained in:
parent
d32bc37556
commit
8eaf11c8dc
5 changed files with 75 additions and 40 deletions
|
|
@ -619,7 +619,7 @@ func (c *Client) KeyID() string {
|
|||
|
||||
// Create an http POST request with forgejo/gitea specific headers
|
||||
//
|
||||
//nolint:dupl
|
||||
|
||||
func (c *Client) PostRequest(b []byte, to string) (req *http.Request, err error) {
|
||||
if req, err = c.newRequest(http.MethodPost, b, to); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -628,7 +628,13 @@ func (c *Client) PostRequest(b []byte, to string) (req *http.Request, err error)
|
|||
if c.pubID != "" {
|
||||
if c.useRFC9421 {
|
||||
config := httpsign9421.NewSignConfig().SignCreated(true)
|
||||
fields := httpsign9421.Headers(setting.Federation.PostHeadersRFC9421...)
|
||||
|
||||
hasBody := len(b) > 0
|
||||
sigHeaders := setting.Federation.PostHeadersRFC9421
|
||||
if hasBody {
|
||||
sigHeaders = append(sigHeaders, "Content-Digest")
|
||||
}
|
||||
fields := httpsign9421.Headers(sigHeaders...)
|
||||
|
||||
signers, err := c.SignersRFC9421(config, fields)
|
||||
if err != nil {
|
||||
|
|
@ -636,11 +642,13 @@ func (c *Client) PostRequest(b []byte, to string) (req *http.Request, err error)
|
|||
}
|
||||
|
||||
req.Header.Set("Created", fmt.Sprintf("%d", time.Now().Unix()))
|
||||
digest, err := ContentDigest(&req.Body, setting.Federation.DigestAlgorithms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if hasBody {
|
||||
digest, err := ContentDigest(&req.Body, setting.Federation.DigestAlgorithms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Digest", digest)
|
||||
}
|
||||
req.Header.Set("Content-Digest", digest)
|
||||
|
||||
for i, signer := range signers {
|
||||
sigName := fmt.Sprintf("sig%d", i+1)
|
||||
|
|
@ -677,7 +685,7 @@ func (c *Client) Get(to string) (resp *http.Response, err error) {
|
|||
|
||||
// Create an http GET request with forgejo/gitea specific headers
|
||||
//
|
||||
//nolint:dupl
|
||||
|
||||
func (c *Client) GetRequest(to string) (req *http.Request, err error) {
|
||||
if req, err = c.newRequest(http.MethodGet, nil, to); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -694,11 +702,6 @@ func (c *Client) GetRequest(to string) (req *http.Request, err error) {
|
|||
}
|
||||
|
||||
req.Header.Set("Created", fmt.Sprintf("%d", time.Now().Unix()))
|
||||
digest, err := ContentDigest(&req.Body, setting.Federation.DigestAlgorithms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Digest", digest)
|
||||
|
||||
for i, signer := range signers {
|
||||
sigName := fmt.Sprintf("sig%d", i+1)
|
||||
|
|
@ -760,19 +763,27 @@ func (c *Client) GetRFC9421() bool {
|
|||
return c.useRFC9421
|
||||
}
|
||||
|
||||
func (c *Client) SignedHeaders(method string) string {
|
||||
func (c *Client) SignedHeaders(method string, hasBody bool) string {
|
||||
var ret string
|
||||
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
if c.GetRFC9421() {
|
||||
ret = fmt.Sprintf(`"%v"`, strings.Join(setting.Federation.GetHeadersRFC9421, `" "`))
|
||||
headers := setting.Federation.GetHeadersRFC9421
|
||||
if hasBody {
|
||||
headers = append(headers, "Content-Digest")
|
||||
}
|
||||
ret = fmt.Sprintf(`"%v"`, strings.Join(headers, `" "`))
|
||||
} else {
|
||||
ret = strings.Join(setting.Federation.GetHeaders, " ")
|
||||
}
|
||||
case http.MethodPost:
|
||||
if c.GetRFC9421() {
|
||||
ret = fmt.Sprintf(`"%v"`, strings.Join(setting.Federation.PostHeadersRFC9421, `" "`))
|
||||
headers := setting.Federation.PostHeadersRFC9421
|
||||
if hasBody {
|
||||
headers = append(headers, "Content-Digest")
|
||||
}
|
||||
ret = fmt.Sprintf(`"%v"`, strings.Join(headers, `" "`))
|
||||
} else {
|
||||
ret = strings.Join(setting.Federation.PostHeaders, " ")
|
||||
}
|
||||
|
|
@ -806,7 +817,7 @@ type APClient interface {
|
|||
GetRFC9421() bool
|
||||
SetRFC9421(use bool)
|
||||
KeyID() string
|
||||
SignedHeaders(method string) string
|
||||
SignedHeaders(method string, hasBody bool) string
|
||||
}
|
||||
|
||||
// contextKey is a value for use with context.WithValue.
|
||||
|
|
|
|||
|
|
@ -329,16 +329,21 @@ func TestActivityPubSignedPostRFC9421(t *testing.T) {
|
|||
|
||||
expected := "BODY"
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Regexp(t, "^"+strings.ToLower(setting.Federation.DigestAlgorithm), r.Header.Get("Content-Digest"))
|
||||
hasBody := r.Header.Get("Content-Digest") != ""
|
||||
if hasBody {
|
||||
assert.Regexp(t, "^"+strings.ToLower(setting.Federation.DigestAlgorithm), r.Header.Get("Content-Digest"))
|
||||
}
|
||||
for _, input := range setting.Federation.PostHeadersRFC9421 {
|
||||
assert.Contains(t, r.Header.Get("Signature-Input"), strings.ToLower(input))
|
||||
}
|
||||
assert.Equal(t, activitypub.ActivityStreamsContentType, r.Header.Get("Content-Type"))
|
||||
require.NoError(t, activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, digestAlgs))
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
fmt.Fprint(w, expected)
|
||||
if hasBody {
|
||||
assert.Equal(t, activitypub.ActivityStreamsContentType, r.Header.Get("Content-Type"))
|
||||
require.NoError(t, activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, digestAlgs))
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
fmt.Fprint(w, expected)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
|
|
@ -363,16 +368,21 @@ func TestActivityPubSignedGetRFC9421(t *testing.T) {
|
|||
|
||||
expected := ""
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Regexp(t, "^"+strings.ToLower(setting.Federation.DigestAlgorithm), r.Header.Get("Content-Digest"))
|
||||
hasBody := r.Header.Get("Content-Digest") != ""
|
||||
if hasBody {
|
||||
assert.Regexp(t, "^"+strings.ToLower(setting.Federation.DigestAlgorithm), r.Header.Get("Content-Digest"))
|
||||
}
|
||||
for _, input := range setting.Federation.PostHeadersRFC9421 {
|
||||
assert.Contains(t, r.Header.Get("Signature-Input"), strings.ToLower(input))
|
||||
}
|
||||
assert.Equal(t, activitypub.ActivityStreamsContentType, r.Header.Get("Content-Type"))
|
||||
require.NoError(t, activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, digestAlgs))
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
fmt.Fprint(w, expected)
|
||||
if hasBody {
|
||||
assert.Equal(t, activitypub.ActivityStreamsContentType, r.Header.Get("Content-Type"))
|
||||
require.NoError(t, activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, digestAlgs))
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
fmt.Fprint(w, expected)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ var (
|
|||
DigestAlgorithm: "SHA-256",
|
||||
DigestAlgorithms: []string{"sha-256", "sha-512"},
|
||||
GetHeaders: []string{"(request-target)", "Date", "Host"},
|
||||
GetHeadersRFC9421: []string{"@method", "@target-uri", "Content-Digest", "Created"},
|
||||
GetHeadersRFC9421: []string{"@method", "@target-uri", "Created"},
|
||||
PostHeaders: []string{"(request-target)", "Date", "Host", "Digest"},
|
||||
PostHeadersRFC9421: []string{"@method", "@target-uri", "Content-Digest", "Created"},
|
||||
PostHeadersRFC9421: []string{"@method", "@target-uri", "Created"},
|
||||
SignatureEnforced: true,
|
||||
UseRFC9421: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,15 +57,27 @@ func verifyHTTPMessageSignature(ctx app_context.APIContext) (authenticated bool,
|
|||
if len(sigHeaders) == 0 {
|
||||
return false, fmt.Errorf("missing signature header")
|
||||
}
|
||||
|
||||
hasBody := r.Header.Get("Content-Digest") != ""
|
||||
|
||||
var fields httpsign9421.Fields
|
||||
var headers []string
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
fields = httpsign9421.Headers(setting.Federation.GetHeadersRFC9421...)
|
||||
headers = setting.Federation.GetHeadersRFC9421
|
||||
if hasBody {
|
||||
headers = append(headers, "Content-Digest")
|
||||
}
|
||||
case http.MethodPost:
|
||||
fields = httpsign9421.Headers(setting.Federation.PostHeadersRFC9421...)
|
||||
headers = setting.Federation.PostHeadersRFC9421
|
||||
if hasBody {
|
||||
headers = append(headers, "Content-Digest")
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported request type: %v", r.Method)
|
||||
}
|
||||
|
||||
fields = httpsign9421.Headers(headers...)
|
||||
sigNames, err := httpsign9421.RequestSignatureNames(r, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
@ -78,8 +90,10 @@ func verifyHTTPMessageSignature(ctx app_context.APIContext) (authenticated bool,
|
|||
config := httpsign9421.NewVerifyConfig()
|
||||
config.SetAllowedAlgs(setting.Federation.SignatureAlgorithmsRFC9421)
|
||||
|
||||
if err = activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, setting.Federation.DigestAlgorithms); err != nil {
|
||||
return false, fmt.Errorf("invalid HTTP Content-Digest header: %v", err)
|
||||
if hasBody {
|
||||
if err = activitypub.ValidateContentDigest(r.Header.Get("Content-Digest"), &r.Body, setting.Federation.DigestAlgorithms); err != nil {
|
||||
return false, fmt.Errorf("invalid HTTP Content-Digest header: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range sigNames {
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func TestFederationHttpSigValidation(t *testing.T) {
|
|||
expKeyID := fmt.Sprintf(`keyId="%v"`, apClient.KeyID())
|
||||
assert.Contains(t, sig, expKeyID)
|
||||
|
||||
expHeaders := fmt.Sprintf(`headers="%v"`, apClient.SignedHeaders(http.MethodGet))
|
||||
expHeaders := fmt.Sprintf(`headers="%v"`, apClient.SignedHeaders(http.MethodGet, false))
|
||||
assert.Contains(t, sig, expHeaders)
|
||||
|
||||
assert.Contains(t, sig, "signature=")
|
||||
|
|
@ -177,7 +177,7 @@ func TestFederationHttpSigValidation(t *testing.T) {
|
|||
expKeyID := fmt.Sprintf(`keyid="%v"`, apClient.KeyID())
|
||||
assert.Contains(t, sigInput, expKeyID)
|
||||
|
||||
expHeaders := fmt.Sprintf(`sig1=(%v)`, apClient.SignedHeaders(http.MethodGet))
|
||||
expHeaders := fmt.Sprintf(`sig1=(%v)`, apClient.SignedHeaders(http.MethodGet, false))
|
||||
assert.Contains(t, sigInput, expHeaders)
|
||||
|
||||
resp, err := apClient.Do(req)
|
||||
|
|
@ -265,7 +265,7 @@ func TestFederationHttpSigValidation(t *testing.T) {
|
|||
expKeyID := fmt.Sprintf(`keyId="%v"`, followClient.KeyID())
|
||||
assert.Contains(t, sig, expKeyID)
|
||||
|
||||
expHeaders := fmt.Sprintf(`headers="%v"`, followClient.SignedHeaders(http.MethodPost))
|
||||
expHeaders := fmt.Sprintf(`headers="%v"`, followClient.SignedHeaders(http.MethodPost, true))
|
||||
assert.Contains(t, sig, expHeaders)
|
||||
|
||||
assert.Contains(t, sig, "signature=")
|
||||
|
|
@ -362,7 +362,7 @@ func TestFederationHttpSigValidation(t *testing.T) {
|
|||
expKeyID := fmt.Sprintf(`keyid="%v"`, followClient.KeyID())
|
||||
assert.Contains(t, sigInput, expKeyID)
|
||||
|
||||
expHeaders := fmt.Sprintf(`sig1=(%v)`, followClient.SignedHeaders(http.MethodPost))
|
||||
expHeaders := fmt.Sprintf(`sig1=(%v)`, followClient.SignedHeaders(http.MethodPost, true))
|
||||
assert.Contains(t, sigInput, expHeaders)
|
||||
|
||||
resp, err := followClient.Do(req)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue