diff --git a/fuzz_test.go b/fuzz_test.go index 708ba51..a34e4f2 100644 --- a/fuzz_test.go +++ b/fuzz_test.go @@ -2,8 +2,11 @@ package httpsign import ( "encoding/base64" - "github.com/stretchr/testify/assert" + "net/http" + "net/url" "testing" + + "github.com/stretchr/testify/assert" ) var httpreq1pssNoSig = `POST /foo?param=Value&Pet=dog HTTP/1.1 @@ -83,3 +86,96 @@ func FuzzSignAndVerifyHMAC(f *testing.F) { } }) } + +func FuzzVerify(f *testing.F) { + f.Add("GET", "https://example.com/path", "example.com", "https", 0, "", "", "", "", true, false) + f.Add("POST", "https://api.example.com", "api.example.com", "https", 0, "", "", "", "", false, true) + f.Add("", "", "", "", 200, "GET", "https://example.com", "example.com", "https", true, false) + f.Add("PUT", "", "", "http", 0, "", "", "", "", false, false) + f.Add("", "", "", "", 404, "", "", "", "", false, false) + f.Add("0", "%", "0", "0", 0, "", "", "", "", true, false) + + f.Fuzz(func(t *testing.T, method, urlStr, authority, scheme string, statusCode int, + assocMethod, assocURLStr, assocAuthority, assocScheme string, + hasHeaders, hasTrailers bool) { + + config := NewMessageConfig() + + if method != "" { + config = config.WithMethod(method) + } + if urlStr != "" { + u, err := url.Parse(urlStr) + if err == nil { + config = config.WithURL(u) + } + } + if authority != "" { + config = config.WithAuthority(authority) + } + if scheme != "" { + config = config.WithScheme(scheme) + } + + if statusCode > 0 { + config = config.WithStatusCode(statusCode) + } + + if hasHeaders { + headers := http.Header{ + "Content-Type": []string{"application/json"}, + "X-Test": []string{"fuzz"}, + } + config = config.WithHeaders(headers) + } + if hasTrailers { + trailers := http.Header{ + "X-Trailer": []string{"test"}, + } + config = config.WithTrailers(trailers) + } + + if statusCode > 0 && assocMethod != "" { + var assocURL *url.URL + if assocURLStr != "" { + assocURL, _ = url.Parse(assocURLStr) + } + assocHeaders := http.Header{"X-Assoc": []string{"test"}} + config = config.WithAssociatedRequest(assocMethod, assocURL, assocHeaders, assocAuthority, assocScheme) + } + + msg, err := NewMessage(config) + + if err == nil { + if msg.headers == nil && msg.method != "" { + t.Errorf("Request message created without headers") + } + if msg.headers == nil && msg.statusCode != nil { + t.Errorf("Response message created without headers") + } + + key, _ := base64.StdEncoding.DecodeString("uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ==") + verifier, _ := NewHMACSHA256Verifier(key, NewVerifyConfig().SetVerifyCreated(false), Fields{}) + + if msg.headers != nil { + msg.headers.Set("Signature-Input", `sig1=("@method");created=1618884473;keyid="test-key"`) + msg.headers.Set("Signature", `sig1=:test:`) + } + + _, _ = msg.Verify("sig1", *verifier) + } + + if err != nil { + hasRequest := method != "" + hasResponse := statusCode > 0 + + if !hasRequest && !hasResponse { + assert.Contains(t, err.Error(), "must have either method") + } else if hasRequest && hasResponse { + assert.Contains(t, err.Error(), "cannot have both request and response") + } else if (hasRequest || hasResponse) && !hasHeaders { + assert.Contains(t, err.Error(), "must have headers") + } + } + }) +} diff --git a/http2_test.go b/http2_test.go index 8984c0f..debf79a 100644 --- a/http2_test.go +++ b/http2_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "crypto/tls" - "github.com/andreyvit/diff" "io" "net/http" "net/http/httptest" @@ -12,6 +11,8 @@ import ( "strings" "testing" "text/template" + + "github.com/andreyvit/diff" ) var wantFields = `"kuku": my awesome header @@ -81,7 +82,11 @@ func testHTTP(t *testing.T, proto string) { if err != nil { t.Errorf("could not create verifier") } - sigInput, err := verifyRequestDebug("sig1", *verifier, r) + message, err := NewMessage(NewMessageConfig().WithRequest(r)) + if err != nil { + t.Errorf("failed to create message: %v", err) + } + sigInput, _, err := verifyDebug("sig1", *verifier, message) if err != nil { t.Errorf("failed to verify request: sig input: %s\nerr: %v", sigInput, err) } diff --git a/httpparse.go b/httpparse.go index c7e1232..36ff12c 100644 --- a/httpparse.go +++ b/httpparse.go @@ -22,40 +22,23 @@ func parseRequest(req *http.Request, withTrailers bool) (*parsedMessage, error) if req == nil { return nil, nil } - err := validateMessageHeaders(req.Header) - if err != nil { - return nil, err - } - if withTrailers { - _, err = duplicateBody(&req.Body) // read the entire body to populate the trailers - if err != nil { - return nil, fmt.Errorf("cannot duplicate request body: %w", err) - } - err = validateMessageHeaders(req.Trailer) - if err != nil { - return nil, fmt.Errorf("could not validate trailers: %w", err) - } + + scheme := "http" + if req.TLS != nil { + scheme = "https" } - // Query params are only obtained from the URL (i.e. not from the message body, when using application/x-www-form-urlencoded) - // So we are not vulnerable to the issue described in Sec. "Ambiguous Handling of Query Elements" of the draft. - values, err := url.ParseQuery(req.URL.RawQuery) - if err != nil { - return nil, fmt.Errorf("cannot parse query: %s", req.URL.RawQuery) - } - escaped := reEncodeQPs(values) - u := req.URL - if u.Host == "" { - u.Host = req.Host - } - if u.Scheme == "" { - if req.TLS == nil { - u.Scheme = "http" - } else { - u.Scheme = "https" - } + + msg := &Message{ + method: req.Method, + url: req.URL, + headers: req.Header, + trailers: req.Trailer, + body: &req.Body, + authority: req.Host, + scheme: scheme, } - return &parsedMessage{derived: generateReqDerivedComponents(req), url: u, headers: normalizeHeaderNames(req.Header), - trailers: normalizeHeaderNames(req.Trailer), qParams: escaped}, nil + + return parseMessage(msg, withTrailers) } func reEncodeQPs(values url.Values) url.Values { @@ -82,23 +65,14 @@ func normalizeHeaderNames(header http.Header) http.Header { } func parseResponse(res *http.Response, withTrailers bool) (*parsedMessage, error) { - err := validateMessageHeaders(res.Header) - if err != nil { - return nil, err - } - if withTrailers { - _, err = duplicateBody(&res.Body) // read the entire body to populate the trailers - if err != nil { - return nil, fmt.Errorf("cannot duplicate request body: %w", err) - } - err = validateMessageHeaders(res.Trailer) - if err != nil { - return nil, fmt.Errorf("could not validate trailers: %w", err) - } + msg := &Message{ + statusCode: &res.StatusCode, + headers: res.Header, + trailers: res.Trailer, + body: &res.Body, } - return &parsedMessage{derived: generateResDerivedComponents(res), url: nil, - headers: normalizeHeaderNames(res.Header)}, nil + return parseMessage(msg, withTrailers) } func validateMessageHeaders(header http.Header) error { @@ -112,6 +86,9 @@ func validateMessageHeaders(header http.Header) error { } func foldFields(fields []string) string { + if len(fields) == 0 { + return "" + } ff := strings.TrimSpace(fields[0]) for i := 1; i < len(fields); i++ { ff += ", " + strings.TrimSpace(fields[i]) @@ -123,17 +100,14 @@ func derivedComponent(name, v string, components components) { components[name] = v } -func generateReqDerivedComponents(req *http.Request) components { - components := components{} - derivedComponent("@method", scMethod(req), components) - theURL := req.URL - derivedComponent("@target-uri", scTargetURI(theURL), components) - derivedComponent("@path", scPath(theURL), components) - derivedComponent("@authority", scAuthority(req), components) - derivedComponent("@scheme", scScheme(theURL), components) - derivedComponent("@request-target", scRequestTarget(theURL), components) - derivedComponent("@query", scQuery(theURL), components) - return components +func generateReqDerivedComponents(method string, u *url.URL, authority string, components components) { + derivedComponent("@method", method, components) + derivedComponent("@target-uri", scTargetURI(u), components) + derivedComponent("@path", scPath(u), components) + derivedComponent("@authority", authority, components) + derivedComponent("@scheme", scScheme(u), components) + derivedComponent("@request-target", scRequestTarget(u), components) + derivedComponent("@query", scQuery(u), components) } func scPath(theURL *url.URL) string { @@ -162,24 +136,81 @@ func scScheme(url *url.URL) string { return url.Scheme } -func scAuthority(req *http.Request) string { - return req.Host -} - func scTargetURI(url *url.URL) string { return url.String() } -func scMethod(req *http.Request) string { - return req.Method +func scStatus(statusCode int) string { + return strconv.Itoa(statusCode) } -func generateResDerivedComponents(res *http.Response) components { - components := components{} - derivedComponent("@status", scStatus(res), components) - return components -} +func parseMessage(msg *Message, withTrailers bool) (*parsedMessage, error) { + if msg == nil { + return nil, nil + } + + err := validateMessageHeaders(msg.headers) + if err != nil { + return nil, err + } + + if withTrailers { + if msg.body != nil { + _, err = duplicateBody(msg.body) + if err != nil { + return nil, fmt.Errorf("cannot duplicate message body: %w", err) + } + } + err = validateMessageHeaders(msg.trailers) + if err != nil { + return nil, fmt.Errorf("could not validate trailers: %w", err) + } + } + + derived := components{} + var u *url.URL + var qParams url.Values + + if msg.method != "" || msg.url != nil { + if msg.method == "" || msg.url == nil { + return nil, fmt.Errorf("invalid state: method or url without the other") + } + + u = msg.url + if u == nil { + u = &url.URL{Path: "/"} + } + if u.Host == "" && msg.authority != "" { + u.Host = msg.authority + } + if u.Scheme == "" { + if msg.scheme != "" { + u.Scheme = msg.scheme + } else { + u.Scheme = "http" + } + } + + if u.RawQuery != "" { + values, err := url.ParseQuery(u.RawQuery) + if err != nil { + return nil, fmt.Errorf("cannot parse query: %s", u.RawQuery) + } + qParams = reEncodeQPs(values) + } + + generateReqDerivedComponents(msg.method, u, msg.authority, derived) + } else if msg.statusCode != nil { + derivedComponent("@status", scStatus(*msg.statusCode), derived) + } else { + return nil, fmt.Errorf("invalid state: method and url, or status required") + } -func scStatus(res *http.Response) string { - return strconv.Itoa(res.StatusCode) + return &parsedMessage{ + derived: derived, + url: u, + headers: normalizeHeaderNames(msg.headers), + trailers: normalizeHeaderNames(msg.trailers), + qParams: qParams, + }, nil } diff --git a/message.go b/message.go new file mode 100644 index 0000000..7d8b4ab --- /dev/null +++ b/message.go @@ -0,0 +1,248 @@ +package httpsign + +import ( + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +// MessageDetails aggregates the details of a signed message, for a given signature +type MessageDetails struct { + KeyID string + Alg string + Fields Fields + Created *time.Time + Expires *time.Time + Nonce *string + Tag *string +} + +// Message represents a parsed HTTP message ready for signature verification. +type Message struct { + headers http.Header + trailers http.Header + body *io.ReadCloser + + method string + url *url.URL + authority string + scheme string + statusCode *int + assocReq *Message +} + +// NewMessage constructs a new Message from the provided config. +func NewMessage(config *MessageConfig) (*Message, error) { + if config == nil { + config = NewMessageConfig() + } + + hasRequest := config.method != "" + hasResponse := config.statusCode != nil + + if !hasRequest && !hasResponse { + return nil, fmt.Errorf("message config must have either method (for request) or status code (for response)") + } + + if hasRequest && hasResponse { + return nil, fmt.Errorf("message config cannot have both request and response fields set") + } + + if hasRequest { + if config.headers == nil { + return nil, fmt.Errorf("request message must have headers") + } + } + + if hasResponse { + if config.headers == nil { + return nil, fmt.Errorf("response message must have headers") + } + } + + var assocReq *Message + if config.assocReq != nil { + method := config.assocReq.method + u := config.assocReq.url + headers := config.assocReq.headers + authority := config.assocReq.authority + scheme := config.assocReq.scheme + if method == "" || u == nil || headers == nil || authority == "" || scheme == "" { + return nil, fmt.Errorf("invalid associated request") + } + assocReq = &Message{ + method: method, + url: u, + headers: headers, + authority: authority, + scheme: scheme, + } + } + + return &Message{ + headers: config.headers, + trailers: config.trailers, + body: config.body, + method: config.method, + url: config.url, + authority: config.authority, + scheme: config.scheme, + statusCode: config.statusCode, + assocReq: assocReq, + }, nil +} + +// MessageConfig configures a Message for signature verification. +type MessageConfig struct { + method string + url *url.URL + headers http.Header + trailers http.Header + body *io.ReadCloser + authority string + scheme string + + statusCode *int + + assocReq *MessageConfig +} + +// NewMessageConfig returns a new MessageConfig. +func NewMessageConfig() *MessageConfig { + return &MessageConfig{} +} + +func (b *MessageConfig) WithMethod(method string) *MessageConfig { + b.method = method + return b +} + +func (b *MessageConfig) WithURL(u *url.URL) *MessageConfig { + b.url = u + return b +} + +func (b *MessageConfig) WithHeaders(headers http.Header) *MessageConfig { + b.headers = headers + return b +} + +func (b *MessageConfig) WithTrailers(trailers http.Header) *MessageConfig { + b.trailers = trailers + return b +} + +func (b *MessageConfig) WithBody(body *io.ReadCloser) *MessageConfig { + b.body = body + return b +} + +func (b *MessageConfig) WithAuthority(authority string) *MessageConfig { + b.authority = authority + return b +} + +func (b *MessageConfig) WithScheme(scheme string) *MessageConfig { + b.scheme = scheme + return b +} + +func (b *MessageConfig) WithStatusCode(statusCode int) *MessageConfig { + b.statusCode = &statusCode + return b +} + +func (b *MessageConfig) WithAssociatedRequest(method string, u *url.URL, headers http.Header, authority, scheme string) *MessageConfig { + b.assocReq = &MessageConfig{ + method: method, + url: u, + headers: headers, + authority: authority, + scheme: scheme, + } + return b +} + +func (b *MessageConfig) WithRequest(req *http.Request) *MessageConfig { + if req == nil { + return b + } + + scheme := "http" + if req.TLS != nil { + scheme = "https" + } + + return b. + WithMethod(req.Method). + WithURL(req.URL). + WithHeaders(req.Header). + WithTrailers(req.Trailer). + WithBody(&req.Body). + WithAuthority(req.Host). + WithScheme(scheme) +} + +func (b *MessageConfig) WithResponse(res *http.Response, req *http.Request) *MessageConfig { + if res == nil { + return b + } + + b = b. + WithStatusCode(res.StatusCode). + WithHeaders(res.Header). + WithTrailers(res.Trailer). + WithBody(&res.Body) + + if req != nil { + scheme := "http" + if req.TLS != nil { + scheme = "https" + } + b = b.WithAssociatedRequest(req.Method, req.URL, req.Header, req.Host, scheme) + } + + return b +} + +// Verify verifies a signature on this message. +func (m *Message) Verify(signatureName string, verifier Verifier) (*MessageDetails, error) { + _, psiSig, err := verifyDebug(signatureName, verifier, m) + if err != nil { + return nil, err + } + + details := &MessageDetails{ + Fields: psiSig.fields, + } + + if keyID, ok := psiSig.params["keyid"].(string); ok { + details.KeyID = keyID + } + + if alg, ok := psiSig.params["alg"].(string); ok { + details.Alg = alg + } + + if created, ok := psiSig.params["created"].(int64); ok { + t := time.Unix(created, 0) + details.Created = &t + } + + if expires, ok := psiSig.params["expires"].(int64); ok { + t := time.Unix(expires, 0) + details.Expires = &t + } + + if nonce, ok := psiSig.params["nonce"].(string); ok { + details.Nonce = &nonce + } + + if tag, ok := psiSig.params["tag"].(string); ok { + details.Tag = &tag + } + + return details, nil +} diff --git a/message_test.go b/message_test.go new file mode 100644 index 0000000..b586179 --- /dev/null +++ b/message_test.go @@ -0,0 +1,48 @@ +package httpsign_test + +import ( + "bufio" + "bytes" + "fmt" + "net/http" + "strings" + + "github.com/yaronf/httpsign" +) + +func ExampleMessage_Verify() { + config := httpsign.NewVerifyConfig().SetKeyID("my-shared-secret").SetVerifyCreated(false) // for testing only + verifier, _ := httpsign.NewHMACSHA256Verifier(bytes.Repeat([]byte{0x77}, 64), config, + httpsign.Headers("@authority", "Date", "@method")) + reqStr := `GET /foo HTTP/1.1 +Host: example.org +Date: Tue, 20 Apr 2021 02:07:55 GMT +Cache-Control: max-age=60 +Signature-Input: sig77=("@authority" "date" "@method");alg="hmac-sha256";keyid="my-shared-secret" +Signature: sig77=:3e9KqLP62NHfHY5OMG4036+U6tvBowZF35ALzTjpsf0=: + +` + req, _ := http.ReadRequest(bufio.NewReader(strings.NewReader(reqStr))) + + // Using WithRequest + msgWithRequest, _ := httpsign.NewMessage(httpsign.NewMessageConfig().WithRequest(req)) + _, err1 := msgWithRequest.Verify("sig77", *verifier) + + // Using constituent parts + msgWithConstituents, _ := httpsign.NewMessage(httpsign.NewMessageConfig(). + WithMethod(req.Method). + WithURL(req.URL). + WithHeaders(req.Header). + WithTrailers(req.Trailer). + WithBody(&req.Body). + WithAuthority(req.Host). + WithScheme(req.URL.Scheme)) + + _, err2 := msgWithConstituents.Verify("sig77", *verifier) + + fmt.Printf("WithRequest: %t\n", err1 == nil) + fmt.Printf("Constituents: %t", err2 == nil) + // Output: + // WithRequest: true + // Constituents: true +} diff --git a/signatures.go b/signatures.go index 4f96d08..57a848f 100644 --- a/signatures.go +++ b/signatures.go @@ -3,11 +3,12 @@ package httpsign import ( "errors" "fmt" - "github.com/dunglas/httpsfv" "io" "net/http" "strings" "time" + + "github.com/dunglas/httpsfv" ) func signMessage(config SignConfig, signatureName string, signer Signer, parsedMessage, parsedAssocMessage *parsedMessage, @@ -353,33 +354,12 @@ func signResponseDebug(signatureName string, signer Signer, res *http.Response, // VerifyRequest verifies a signed HTTP request. Returns an error if verification failed for any reason, otherwise nil. func VerifyRequest(signatureName string, verifier Verifier, req *http.Request) error { - _, err := verifyRequestDebug(signatureName, verifier, req) - return err -} - -func verifyRequestDebug(signatureName string, verifier Verifier, req *http.Request) (signatureBase string, err error) { - if req == nil { - return "", fmt.Errorf("nil request") - } - if signatureName == "" { - return "", fmt.Errorf("empty signature name") - } - withTrailers, wantSigRaw, psiSig, err := extractSignatureFields(signatureName, &verifier, req.Header, req.Trailer, &req.Body) - if err != nil { - return "", err - } - parsedMessage, err := parseRequest(req, withTrailers) + msg, err := NewMessage(NewMessageConfig().WithRequest(req)) if err != nil { - return "", err + return err } - return verifyMessage(*verifier.config, verifier, parsedMessage, nil, verifier.fields, - wantSigRaw, psiSig) -} - -// MessageDetails aggregates the details of a signed message, for a given signature -type MessageDetails struct { - KeyID, Alg string - Fields Fields + _, err = msg.Verify(signatureName, verifier) + return err } // RequestDetails parses a signed request and returns the key ID and optionally the algorithm used in the given signature. @@ -397,6 +377,46 @@ func RequestDetails(signatureName string, req *http.Request) (details *MessageDe return signatureDetails(psiSig) } +func verifyDebug(signatureName string, verifier Verifier, message *Message) (string, *psiSignature, error) { + if message == nil { + return "", nil, fmt.Errorf("nil message") + } + if signatureName == "" { + return "", nil, fmt.Errorf("empty signature name") + } + + withTrailers, wantSigRaw, psiSig, err := extractSignatureFields( + signatureName, &verifier, message.headers, message.trailers, message.body) + if err != nil { + return "", nil, err + } + + var parsedMsg *parsedMessage + var parsedAssoc *parsedMessage + + // Parse the main message + parsedMsg, err = parseMessage(message, withTrailers) + if err != nil { + return "", nil, err + } + + // If there's an associated request, parse that too + if assocMsg := message.assocReq; assocMsg != nil { + parsedAssoc, err = parseMessage(assocMsg, false) + if err != nil { + return "", nil, err + } + } + + signatureBase, err := verifyMessage(*verifier.config, verifier, parsedMsg, parsedAssoc, + verifier.fields, wantSigRaw, psiSig) + if err != nil { + return "", nil, err + } + + return signatureBase, psiSig, nil +} + // ResponseDetails parses a signed response and returns the key ID and optionally the algorithm used in the given signature. func ResponseDetails(signatureName string, res *http.Response) (details *MessageDetails, err error) { if res == nil { @@ -475,44 +495,38 @@ func signatureDetails(signature *psiSignature) (details *MessageDetails, err err return nil, fmt.Errorf("malformed \"alg\" parameter") } } - return &MessageDetails{ + details = &MessageDetails{ KeyID: keyID, Alg: alg, Fields: signature.fields, - }, nil -} - -// VerifyResponse verifies a signed HTTP response. Returns an error if verification failed for any reason, otherwise nil. -func VerifyResponse(signatureName string, verifier Verifier, res *http.Response, req *http.Request) error { - _, err := verifyResponseDebug(signatureName, verifier, res, req) - return err -} + } -func verifyResponseDebug(signatureName string, verifier Verifier, res *http.Response, req *http.Request) (signatureBase string, err error) { - if res == nil { - return "", fmt.Errorf("nil response") + if created, ok := signature.params["created"].(int64); ok { + t := time.Unix(created, 0) + details.Created = &t } - if signatureName == "" { - return "", fmt.Errorf("empty signature name") + if expires, ok := signature.params["expires"].(int64); ok { + t := time.Unix(expires, 0) + details.Expires = &t } - resWithTrailers, wantSigRaw, psiSig, err := extractSignatureFields(signatureName, &verifier, res.Header, res.Trailer, &res.Body) - if err != nil { - return "", err + if nonce, ok := signature.params["nonce"].(string); ok { + details.Nonce = &nonce } - parsedMessage, err := parseResponse(res, resWithTrailers) - if err != nil { - return "", err + if tag, ok := signature.params["tag"].(string); ok { + details.Tag = &tag } - // Read the associated request with trailers if the verifier requests its trailers, or there are signed trailer - // covered in the signature - reqWithTrailers := verifier.fields.hasTrailerFields(true) || psiSig.fields.hasTrailerFields(true) - parsedAssocMessage, err := parseRequest(req, reqWithTrailers) + + return details, nil +} + +// VerifyResponse verifies a signed HTTP response. Returns an error if verification failed for any reason, otherwise nil. +func VerifyResponse(signatureName string, verifier Verifier, res *http.Response, req *http.Request) error { + msg, err := NewMessage(NewMessageConfig().WithResponse(res, req)) if err != nil { - return "", err + return err } - signatureBase, err = verifyMessage(*verifier.config, verifier, parsedMessage, parsedAssocMessage, - verifier.fields, wantSigRaw, psiSig) - return signatureBase, err + _, err = msg.Verify(signatureName, verifier) + return err } func extractSignatureFields(signatureName string, verifier *Verifier, diff --git a/signatures_test.go b/signatures_test.go index 663476a..b55b449 100644 --- a/signatures_test.go +++ b/signatures_test.go @@ -14,11 +14,12 @@ import ( "encoding/base64" "encoding/pem" "fmt" - "github.com/stretchr/testify/assert" "net/http" "strings" "testing" "time" + + "github.com/stretchr/testify/assert" ) var httpreq1 = `POST /foo?param=value&pet=dog HTTP/1.1 @@ -1406,7 +1407,9 @@ func TestMultipleSignatures(t *testing.T) { SetVerifyCreated(false).SetKeyID("test-key-ecc-p256"), Headers("@method", "@authority", "@path", "content-digest", "content-type", "content-length")) assert.NoError(t, err, "cannot create verifier1") - _, err = verifyRequestDebug("sig1", *verifier1, req) + message, err := NewMessage(NewMessageConfig().WithRequest(req)) + assert.NoError(t, err, "failed to create message") + _, _, err = verifyDebug("sig1", *verifier1, message) assert.Error(t, err, "sig1 cannot be verified, because the proxy modified the authority field") pubKey2, err := parseRsaPublicKey(rsaPubKey) @@ -1414,7 +1417,9 @@ func TestMultipleSignatures(t *testing.T) { verifier2, err := NewRSAVerifier(*pubKey2, NewVerifyConfig(). SetVerifyCreated(false).SetRejectExpired(false).SetKeyID("test-key-rsa"), *NewFields().AddDictHeader("Signature", "sig1").AddHeaders("@authority", "forwarded")) assert.NoError(t, err, "cannot create verifier2") - _, err = verifyRequestDebug("proxy_sig", *verifier2, req) + message, err = NewMessage(NewMessageConfig().WithRequest(req)) + assert.NoError(t, err, "failed to create message") + _, _, err = verifyDebug("proxy_sig", *verifier2, message) assert.NoError(t, err, "proxy signature not verified") } @@ -2047,7 +2052,9 @@ func TestRequestBinding17(t *testing.T) { AddHeaderExt("content-digest", false, false, true, false) verifier2, err := NewP256Verifier(*pubKey2, NewVerifyConfig().SetVerifyCreated(false).SetKeyID("test-key-ecc-p256"), fields2) assert.NoError(t, err, "create verifier") - sigBase, err := verifyResponseDebug("reqres", *verifier2, res, req) + message, err := NewMessage(NewMessageConfig().WithResponse(res, req)) + assert.NoError(t, err, "failed to create message") + sigBase, _, err := verifyDebug("reqres", *verifier2, message) expected := `"@status": 503 "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: "content-type": application/json @@ -2098,7 +2105,9 @@ func TestMultipleSignatures17(t *testing.T) { SetVerifyCreated(false).SetKeyID("test-key-ecc-p256"), Headers("@method", "@authority", "@path", "content-digest", "content-type", "content-length")) assert.NoError(t, err, "cannot create verifier1") - _, err = verifyRequestDebug("sig1", *verifier1, req) + message, err := NewMessage(NewMessageConfig().WithRequest(req)) + assert.NoError(t, err, "failed to create message") + _, _, err = verifyDebug("sig1", *verifier1, message) assert.Error(t, err, "sig1 cannot be verified, because the proxy modified the authority field") pubKey2, err := parseRsaPublicKey(rsaPubKey) @@ -2106,11 +2115,15 @@ func TestMultipleSignatures17(t *testing.T) { verifier2, err := NewRSAVerifier(*pubKey2, NewVerifyConfig(). SetVerifyCreated(false).SetRejectExpired(false), *NewFields().AddHeaders("@authority", "forwarded")) assert.NoError(t, err, "cannot create verifier2") - _, err = verifyRequestDebug("proxy_sig", *verifier2, req) + message, err = NewMessage(NewMessageConfig().WithRequest(req)) + assert.NoError(t, err, "failed to create message") + _, _, err = verifyDebug("proxy_sig", *verifier2, message) assert.NoError(t, err, "proxy signature not verified") req = readRequest(httpreq13) - sigBase, err := verifyRequestDebug("sig1", *verifier1, req) + message, err = NewMessage(NewMessageConfig().WithRequest(req)) + assert.NoError(t, err, "failed to create message") + sigBase, _, err := verifyDebug("sig1", *verifier1, message) assert.NotEmpty(t, sigBase) assert.NoError(t, err, "sig1 should verify for the original message that the proxy received") } @@ -2156,7 +2169,9 @@ func TestRequestBindingSignedResponse17(t *testing.T) { AddHeaderExt("content-digest", false, false, true, false) verifier2, err := NewP256Verifier(*pubKey2, NewVerifyConfig().SetVerifyCreated(false).SetKeyID("test-key-ecc-p256"), fields2) assert.NoError(t, err, "create verifier") - sigBase, err := verifyResponseDebug("reqres", *verifier2, res, req) + message, err := NewMessage(NewMessageConfig().WithResponse(res, req)) + assert.NoError(t, err, "failed to create message") + sigBase, _, err := verifyDebug("reqres", *verifier2, message) expected := `"@status": 503 "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: "content-type": application/json